@zip.js/zip.js 2.11.2 → 2.11.3

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/BENCHMARKS.md CHANGED
@@ -33,7 +33,7 @@ should not trust anyone's benchmark (including this one) without reproducing it.
33
33
  | jszip | 3.10.1 |
34
34
  | fflate | 0.8.3 |
35
35
  | archiver | 8.0.0 |
36
- | Measured | 2026-09-06, except the 7-Zip section (see its own note) |
36
+ | Measured | 2026-09-06 — the 7-Zip section under Deno 2.9.6, everything else on Node |
37
37
 
38
38
  ## Method
39
39
 
@@ -127,6 +127,11 @@ level (e.g. `{ level: 5 }`) makes zip.js fall back to the WASM zlib codec — wh
127
127
  the table, does not parallelize via concurrent `add()`. Keep the default level to keep
128
128
  the native-backend parallelism, or pair a custom level with Web Workers (below).
129
129
 
130
+ That fallback costs more than a backend switch, and the
131
+ [frontier](#the-frontier--one-axis-both-sides) measures how much: how much speed a lower
132
+ level actually buys depends on how fast the host's own zlib is, and on Deno it buys
133
+ nothing at all — `level: 5` there is slower *and* larger than the default.
134
+
130
135
  ### Parallelism is runtime-dependent
131
136
 
132
137
  The table above is measured on **Node**, and its "no Web Workers needed" result does
@@ -237,50 +242,153 @@ avoid jszip.
237
242
 
238
243
  How far is zip.js from a native archiver? [`benchmarks/bench-7z.js`](benchmarks/bench-7z.js)
239
244
  compares it against the `7zz` CLI (7-Zip 25.01), disk-to-disk on both sides, under Deno
240
- 2.9.3 and Bun 1.3.14 (both runtimes agree within noise; Deno numbers shown). 7-Zip
241
- timings include process spawn (~ms). Unlike the tables above, this section and the next
242
- were measured in July 2026 with zip.js 2.8.29 and have not been re-run; they compare
243
- zip.js against tools that have not changed, so they age slowly, but treat the zip.js
244
- columns as a floor rather than as current.
245
-
246
- One comparison is impossible to make perfectly fair: **no 7-Zip preset runs zlib's
247
- algorithm.** `-mx=5+` is a near-optimal parser (much slower, smaller output) and
248
- `-mx=1..4` a greedy one (faster, larger output) — they bracket zlib level 6. So the
249
- table carries two anchors: `-mx=6` as the *ratio* anchor and `-mx=1` as the *speed*
250
- anchor. On this machine the `-mx` ladder on the 256 MB file reads: `-mx=1/3` → 5.9 s,
251
- `-mx=5/6` → 34 s, `-mx=7` → 85 s — the 6× jump at `-mx=5` is the switch from greedy
252
- matching to optimal parsing, and it cannot be hidden by threads (one file = one deflate
253
- stream = one core).
254
-
255
- | Workload (compress) | zip.js (workers) | 7-Zip `-mx=6` (mt) | 7-Zip `-mx=1` (mt) |
256
- |---|--:|--:|--:|
257
- | Compressible text (20 MB) | **426 ms** / 6.2 MB | 2606 ms / 5.8 MB | 470 ms / 6.8 MB |
258
- | Incompressible data (20 MB) | **342 ms** | 410 ms | 372 ms |
259
- | 5,000 files × ~2 KB | 780 ms / 5.5 MB | 199 ms / 4.9 MB | **168 ms** / 5.0 MB |
260
- | Large file, disk-to-disk (256 MB) | **5.5 s** / 79.5 MB | 33.4 s / 73.6 MB | 5.9 s / 86.7 MB |
245
+ 2.9.6. 7-Zip timings include process spawn (~ms).
261
246
 
262
- | Workload (decompress) | zip.js (workers) | 7-Zip (mt) |
263
- |---|--:|--:|
264
- | Compressible text (20 MB) | **69 ms** | 88 ms |
265
- | 5,000 files × ~2 KB | 976 ms | **471 ms** |
266
-
267
- - **On single-file streaming, zip.js strictly dominates 7-Zip's speed tier**: a bit
268
- faster *and* ~9 % smaller output, because zlib-6's lazy matching is a better
269
- speed/ratio point than 7-Zip's greedy fast mode. What `-mx=6` buys for its 6× time is
270
- ~7 % more compression — a trade zip.js cannot make, but also one most workloads don't
271
- want.
272
- - **7-Zip legitimately dominates many small files** (~4× on compress, ~2× on
273
- decompress): its per-entry cost is near zero, while zip.js pays per-entry
274
- orchestration (worker round trips, header writes, one output stream per extracted
275
- file). Same lesson as the fflate rows above — tiny-entry workloads are zip.js's cost
276
- center, and the codec backend is irrelevant there.
247
+ **No 7-Zip preset runs zlib's algorithm**, so `-mx=6` and level 6 are not the same request
248
+ and a table pairing them by digit is measuring two things at once. This section therefore
249
+ has two parts: the defaults against each other, and then a frontier that varies *one* axis,
250
+ the compression level, on **both** sides at a fixed core budget.
251
+
252
+ ### Defaults against defaults
253
+
254
+ Level 6 and `-mx=6`, i.e. what each tool does when you do not tune it. Every contender the
255
+ harness measures is listed; time / output size.
256
+
257
+ | Workload (compress) | zip.js (1 thread) | zip.js (workers) | 7-Zip (1 thread) | 7-Zip `-mx=6` (mt) | 7-Zip `-mx=1` (mt) |
258
+ |---|--:|--:|--:|--:|--:|
259
+ | Compressible text (20 MB) | **418 ms** / 6.2 MB | 428 ms / 6.2 MB | — | 2540 ms / 5.8 MB | 444 ms / 6.8 MB |
260
+ | Incompressible data (20 MB) | **321 ms** / 21.0 MB | 333 ms / 21.0 MB | — | 418 ms / 21.0 MB | 353 ms / 21.0 MB |
261
+ | 8 files × 8 MB | 1308 ms / 19.9 MB | 308 ms / 19.9 MB | 8102 ms / 18.4 MB | 1510 ms / 18.4 MB | **294 ms** / 21.7 MB |
262
+ | 5,000 files × ~2 KB | 1245 ms / 5.2 MB | 825 ms / 5.2 MB | 580 ms / 4.9 MB | 193 ms / 4.9 MB | **173 ms** / 5.0 MB |
263
+ | Large file, disk-to-disk (256 MB) | **5.4 s** / 79.5 MB | 5.5 s / 79.5 MB | — | 33.4 s / 73.6 MB | 5.8 s / 86.7 MB |
264
+
265
+ | Workload (decompress) | zip.js (1 thread) | zip.js (workers) | 7-Zip (1 thread) | 7-Zip (mt) |
266
+ |---|--:|--:|--:|--:|
267
+ | Compressible text (20 MB) | 83 ms | **61 ms** | — | 81 ms |
268
+ | 8 files × 8 MB | 181 ms | **83 ms** | 241 ms | 247 ms |
269
+ | 5,000 files × ~2 KB | 989 ms | 906 ms | 491 ms | **462 ms** |
270
+
271
+ `7-Zip (1 thread)` is only listed where it means something: a single file is one deflate
272
+ stream, so `-mmt` changes nothing there and the two 7-Zip columns would measure the same
273
+ run. That is also why zip.js's worker pool does nothing on the single-file rows — the
274
+ parallelism both tools have is *between* entries, never inside one.
275
+
276
+ ### The frontier — one axis, both sides
277
+
278
+ Levels 1–9 on the zip.js side against `-mx=1,3,5,6,7,9` on the 7-Zip side, sorted by output
279
+ size. A tool is faster than another only where it is faster **at the same size**, and
280
+ `frontier` marks a row that nothing smaller beats on time.
281
+
282
+ **One thread, one deflate stream on both sides** — 20 MB of text:
283
+
284
+ | Encoder | Output | Ratio | Time | |
285
+ |---|--:|--:|--:|---|
286
+ | 7-Zip `-mx=9` | 5.7 MB | 3.665 | 14889 ms | frontier |
287
+ | 7-Zip `-mx=7` | 5.7 MB | 3.665 | 6643 ms | frontier |
288
+ | 7-Zip `-mx=5` | 5.8 MB | 3.646 | 2597 ms | frontier |
289
+ | 7-Zip `-mx=6` | 5.8 MB | 3.646 | 2605 ms | |
290
+ | zip.js level 8 | 6.1 MB | 3.429 | 1537 ms | frontier |
291
+ | zip.js level 9 | 6.1 MB | 3.429 | 1544 ms | |
292
+ | zip.js level 7 | 6.1 MB | 3.421 | 1305 ms | frontier |
293
+ | **zip.js level 6 (default)** | 6.2 MB | 3.377 | **418 ms** | frontier |
294
+ | zip.js level 5 | 6.6 MB | 3.196 | 590 ms | |
295
+ | 7-Zip `-mx=1` | 6.8 MB | 3.096 | 458 ms | |
296
+ | 7-Zip `-mx=3` | 6.8 MB | 3.096 | 453 ms | |
297
+ | zip.js level 4 | 6.9 MB | 3.036 | 358 ms | frontier |
298
+ | zip.js level 3 | 7.0 MB | 2.975 | 420 ms | |
299
+ | zip.js level 2 | 7.4 MB | 2.850 | 266 ms | frontier |
300
+ | zip.js level 1 | 7.5 MB | 2.785 | 239 ms | frontier |
301
+
302
+ **The same 20 MB, one thread, but on Node** — because the zip.js side of that table is not
303
+ one codec. Level 6 is the host's `CompressionStream`, so it changes with the runtime; every
304
+ other level is the bundled WASM codec, which does not (its output is byte-identical on both
305
+ and its times agree within 5 %):
306
+
307
+ | Encoder | Output | Ratio | Time | |
308
+ |---|--:|--:|--:|---|
309
+ | 7-Zip `-mx=9` | 5.7 MB | 3.665 | 14904 ms | frontier |
310
+ | 7-Zip `-mx=7` | 5.7 MB | 3.665 | 6548 ms | frontier |
311
+ | 7-Zip `-mx=5` | 5.8 MB | 3.646 | 2535 ms | frontier |
312
+ | 7-Zip `-mx=6` | 5.8 MB | 3.646 | 2553 ms | |
313
+ | zip.js level 8 | 6.1 MB | 3.429 | 1474 ms | frontier |
314
+ | zip.js level 9 | 6.1 MB | 3.429 | 1476 ms | |
315
+ | **zip.js level 6 (default)** | 6.1 MB | 3.426 | **696 ms** | frontier |
316
+ | zip.js level 7 | 6.1 MB | 3.421 | 1238 ms | |
317
+ | zip.js level 5 | 6.6 MB | 3.196 | 514 ms | frontier |
318
+ | 7-Zip `-mx=1` | 6.8 MB | 3.096 | 447 ms | frontier |
319
+ | 7-Zip `-mx=3` | 6.8 MB | 3.096 | 445 ms | frontier |
320
+ | zip.js level 4 | 6.9 MB | 3.036 | 304 ms | frontier |
321
+ | zip.js level 3 | 7.0 MB | 2.975 | 375 ms | |
322
+ | zip.js level 2 | 7.4 MB | 2.850 | 221 ms | frontier |
323
+ | zip.js level 1 | 7.5 MB | 2.785 | 200 ms | frontier |
324
+
325
+ **Every core, both sides** — 8 files × 8 MB, on Deno. It has to be Deno: Node exposes no
326
+ global `Worker`, so `useWebWorkers` cannot spawn one there and only the default level —
327
+ which rides the platform threadpool instead — parallelizes at all. Measured on Node the
328
+ same table reads 617 ms at level 6 against 4090 ms at level 7, i.e. the WASM levels run
329
+ serially, which would make it a comparison of 7-Zip on 8 cores against zip.js on 1.
330
+
331
+ | Encoder | Output | Ratio | Time | |
332
+ |---|--:|--:|--:|---|
333
+ | 7-Zip `-mx=9` | 18.3 MB | 3.660 | 8625 ms | frontier |
334
+ | 7-Zip `-mx=7` | 18.3 MB | 3.660 | 3796 ms | frontier |
335
+ | 7-Zip `-mx=5` | 18.4 MB | 3.645 | 1510 ms | frontier |
336
+ | 7-Zip `-mx=6` | 18.4 MB | 3.645 | 1540 ms | |
337
+ | zip.js level 9 | 19.6 MB | 3.428 | 971 ms | frontier |
338
+ | zip.js level 8 | 19.6 MB | 3.428 | 991 ms | |
339
+ | zip.js level 7 | 19.6 MB | 3.420 | 852 ms | frontier |
340
+ | **zip.js level 6 (default)** | 19.9 MB | 3.376 | **317 ms** | frontier |
341
+ | zip.js level 5 | 21.0 MB | 3.196 | 405 ms | |
342
+ | 7-Zip `-mx=3` | 21.7 MB | 3.096 | 309 ms | frontier |
343
+ | 7-Zip `-mx=1` | 21.7 MB | 3.096 | 313 ms | |
344
+ | zip.js level 4 | 22.1 MB | 3.037 | 287 ms | frontier |
345
+ | zip.js level 3 | 22.6 MB | 2.975 | 316 ms | |
346
+ | zip.js level 2 | 23.6 MB | 2.849 | 239 ms | frontier |
347
+ | zip.js level 1 | 24.1 MB | 2.784 | 218 ms | frontier |
348
+
349
+ What the two curves say:
350
+
351
+ - **zip.js's default is a spike on its own curve, because it is a different codec.** The
352
+ platform's `CompressionStream` exposes no level control, so **level 6 runs the host's
353
+ native zlib and every other level drops onto the bundled WASM one**. How big the spike is
354
+ therefore depends on whose zlib the host ships: 418 ms at ratio 3.377 on Deno's zlib-ng,
355
+ 696 ms at 3.426 on Node's Chromium zlib — ~1.7× faster and ~1.4 % looser.
356
+ - **So whether a lower level buys anything is runtime-dependent, and on Deno it does not.**
357
+ On Node, `level: 5` costs 514 ms against the default's 696 ms: the ordinary trade, 26 %
358
+ faster for 7 % more bytes. On Deno the default is already 418 ms, so the same `level: 5`
359
+ takes 590 ms and produces a *larger* archive — **slower and bigger, strictly worse**. If
360
+ you reach for a lower level to go faster, measure it on your runtime first.
361
+ - **One level is a bad deal everywhere: `level: 3` loses to `level: 4`** on both time and
362
+ size (375 ms / 7.0 MB against 304 ms / 6.9 MB on Node; 420 ms against 358 ms on Deno).
363
+ Both are the same WASM codec, so this one is zlib's own curve, not a fallback artifact.
364
+ - **7-Zip's nine presets are three encoders.** `-mx=1` and `-mx=3` are identical, so are
365
+ `-mx=5`/`-mx=6` and `-mx=7`/`-mx=9` — **`-mx=9` costs 2.3× the time of `-mx=7` for
366
+ byte-identical output**. The 5.7× jump from `-mx=3` to `-mx=5` is greedy matching giving
367
+ way to optimal parsing, and threads cannot hide it.
368
+ - **Against the greedy tier zip.js wins outright.** Single-threaded it is 418 ms / 6.2 MB
369
+ against 453–458 ms / 6.8 MB: faster *and* 9 % smaller than both `-mx=1` and `-mx=3`. On
370
+ all cores it ties them on time (317 ms against 309 ms) and is still 8 % smaller.
371
+ - **Against the optimal-parsing tier it has nothing**, at any level. 7-Zip's cheapest route
372
+ to 3.6 costs 1510 ms where zip.js's best is 971 ms at 3.43; buying that last 6 % of ratio
373
+ costs 4.8× the default's time. That is a real limit of DEFLATE-as-zlib-writes-it, not a
374
+ tuning gap.
375
+ - **Threading buys the two sides different amounts**: 4.2× for zip.js on 8 × 8 MB
376
+ (1308 → 308 ms) against 5.4× for 7-Zip (8102 → 1510 ms). zip.js closes most of that gap
377
+ because it starts from a much faster single-threaded number.
378
+ - **7-Zip legitimately dominates many small files** (~4× on compress, ~2× on decompress):
379
+ its per-entry cost is near zero, while zip.js pays per-entry orchestration. Same lesson
380
+ as the fflate rows above — tiny-entry workloads are zip.js's cost center, and the codec
381
+ is irrelevant there. Note the reverse on 8 × 8 MB decompression, where entries are large
382
+ enough for the pool to pay off: 83 ms against 247 ms, zip.js ~3× faster.
277
383
 
278
384
  ## The runtime's zlib decides zip.js throughput
279
385
 
280
386
  On bulk data zip.js is a thin wrapper around the platform's `CompressionStream` — it
281
387
  adds ~7 % over the raw encoder on the 256 MB stream. That means throughput is decided
282
388
  by **which zlib the runtime vendors**, and they differ a lot. Same 256 MB compressible
283
- file, one thread, level-6-class output everywhere:
389
+ file, one thread, level-6-class output everywhere. This is the one table on this page
390
+ still carrying its July 2026 measurement: it compares runtimes to each other rather than
391
+ zip.js to anything, so it ages with their zlib and not with ours.
284
392
 
285
393
  | Encoder | Time | Output |
286
394
  |---|--:|--:|
package/deno.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zip-js/zip-js",
3
- "version": "2.11.2",
3
+ "version": "2.11.3",
4
4
  "exports": {
5
5
  ".": "./index.js",
6
6
  "./external": "./lib/zip-fs-external.js",
@@ -6277,6 +6277,7 @@ const ERR_INVALID_ENTRY_NAME = "File entry name exceeds 64KB";
6277
6277
  const ERR_INVALID_VERSION = "Version exceeds 65535";
6278
6278
  const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
6279
6279
  const ERR_UNSUPPORTED_ENCRYPTION_USDZ = "Encryption is not supported in USDZ files";
6280
+ const ERR_UNSUPPORTED_SPLIT_USDZ = "Split zip files are not supported in USDZ files";
6280
6281
  const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set";
6281
6282
  const ERR_INVALID_EXTRAFIELD = "Invalid extra field (must be a Map)";
6282
6283
  const ERR_INVALID_EXTRAFIELD_TYPE = "Invalid extra field type (must be integer 0..65535)";
@@ -6291,6 +6292,7 @@ const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
6291
6292
  const ERR_UNDEFINED_COMPRESSION_METHOD = "Undefined compression method";
6292
6293
  const ERR_UNDETERMINED_SIZE = "Undetermined size";
6293
6294
  const ERR_UNDEFINED_READER = "Undefined reader";
6295
+ const ERR_INVALID_READER = "Invalid reader (must be a Reader instance, a ReadableStream instance, or an object with a 'readable' property)";
6294
6296
  const ERR_ZIP_NOT_EMPTY = "Zip file not empty";
6295
6297
  const ERR_INVALID_UID = "Invalid uid (must be integer 0..2^32-1)";
6296
6298
  const ERR_INVALID_GID = "Invalid gid (must be integer 0..2^32-1)";
@@ -6322,6 +6324,9 @@ class ZipWriter {
6322
6324
  const addSplitZipSignature =
6323
6325
  availableSize > 0 && availableSize !== INFINITY_VALUE &&
6324
6326
  maxSize > 0 && maxSize !== INFINITY_VALUE;
6327
+ if (addSplitZipSignature && options[OPTION_USDZ]) {
6328
+ throw new Error(ERR_UNSUPPORTED_SPLIT_USDZ);
6329
+ }
6325
6330
  Object.assign(this, {
6326
6331
  writer,
6327
6332
  addSplitZipSignature,
@@ -6437,6 +6442,7 @@ class ZipWriter {
6437
6442
  }
6438
6443
  const { offset, diskNumberStart } = entryPositions.get(entry);
6439
6444
  Object.assign(entry, {
6445
+ zip64Enabled: true,
6440
6446
  zip64UncompressedSize,
6441
6447
  zip64CompressedSize,
6442
6448
  offset,
@@ -7012,6 +7018,9 @@ async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, op
7012
7018
  if (reader) {
7013
7019
  reader = new GenericReader(reader);
7014
7020
  await initStream(reader);
7021
+ if (!reader.readable && !reader.readUint8Array) {
7022
+ throw new Error(ERR_INVALID_READER);
7023
+ }
7015
7024
  ({ size: contentSize } = reader);
7016
7025
  }
7017
7026
  return Object.assign({ reader }, resolveEntrySizes(zipWriter, Boolean(reader), contentSize, metadata, options));
@@ -7022,6 +7031,7 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
7022
7031
  let { dataDescriptor, zip64, level, compressionMethod } = metadata;
7023
7032
  let maximumCompressedSize = 0;
7024
7033
  let uncompressedSize = 0;
7034
+ let unknownSize = false;
7025
7035
  if (passThrough && hasContent) {
7026
7036
  uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
7027
7037
  if (uncompressedSize === UNDEFINED_VALUE) {
@@ -7047,8 +7057,8 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
7047
7057
  if (contentSize === UNDEFINED_VALUE) {
7048
7058
  dataDescriptor = true;
7049
7059
  if (zip64 || zip64 === UNDEFINED_VALUE) {
7050
- zip64 = true;
7051
- uncompressedSize = maximumCompressedSize = MAX_32_BITS + 1;
7060
+ zip64 = unknownSize = true;
7061
+ maximumCompressedSize = MAX_32_BITS + 1;
7052
7062
  }
7053
7063
  } else {
7054
7064
  options.uncompressedSize = uncompressedSize = contentSize;
@@ -7063,7 +7073,7 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
7063
7073
  if (emptyEntry && !zipCrypto && getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR) === UNDEFINED_VALUE) {
7064
7074
  dataDescriptor = false;
7065
7075
  }
7066
- const zip64UncompressedSize = zip64Enabled || uncompressedSize >= MAX_32_BITS;
7076
+ const zip64UncompressedSize = zip64Enabled || unknownSize || uncompressedSize >= MAX_32_BITS;
7067
7077
  const zip64CompressedSize = zip64Enabled || maximumCompressedSize >= MAX_32_BITS;
7068
7078
  if (zip64UncompressedSize || zip64CompressedSize) {
7069
7079
  if (zip64 === false) {
@@ -7079,6 +7089,8 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
7079
7089
  dataDescriptor,
7080
7090
  emptyEntry,
7081
7091
  zip64,
7092
+ zip64Enabled,
7093
+ unknownSize,
7082
7094
  zip64UncompressedSize,
7083
7095
  zip64CompressedSize,
7084
7096
  uncompressedSize,
@@ -7277,6 +7289,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
7277
7289
  rawPassword,
7278
7290
  level,
7279
7291
  zip64,
7292
+ zip64Enabled,
7280
7293
  zip64UncompressedSize,
7281
7294
  zip64CompressedSize,
7282
7295
  zipCrypto,
@@ -7317,6 +7330,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
7317
7330
  lockFileEntry,
7318
7331
  versionMadeBy,
7319
7332
  zip64,
7333
+ zip64Enabled,
7320
7334
  directory: Boolean(directory),
7321
7335
  executable: Boolean(executable),
7322
7336
  filenameUTF8: true,
@@ -7460,13 +7474,14 @@ function getHeaderInfo(options) {
7460
7474
  zip64UncompressedSize,
7461
7475
  zip64CompressedSize,
7462
7476
  uncompressedSize,
7477
+ unknownSize,
7463
7478
  crc32
7464
7479
  } = options;
7465
7480
  let { version, compressionMethod } = options;
7466
7481
  const compressed = !directory && isCompressed(compressionMethod, level);
7467
7482
  let rawLocalExtraFieldZip64;
7468
7483
  const uncompressedFile = passThrough || !compressed;
7469
- const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || uncompressedFile));
7484
+ const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || (uncompressedFile && !unknownSize)));
7470
7485
  const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
7471
7486
  if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
7472
7487
  const length = 4 + 16;
@@ -7600,6 +7615,10 @@ function getHeaderInfo(options) {
7600
7615
  throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
7601
7616
  }
7602
7617
  const dosLastModDate = new Date(Math.ceil(Math.floor(lastModDate.getTime() / 1000) / 2) * 2000);
7618
+ const clampedLastModDate = dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate;
7619
+ const storedLastModDate = getLength(rawExtraFieldExtendedTimestamp) ?
7620
+ new Date(getTimeUnix(lastModDate) * 1000) :
7621
+ getLength(rawExtraFieldNTFS) ? lastModDate : clampedLastModDate;
7603
7622
  const {
7604
7623
  headerArray,
7605
7624
  headerView,
@@ -7609,7 +7628,7 @@ function getHeaderInfo(options) {
7609
7628
  bitFlag: getBitFlag(level, useUnicodeFileNames, dataDescriptor, encrypted, compressionMethod),
7610
7629
  compressionMethod,
7611
7630
  uncompressedSize,
7612
- lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
7631
+ lastModDate: clampedLastModDate,
7613
7632
  rawLastModDate: rawLastModDateOption,
7614
7633
  rawFilename,
7615
7634
  zip64CompressedSize,
@@ -7644,7 +7663,7 @@ function getHeaderInfo(options) {
7644
7663
  localHeaderView,
7645
7664
  headerArray,
7646
7665
  headerView,
7647
- lastModDate,
7666
+ lastModDate: storedLastModDate,
7648
7667
  rawLastModDate,
7649
7668
  encrypted,
7650
7669
  compressed,
@@ -7816,14 +7835,15 @@ function updateLocalHeader({
7816
7835
 
7817
7836
 
7818
7837
  async function closeFile(zipWriter, comment, options) {
7819
- const directoryDataLength = createDirectoryRecords(zipWriter.fileEntries);
7838
+ const { directoryDataLength, zip64Entries } = createDirectoryRecords(zipWriter.fileEntries);
7820
7839
  const { directoryStart, directoryEnd, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
7821
7840
  const signatureLength = await writeDigitalSignatureRecord(zipWriter, directoryArray, options);
7822
- await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength });
7841
+ await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength, zip64Entries });
7823
7842
  }
7824
7843
 
7825
7844
  function createDirectoryRecords(files) {
7826
7845
  let directoryDataLength = 0;
7846
+ let zip64Entries = false;
7827
7847
  for (const [, fileEntry] of files) {
7828
7848
  const {
7829
7849
  rawFilename,
@@ -7836,11 +7856,20 @@ function createDirectoryRecords(files) {
7836
7856
  extendedTimestamp,
7837
7857
  extraFieldExtendedTimestampFlag,
7838
7858
  lastModDate,
7839
- zip64UncompressedSize,
7840
- zip64CompressedSize,
7859
+ zip64Enabled,
7841
7860
  uncompressedSize,
7842
7861
  compressedSize
7843
7862
  } = fileEntry;
7863
+ let { zip64UncompressedSize, zip64CompressedSize } = fileEntry;
7864
+ if (!zip64Enabled) {
7865
+ if (zip64UncompressedSize && uncompressedSize < MAX_32_BITS) {
7866
+ zip64UncompressedSize = fileEntry.zip64UncompressedSize = false;
7867
+ }
7868
+ if (zip64CompressedSize && compressedSize < MAX_32_BITS) {
7869
+ zip64CompressedSize = fileEntry.zip64CompressedSize = false;
7870
+ }
7871
+ }
7872
+ zip64Entries = zip64Entries || zip64UncompressedSize || zip64CompressedSize;
7844
7873
  const zip64Offset = fileEntry.offset >= MAX_32_BITS;
7845
7874
  const zip64DiskNumberStart = fileEntry.diskNumberStart >= MAX_16_BITS;
7846
7875
  let rawExtraFieldZip64;
@@ -7894,7 +7923,7 @@ function createDirectoryRecords(files) {
7894
7923
  }
7895
7924
  directoryDataLength += CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
7896
7925
  }
7897
- return directoryDataLength;
7926
+ return { directoryDataLength, zip64Entries };
7898
7927
  }
7899
7928
 
7900
7929
  async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
@@ -8014,7 +8043,7 @@ async function writeDigitalSignatureRecord(zipWriter, directoryArray, options) {
8014
8043
 
8015
8044
  async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
8016
8045
  const { writer } = zipWriter;
8017
- const { directoryStart, directoryEnd, signatureLength } = cdInfo;
8046
+ const { directoryStart, directoryEnd, signatureLength, zip64Entries } = cdInfo;
8018
8047
  let { directoryDataLength } = cdInfo;
8019
8048
  let fileEntriesLength = zipWriter.fileEntries.size;
8020
8049
  let diskNumber = directoryStart.diskNumber;
@@ -8034,6 +8063,8 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
8034
8063
  } else {
8035
8064
  zip64 = true;
8036
8065
  }
8066
+ } else if (zip64 === UNDEFINED_VALUE && zip64Entries) {
8067
+ zip64 = true;
8037
8068
  }
8038
8069
  const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
8039
8070
  if (exceedsAvailableSize(writer, getLength(endOfdirectoryRecord.array) + commentLength)) {
@@ -8537,7 +8568,7 @@ function formatSupported(StreamClass, format) {
8537
8568
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8538
8569
  */
8539
8570
 
8540
- const VERSION = "2.11.2";
8571
+ const VERSION = "2.11.3";
8541
8572
 
8542
8573
  /*
8543
8574
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -9368,4 +9399,4 @@ function terminateWorkersAndModule() {
9368
9399
 
9369
9400
  configureExternalAssets();
9370
9401
 
9371
- export { BlobReader, BlobWriter, Data64URIReader, Data64URIWriter, ERR_AMBIGUOUS_ARCHIVE, ERR_BAD_FORMAT, ERR_CENTRAL_DIRECTORY_NOT_FOUND, ERR_DUPLICATED_NAME, ERR_ENCRYPTED, ERR_ENCRYPTED_CENTRAL_DIRECTORY, ERR_ENTRY_DATA_OUT_OF_BOUNDS, ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND, ERR_EOCDR_NOT_FOUND, ERR_EXTRAFIELD_ZIP64_NOT_FOUND, ERR_HTTP_RANGE, ERR_HTTP_RESOURCE_CHANGED, ERR_INVALID_AUTHENTICATION_CODE, ERR_INVALID_CODEC_DEFINITION, ERR_INVALID_CODEC_MODULE, ERR_INVALID_COMMENT, ERR_INVALID_COMMENT_TYPE, ERR_INVALID_COMPRESSED_DATA, ERR_INVALID_CRC32, ERR_INVALID_DATE, ERR_INVALID_ENCRYPTION_STRENGTH, ERR_INVALID_ENTRY_COMMENT, ERR_INVALID_ENTRY_COMMENT_TYPE, ERR_INVALID_ENTRY_NAME, ERR_INVALID_EXTRAFIELD, ERR_INVALID_EXTRAFIELD_DATA, ERR_INVALID_EXTRAFIELD_DATA_TYPE, ERR_INVALID_EXTRAFIELD_TYPE, ERR_INVALID_FILENAME_VALIDATION, ERR_INVALID_FUNCTION_OPTION, ERR_INVALID_GID, ERR_INVALID_LEVEL, ERR_INVALID_MAX_APPENDED_DATA_SIZE, ERR_INVALID_MAX_WORKERS, ERR_INVALID_MSDOS_ATTRIBUTES, ERR_INVALID_MSDOS_DATA, ERR_INVALID_PASSWORD, ERR_INVALID_PASSWORD_TYPE, ERR_INVALID_SIGNAL, ERR_INVALID_SIGNATURE_DATA, ERR_INVALID_STRICTNESS, ERR_INVALID_UID, ERR_INVALID_UNCOMPRESSED_SIZE, ERR_INVALID_UNIX_EXTRA_FIELD_TYPE, ERR_INVALID_UNIX_ID_SIZE, ERR_INVALID_UNIX_MODE, ERR_INVALID_VERSION, ERR_ITERATOR_COMPLETED_TOO_SOON, ERR_LOCAL_FILE_HEADER_NOT_FOUND, ERR_OVERLAPPING_ENTRY, ERR_RESERVED_COMPRESSION_METHOD, ERR_SPLIT_ZIP_FILE, ERR_UNDEFINED_COMPRESSION_METHOD, ERR_UNDEFINED_READER, ERR_UNDEFINED_UNCOMPRESSED_SIZE, ERR_UNDETERMINED_SIZE, ERR_UNSAFE_FILENAME, ERR_UNSUPPORTED_COMPRESSION, ERR_UNSUPPORTED_CONTEXT, ERR_UNSUPPORTED_CRYPTO_API, ERR_UNSUPPORTED_ENCRYPTION, ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH, ERR_UNSUPPORTED_ENCRYPTION_USDZ, ERR_UNSUPPORTED_FORMAT, ERR_UNSUPPORTED_UINT64, ERR_WORKER_STARTUP_TIMEOUT, ERR_WRITER_NOT_INITIALIZED, ERR_ZIP_NOT_EMPTY, HttpRangeReader, HttpReader, Reader, SplitDataReader, SplitDataWriter, TextReader, TextWriter, Uint8ArrayReader, Uint8ArrayWriter, VERSION, WARNING_APPENDED_DATA, WARNING_COMPRESSED_PATCHED_DATA, WARNING_DUPLICATE_FILENAME, WARNING_MALFORMED_EXTRA_FIELD, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG, WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY, WARNING_PREPENDED_CENTRAL_DIRECTORY, WARNING_PREPENDED_DATA, WARNING_TRAILING_CENTRAL_DIRECTORY_DATA, WARNING_UNKNOWN_VERSION, WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA, WARNING_UNSORTED_CENTRAL_DIRECTORY, WARNING_WRAPPED_ENTRIES_COUNT, Writer, ZipReader, ZipReaderStream, ZipWriter, ZipWriterStream, configure, createBlobTempStream, createOPFSTempStream, createSyncAccessHandleTempStream, getMimeType, getRegisteredCodecs, getSupportedCompressionMethods, isZipFile, registerCodec, resetConfiguration, terminateWorkersAndModule as terminateWorkers, unregisterCodec };
9402
+ export { BlobReader, BlobWriter, Data64URIReader, Data64URIWriter, ERR_AMBIGUOUS_ARCHIVE, ERR_BAD_FORMAT, ERR_CENTRAL_DIRECTORY_NOT_FOUND, ERR_DUPLICATED_NAME, ERR_ENCRYPTED, ERR_ENCRYPTED_CENTRAL_DIRECTORY, ERR_ENTRY_DATA_OUT_OF_BOUNDS, ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND, ERR_EOCDR_NOT_FOUND, ERR_EXTRAFIELD_ZIP64_NOT_FOUND, ERR_HTTP_RANGE, ERR_HTTP_RESOURCE_CHANGED, ERR_INVALID_AUTHENTICATION_CODE, ERR_INVALID_CODEC_DEFINITION, ERR_INVALID_CODEC_MODULE, ERR_INVALID_COMMENT, ERR_INVALID_COMMENT_TYPE, ERR_INVALID_COMPRESSED_DATA, ERR_INVALID_CRC32, ERR_INVALID_DATE, ERR_INVALID_ENCRYPTION_STRENGTH, ERR_INVALID_ENTRY_COMMENT, ERR_INVALID_ENTRY_COMMENT_TYPE, ERR_INVALID_ENTRY_NAME, ERR_INVALID_EXTRAFIELD, ERR_INVALID_EXTRAFIELD_DATA, ERR_INVALID_EXTRAFIELD_DATA_TYPE, ERR_INVALID_EXTRAFIELD_TYPE, ERR_INVALID_FILENAME_VALIDATION, ERR_INVALID_FUNCTION_OPTION, ERR_INVALID_GID, ERR_INVALID_LEVEL, ERR_INVALID_MAX_APPENDED_DATA_SIZE, ERR_INVALID_MAX_WORKERS, ERR_INVALID_MSDOS_ATTRIBUTES, ERR_INVALID_MSDOS_DATA, ERR_INVALID_PASSWORD, ERR_INVALID_PASSWORD_TYPE, ERR_INVALID_READER, ERR_INVALID_SIGNAL, ERR_INVALID_SIGNATURE_DATA, ERR_INVALID_STRICTNESS, ERR_INVALID_UID, ERR_INVALID_UNCOMPRESSED_SIZE, ERR_INVALID_UNIX_EXTRA_FIELD_TYPE, ERR_INVALID_UNIX_ID_SIZE, ERR_INVALID_UNIX_MODE, ERR_INVALID_VERSION, ERR_ITERATOR_COMPLETED_TOO_SOON, ERR_LOCAL_FILE_HEADER_NOT_FOUND, ERR_OVERLAPPING_ENTRY, ERR_RESERVED_COMPRESSION_METHOD, ERR_SPLIT_ZIP_FILE, ERR_UNDEFINED_COMPRESSION_METHOD, ERR_UNDEFINED_READER, ERR_UNDEFINED_UNCOMPRESSED_SIZE, ERR_UNDETERMINED_SIZE, ERR_UNSAFE_FILENAME, ERR_UNSUPPORTED_COMPRESSION, ERR_UNSUPPORTED_CONTEXT, ERR_UNSUPPORTED_CRYPTO_API, ERR_UNSUPPORTED_ENCRYPTION, ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH, ERR_UNSUPPORTED_ENCRYPTION_USDZ, ERR_UNSUPPORTED_FORMAT, ERR_UNSUPPORTED_SPLIT_USDZ, ERR_UNSUPPORTED_UINT64, ERR_WORKER_STARTUP_TIMEOUT, ERR_WRITER_NOT_INITIALIZED, ERR_ZIP_NOT_EMPTY, HttpRangeReader, HttpReader, Reader, SplitDataReader, SplitDataWriter, TextReader, TextWriter, Uint8ArrayReader, Uint8ArrayWriter, VERSION, WARNING_APPENDED_DATA, WARNING_COMPRESSED_PATCHED_DATA, WARNING_DUPLICATE_FILENAME, WARNING_MALFORMED_EXTRA_FIELD, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG, WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY, WARNING_PREPENDED_CENTRAL_DIRECTORY, WARNING_PREPENDED_DATA, WARNING_TRAILING_CENTRAL_DIRECTORY_DATA, WARNING_UNKNOWN_VERSION, WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA, WARNING_UNSORTED_CENTRAL_DIRECTORY, WARNING_WRAPPED_ENTRIES_COUNT, Writer, ZipReader, ZipReaderStream, ZipWriter, ZipWriterStream, configure, createBlobTempStream, createOPFSTempStream, createSyncAccessHandleTempStream, getMimeType, getRegisteredCodecs, getSupportedCompressionMethods, isZipFile, registerCodec, resetConfiguration, terminateWorkersAndModule as terminateWorkers, unregisterCodec };