@zip.js/zip.js 2.15.0 → 2.16.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.
Files changed (44) hide show
  1. package/BENCHMARKS.md +4 -2
  2. package/deno.json +1 -1
  3. package/dist/zip-core-external.js +410 -227
  4. package/dist/zip-core-external.min.js +1 -1
  5. package/dist/zip-core.js +234 -109
  6. package/dist/zip-core.min.js +1 -1
  7. package/dist/zip-fs-core-external.js +552 -237
  8. package/dist/zip-fs-core-external.min.js +1 -1
  9. package/dist/zip-fs-core.js +377 -118
  10. package/dist/zip-fs-core.min.js +1 -1
  11. package/dist/zip-fs-external.js +552 -237
  12. package/dist/zip-fs-external.min.js +1 -1
  13. package/dist/zip-fs-native.js +379 -120
  14. package/dist/zip-fs-native.min.js +1 -1
  15. package/dist/zip-fs.js +554 -237
  16. package/dist/zip-fs.min.js +1 -1
  17. package/dist/zip-legacy.js +236 -111
  18. package/dist/zip-legacy.min.js +1 -1
  19. package/dist/zip-native.js +236 -111
  20. package/dist/zip-native.min.js +1 -1
  21. package/dist/zip-web-worker-native.js +1 -1
  22. package/dist/zip-web-worker.js +1 -1
  23. package/dist/zip.js +411 -228
  24. package/dist/zip.min.js +1 -1
  25. package/eslint.config.mjs +1 -1
  26. package/index-native.cjs +379 -120
  27. package/index-native.min.js +1 -1
  28. package/index.cjs +554 -237
  29. package/index.d.cts +143 -16
  30. package/index.d.ts +143 -16
  31. package/index.min.js +1 -1
  32. package/lib/core/io.js +3 -2
  33. package/lib/core/streams/aes-crypto-stream.js +60 -17
  34. package/lib/core/streams/zip-crypto-stream.js +7 -7
  35. package/lib/core/streams/zip-entry-stream.js +146 -73
  36. package/lib/core/streams/zlib-wasm/zlib-streams.js +176 -118
  37. package/lib/core/util/compatible-streams.js +10 -5
  38. package/lib/core/version.js +1 -1
  39. package/lib/core/web-worker-inline-native.js +1 -1
  40. package/lib/core/web-worker-inline-wasm.js +1 -1
  41. package/lib/core/zip-fs.js +144 -10
  42. package/lib/core/zip-reader.js +5 -5
  43. package/lib/core/zip-writer.js +4 -2
  44. package/package.json +3 -2
@@ -214,7 +214,7 @@ const ERR_INVALID_SIGNAL = "Invalid signal (must be an AbortSignal instance)";
214
214
  const ERR_INVALID_PASSWORD_TYPE = "Invalid password (password must be a string, rawPassword must be a Uint8Array)";
215
215
  const ERR_INVALID_PASS_THROUGH_VALUE = "Invalid passThrough option (must be a boolean or 'compressed')";
216
216
  const ERR_ABORTED = "The operation was aborted";
217
- const ABORT_ERROR_NAME = "AbortError";
217
+ const ABORT_ERROR_NAME$1 = "AbortError";
218
218
 
219
219
  function checkFunctionOption(value) {
220
220
  if (value && typeof value != FUNCTION_TYPE) {
@@ -232,7 +232,7 @@ function checkSignalOption(signal) {
232
232
 
233
233
  function throwIfAborted(signal) {
234
234
  if (signal && signal.aborted) {
235
- throw signal.reason === UNDEFINED_VALUE ? new DOMException(ERR_ABORTED, ABORT_ERROR_NAME) : signal.reason;
235
+ throw signal.reason === UNDEFINED_VALUE ? new DOMException(ERR_ABORTED, ABORT_ERROR_NAME$1) : signal.reason;
236
236
  }
237
237
  }
238
238
 
@@ -1310,12 +1310,12 @@ let createEngine$1 = createEngine$2;
1310
1310
  class AESDecryptionStream extends TransformStream {
1311
1311
 
1312
1312
  constructor({ password, rawPassword, encryptionStrength, checkPasswordOnly, checkAuthenticationCode = true }) {
1313
+ const aesCrypto = {};
1313
1314
  super({
1314
1315
  start() {
1315
- initAesCrypto(this, password, rawPassword, encryptionStrength);
1316
+ initAesCrypto(aesCrypto, password, rawPassword, encryptionStrength);
1316
1317
  },
1317
1318
  async transform(chunk, controller) {
1318
- const aesCrypto = this;
1319
1319
  const {
1320
1320
  password,
1321
1321
  strength,
@@ -1334,6 +1334,9 @@ class AESDecryptionStream extends TransformStream {
1334
1334
  } else {
1335
1335
  await ready;
1336
1336
  }
1337
+ if (aesCrypto.discarded) {
1338
+ return;
1339
+ }
1337
1340
  const output = new Uint8Array(chunk.length - AUTHENTICATION_CODE_LENGTH - ((chunk.length - AUTHENTICATION_CODE_LENGTH) % BLOCK_LENGTH));
1338
1341
  controller.enqueue(append(aesCrypto, chunk, output, 0, AUTHENTICATION_CODE_LENGTH, true));
1339
1342
  },
@@ -1342,9 +1345,12 @@ class AESDecryptionStream extends TransformStream {
1342
1345
  engine,
1343
1346
  pendingInput,
1344
1347
  ready
1345
- } = this;
1348
+ } = aesCrypto;
1346
1349
  if (engine) {
1347
1350
  await ready;
1351
+ if (aesCrypto.discarded) {
1352
+ return;
1353
+ }
1348
1354
  const originalAuthenticationCode = subarray(pendingInput, pendingInput.length - AUTHENTICATION_CODE_LENGTH);
1349
1355
  const decryptedChunkArray = new Uint8Array(subarray(pendingInput, 0, pendingInput.length - AUTHENTICATION_CODE_LENGTH));
1350
1356
  engine.process(decryptedChunkArray, true);
@@ -1354,27 +1360,26 @@ class AESDecryptionStream extends TransformStream {
1354
1360
  invalidAuthenticationCode |= authenticationCode[indexByte] ^ originalAuthenticationCode[indexByte];
1355
1361
  }
1356
1362
  if (invalidAuthenticationCode && checkAuthenticationCode) {
1357
- throw new Error(ERR_INVALID_AUTHENTICATION_CODE);
1363
+ controller.error(new Error(ERR_INVALID_AUTHENTICATION_CODE));
1364
+ return;
1358
1365
  }
1359
1366
  controller.enqueue(decryptedChunkArray);
1360
1367
  }
1361
- },
1362
- cancel() {
1363
- disposeEngine(this);
1364
1368
  }
1365
1369
  });
1370
+ setDisposingReadable(this, aesCrypto);
1366
1371
  }
1367
1372
  }
1368
1373
 
1369
1374
  class AESEncryptionStream extends TransformStream {
1370
1375
 
1371
1376
  constructor({ password, rawPassword, encryptionStrength }) {
1377
+ const aesCrypto = {};
1372
1378
  super({
1373
1379
  start() {
1374
- initAesCrypto(this, password, rawPassword, encryptionStrength);
1380
+ initAesCrypto(aesCrypto, password, rawPassword, encryptionStrength);
1375
1381
  },
1376
1382
  async transform(chunk, controller) {
1377
- const aesCrypto = this;
1378
1383
  const {
1379
1384
  password,
1380
1385
  strength,
@@ -1388,6 +1393,9 @@ class AESEncryptionStream extends TransformStream {
1388
1393
  } else {
1389
1394
  await ready;
1390
1395
  }
1396
+ if (aesCrypto.discarded) {
1397
+ return;
1398
+ }
1391
1399
  const output = new Uint8Array(preamble.length + chunk.length - (chunk.length % BLOCK_LENGTH));
1392
1400
  output.set(preamble, 0);
1393
1401
  controller.enqueue(append(aesCrypto, chunk, output, preamble.length, 0, false));
@@ -1397,19 +1405,20 @@ class AESEncryptionStream extends TransformStream {
1397
1405
  engine,
1398
1406
  pendingInput,
1399
1407
  ready
1400
- } = this;
1408
+ } = aesCrypto;
1401
1409
  if (engine) {
1402
1410
  await ready;
1411
+ if (aesCrypto.discarded) {
1412
+ return;
1413
+ }
1403
1414
  const encryptedChunkArray = new Uint8Array(pendingInput);
1404
1415
  engine.process(encryptedChunkArray, false);
1405
1416
  const authenticationCode = subarray(engine.digest(), 0, AUTHENTICATION_CODE_LENGTH);
1406
1417
  controller.enqueue(concat(encryptedChunkArray, authenticationCode));
1407
1418
  }
1408
- },
1409
- cancel() {
1410
- disposeEngine(this);
1411
1419
  }
1412
1420
  });
1421
+ setDisposingReadable(this, aesCrypto);
1413
1422
  }
1414
1423
  }
1415
1424
 
@@ -1422,7 +1431,37 @@ function initAesCrypto(aesCrypto, password, rawPassword, encryptionStrength) {
1422
1431
  ready: new Promise(resolve => aesCrypto.resolveReady = resolve),
1423
1432
  password: encodePassword(password, rawPassword),
1424
1433
  strength: encryptionStrength - 1,
1425
- pendingInput: EMPTY_UINT8_ARRAY
1434
+ pendingInput: EMPTY_UINT8_ARRAY,
1435
+ discarded: false
1436
+ });
1437
+ }
1438
+
1439
+ function setDisposingReadable(stream, aesCrypto) {
1440
+ const reader = stream.readable.getReader();
1441
+ const readable = new ReadableStream({
1442
+ async pull(controller) {
1443
+ try {
1444
+ const { value, done } = await reader.read();
1445
+ if (done) {
1446
+ controller.close();
1447
+ } else {
1448
+ controller.enqueue(value);
1449
+ }
1450
+ } catch (error) {
1451
+ disposeEngine(aesCrypto);
1452
+ reader.cancel(error).catch(() => { });
1453
+ throw error;
1454
+ }
1455
+ },
1456
+ cancel(reason) {
1457
+ disposeEngine(aesCrypto);
1458
+ return reader.cancel(reason);
1459
+ }
1460
+ });
1461
+ Object.defineProperty(stream, "readable", {
1462
+ get() {
1463
+ return readable;
1464
+ }
1426
1465
  });
1427
1466
  }
1428
1467
 
@@ -1455,7 +1494,9 @@ async function createDecryptionKeys(decrypt, strength, password, preamble) {
1455
1494
  }
1456
1495
  }
1457
1496
 
1458
- function disposeEngine({ engine }) {
1497
+ function disposeEngine(aesCrypto) {
1498
+ const { engine } = aesCrypto;
1499
+ aesCrypto.discarded = true;
1459
1500
  if (engine && engine.dispose) {
1460
1501
  engine.dispose();
1461
1502
  }
@@ -1472,6 +1513,9 @@ async function createKeys$1(aesCrypto, strength, password, salt) {
1472
1513
  const keyLength = KEY_LENGTH[strength];
1473
1514
  const compositeKey = await deriveKey(password, salt, keyLength * 2 + PASSWORD_VERIFICATION_LENGTH);
1474
1515
  aesCrypto.engine = createEngine$1(subarray(compositeKey, 0, keyLength), subarray(compositeKey, keyLength, keyLength * 2));
1516
+ if (aesCrypto.discarded) {
1517
+ disposeEngine(aesCrypto);
1518
+ }
1475
1519
  return subarray(compositeKey, keyLength * 2);
1476
1520
  }
1477
1521
 
@@ -1623,11 +1667,11 @@ function encrypt(target, input) {
1623
1667
  }
1624
1668
 
1625
1669
  function createKeys(target, password, rawPassword) {
1626
- const keys = [0x12345678, 0x23456789, 0x34567890];
1670
+ const cryptoKeys = [0x12345678, 0x23456789, 0x34567890];
1627
1671
  Object.assign(target, {
1628
- keys,
1629
- crcKey0: new Crc32(keys[0]),
1630
- crcKey2: new Crc32(keys[2])
1672
+ cryptoKeys,
1673
+ crcKey0: new Crc32(cryptoKeys[0]),
1674
+ crcKey2: new Crc32(cryptoKeys[2])
1631
1675
  });
1632
1676
  if (rawPassword) {
1633
1677
  for (let index = 0; index < rawPassword.length; index++) {
@@ -1641,17 +1685,17 @@ function createKeys(target, password, rawPassword) {
1641
1685
  }
1642
1686
 
1643
1687
  function updateKeys(target, byte) {
1644
- let [, key1] = target.keys;
1688
+ let [, key1] = target.cryptoKeys;
1645
1689
  target.crcKey0.append([byte]);
1646
1690
  const key0 = ~target.crcKey0.get();
1647
1691
  key1 = getInt32(Math.imul(getInt32(key1 + getInt8(key0)), 134775813) + 1);
1648
1692
  target.crcKey2.append([key1 >>> 24]);
1649
1693
  const key2 = ~target.crcKey2.get();
1650
- target.keys = [key0, key1, key2];
1694
+ target.cryptoKeys = [key0, key1, key2];
1651
1695
  }
1652
1696
 
1653
1697
  function getByte(target) {
1654
- const temp = target.keys[2] | 2;
1698
+ const temp = target.cryptoKeys[2] | 2;
1655
1699
  return getInt8(Math.imul(temp, (temp ^ 1)) >>> 8);
1656
1700
  }
1657
1701
 
@@ -1699,11 +1743,16 @@ function toCompatibleReadable(readable) {
1699
1743
  const reader = readable.getReader();
1700
1744
  return new ReadableStream({
1701
1745
  async pull(controller) {
1702
- const { value, done } = await reader.read();
1703
- if (done) {
1704
- controller.close();
1705
- } else {
1706
- controller.enqueue(value);
1746
+ try {
1747
+ const { value, done } = await reader.read();
1748
+ if (done) {
1749
+ controller.close();
1750
+ } else {
1751
+ controller.enqueue(value);
1752
+ }
1753
+ } catch (error) {
1754
+ reader.cancel(error).catch(() => { });
1755
+ throw error;
1707
1756
  }
1708
1757
  },
1709
1758
  cancel(reason) {
@@ -1889,7 +1938,6 @@ const FORMAT_GZIP$1 = "gzip";
1889
1938
  const GZIP_HEADER_LENGTH = 10;
1890
1939
  const GZIP_TRAILER_LENGTH = 8;
1891
1940
  const GZIP_HEADER_BYTES = [0x1f, 0x8b, 0x08];
1892
- const GZIP_OUTPUT_STALL_TIMEOUT = 5000;
1893
1941
 
1894
1942
  class DeflateStream extends TransformStream {
1895
1943
 
@@ -1900,7 +1948,7 @@ class DeflateStream extends TransformStream {
1900
1948
  let crc32Stream, encryptionStream, gzipCrc32Stream;
1901
1949
  let readable = super.readable;
1902
1950
  const codecStreams = format && getCodecStreams(format);
1903
- const GzipCompressionStream = getGzipCompressionStream(useCompressionStream, CompressionStream, CompressionStreamFallback);
1951
+ const GzipCompressionStream = getGzipCodecStream(useCompressionStream, CompressionStream, CompressionStreamFallback);
1904
1952
  const useGzipCrc32 = computeCrc32 && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) && Boolean(GzipCompressionStream);
1905
1953
  if ((!encrypted || zipCrypto) && computeCrc32 && !useGzipCrc32) {
1906
1954
  crc32Stream = new Crc32Stream();
@@ -1917,6 +1965,9 @@ class DeflateStream extends TransformStream {
1917
1965
  try {
1918
1966
  readable = pipeThroughCompressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStream, CompressionStreamFallback);
1919
1967
  } catch (error) {
1968
+ if (!useCompressionStream && CompressionStreamFallback) {
1969
+ throw error;
1970
+ }
1920
1971
  let gzipStream;
1921
1972
  try {
1922
1973
  gzipStream = new CompressionStream(FORMAT_GZIP$1);
@@ -1986,74 +2037,134 @@ class GzipToRawDeflateStream extends TransformStream {
1986
2037
  }
1987
2038
 
1988
2039
  function pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, crc32) {
2040
+ const writer = gzipStream.writable.getWriter();
2041
+ const reader = gzipStream.readable.getReader();
2042
+ const outputCrc32 = crc32 === UNDEFINED_VALUE ? new Crc32() : UNDEFINED_VALUE;
1989
2043
  let outputLength = 0;
1990
2044
  let inputDone = false;
1991
- let watchdogTimeout;
2045
+ let trailerWritten = false;
2046
+ let idleCheckArmed = false;
2047
+ let readCount = 0;
2048
+ let readPending = false;
1992
2049
  let resolveTrailerReady, rejectTrailerReady;
1993
2050
  const trailerReady = new Promise((resolve, reject) => {
1994
2051
  resolveTrailerReady = resolve;
1995
2052
  rejectTrailerReady = reject;
1996
2053
  });
1997
2054
  trailerReady.catch(() => { });
1998
- if (!outputSize) {
1999
- resolveTrailerReady();
2000
- }
2001
- const gzipWrapStream = new TransformStream({
2002
- start(controller) {
2055
+ pump();
2056
+ return new ReadableStream({
2057
+ async pull(controller) {
2058
+ try {
2059
+ const { value, done } = await read();
2060
+ if (done) {
2061
+ controller.close();
2062
+ } else {
2063
+ outputLength += value.length;
2064
+ if (outputLength > outputSize) {
2065
+ throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
2066
+ }
2067
+ if (outputCrc32) {
2068
+ outputCrc32.append(value);
2069
+ }
2070
+ controller.enqueue(value);
2071
+ }
2072
+ } catch (error) {
2073
+ rejectTrailerReady(error);
2074
+ await cancel(reader, error);
2075
+ throw trailerWritten ? getTrailerError(error) : error;
2076
+ }
2077
+ },
2078
+ cancel(reason) {
2079
+ rejectTrailerReady(reason);
2080
+ return reader.cancel(reason);
2081
+ }
2082
+ });
2083
+
2084
+ async function pump() {
2085
+ const inputReader = readable.getReader();
2086
+ try {
2003
2087
  const header = new Uint8Array(GZIP_HEADER_LENGTH);
2004
2088
  header.set(GZIP_HEADER_BYTES);
2005
- controller.enqueue(header);
2006
- },
2007
- transform(chunk, controller) {
2008
- controller.enqueue(chunk);
2009
- },
2010
- async flush(controller) {
2089
+ await writer.write(header);
2090
+ for (; ;) {
2091
+ await writer.ready;
2092
+ const { value, done } = await inputReader.read();
2093
+ if (done) {
2094
+ break;
2095
+ }
2096
+ await writer.write(value);
2097
+ }
2011
2098
  inputDone = true;
2012
- startWatchdog();
2013
- try {
2099
+ if (outputCrc32) {
2100
+ if (readPending) {
2101
+ armIdleCheck();
2102
+ }
2014
2103
  await trailerReady;
2015
- } finally {
2016
- stopWatchdog();
2017
2104
  }
2018
2105
  const trailer = new Uint8Array(GZIP_TRAILER_LENGTH);
2019
2106
  const dataView = getDataView(trailer);
2020
- dataView.setUint32(0, crc32.get(), true);
2107
+ dataView.setUint32(0, outputCrc32 ? outputCrc32.get() : crc32, true);
2021
2108
  dataView.setUint32(4, outputSize, true);
2022
- controller.enqueue(trailer);
2023
- },
2024
- cancel(reason) {
2025
- rejectTrailerReady(reason);
2109
+ trailerWritten = true;
2110
+ await writer.write(trailer);
2111
+ await writer.close();
2112
+ } catch (error) {
2113
+ await abort(writer, error);
2114
+ await cancel(inputReader, error);
2026
2115
  }
2027
- });
2028
- const outputStream = new TransformStream({
2029
- transform(chunk, controller) {
2030
- crc32.append(chunk);
2031
- outputLength += chunk.length;
2032
- if (outputLength >= outputSize) {
2033
- resolveTrailerReady();
2034
- } else if (inputDone) {
2035
- startWatchdog();
2036
- }
2037
- controller.enqueue(chunk);
2038
- },
2039
- cancel(reason) {
2040
- rejectTrailerReady(reason);
2116
+ }
2117
+
2118
+ function read() {
2119
+ readCount++;
2120
+ readPending = true;
2121
+ const result = reader.read();
2122
+ result.then(onReadSettled, onReadSettled);
2123
+ if (inputDone && outputCrc32) {
2124
+ armIdleCheck();
2041
2125
  }
2042
- });
2043
- readable = pipeThrough(readable, gzipWrapStream);
2044
- readable = pipeThroughBackpressured(readable, gzipStream);
2045
- return pipeThrough(readable, outputStream);
2126
+ return result;
2127
+ }
2128
+
2129
+ function onReadSettled() {
2130
+ readPending = false;
2131
+ }
2046
2132
 
2047
- function startWatchdog() {
2048
- stopWatchdog();
2049
- watchdogTimeout = setTimeout(() => rejectTrailerReady(new Error(ERR_INVALID_UNCOMPRESSED_SIZE)), GZIP_OUTPUT_STALL_TIMEOUT);
2133
+ async function armIdleCheck() {
2134
+ if (!idleCheckArmed) {
2135
+ idleCheckArmed = true;
2136
+ const count = readCount;
2137
+ await nextTask();
2138
+ idleCheckArmed = false;
2139
+ if (readPending) {
2140
+ if (readCount == count) {
2141
+ resolveTrailerReady();
2142
+ } else {
2143
+ armIdleCheck();
2144
+ }
2145
+ }
2146
+ }
2050
2147
  }
2051
2148
 
2052
- function stopWatchdog() {
2053
- clearTimeout(watchdogTimeout);
2149
+ function getTrailerError(error) {
2150
+ const trailerError = new Error(outputLength == outputSize ? ERR_INVALID_CRC32 : ERR_INVALID_UNCOMPRESSED_SIZE);
2151
+ trailerError.cause = error;
2152
+ return trailerError;
2054
2153
  }
2055
2154
  }
2056
2155
 
2156
+ function nextTask() {
2157
+ return new Promise(resolve => {
2158
+ const { port1, port2 } = new MessageChannel();
2159
+ port2.onmessage = () => {
2160
+ port1.close();
2161
+ port2.close();
2162
+ resolve();
2163
+ };
2164
+ port1.postMessage(UNDEFINED_VALUE);
2165
+ });
2166
+ }
2167
+
2057
2168
  class InflateStream extends TransformStream {
2058
2169
 
2059
2170
  constructor(options, { chunkSize, DecompressionStreamFallback, DecompressionStream }) {
@@ -2074,20 +2185,32 @@ class InflateStream extends TransformStream {
2074
2185
  if (codecStreams) {
2075
2186
  readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.DecompressionStream, format, { chunkSize, compressionMethod, rawBitFlag, uncompressedSize: outputSize }));
2076
2187
  } else {
2077
- try {
2078
- readable = pipeThroughCompressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamFallback);
2079
- } catch (error) {
2080
- if (deflate64 || outputSize === UNDEFINED_VALUE) {
2081
- throw error;
2082
- }
2083
- let gzipStream;
2188
+ let gzipStream;
2189
+ const GzipDecompressionStream = getGzipCodecStream(useCompressionStream, DecompressionStream, DecompressionStreamFallback);
2190
+ if (checkCrc32 && !deflate64 && crc32 !== UNDEFINED_VALUE && outputSize !== UNDEFINED_VALUE && GzipDecompressionStream) {
2084
2191
  try {
2085
- gzipStream = new DecompressionStream(FORMAT_GZIP$1);
2192
+ gzipStream = new GzipDecompressionStream(FORMAT_GZIP$1, { chunkSize });
2086
2193
  } catch {
2087
- throw error;
2194
+ gzipStream = UNDEFINED_VALUE;
2195
+ }
2196
+ }
2197
+ if (!gzipStream) {
2198
+ try {
2199
+ readable = pipeThroughCompressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamFallback);
2200
+ } catch (error) {
2201
+ if (deflate64 || outputSize === UNDEFINED_VALUE || (!useCompressionStream && DecompressionStreamFallback)) {
2202
+ throw error;
2203
+ }
2204
+ try {
2205
+ gzipStream = new DecompressionStream(FORMAT_GZIP$1);
2206
+ } catch {
2207
+ throw error;
2208
+ }
2088
2209
  }
2089
- gzipCrc32 = new Crc32();
2090
- readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, gzipCrc32);
2210
+ }
2211
+ if (gzipStream) {
2212
+ gzipCrc32 = true;
2213
+ readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, crc32);
2091
2214
  }
2092
2215
  }
2093
2216
  readable = mapInflateStreamError(readable);
@@ -2097,8 +2220,8 @@ class InflateStream extends TransformStream {
2097
2220
  readable = pipeThrough(readable, crc32Stream);
2098
2221
  }
2099
2222
  setReadable(this, readable, () => {
2100
- if (checkCrc32) {
2101
- const computedCrc32 = gzipCrc32 ? gzipCrc32.get() >>> 0 : new DataView(crc32Stream.value.buffer).getUint32(0, false);
2223
+ if (crc32Stream) {
2224
+ const computedCrc32 = new DataView(crc32Stream.value.buffer).getUint32(0, false);
2102
2225
  if (crc32 != computedCrc32) {
2103
2226
  throw new Error(ERR_INVALID_CRC32);
2104
2227
  }
@@ -2155,11 +2278,11 @@ function createCodecStream(CodecStreamClass, format, options) {
2155
2278
  return new CodecStreamClass(format, options);
2156
2279
  }
2157
2280
 
2158
- function getGzipCompressionStream(useCompressionStream, CompressionStreamNative, CompressionStreamFallback) {
2159
- if (useCompressionStream && CompressionStreamNative) {
2160
- return CompressionStreamNative;
2161
- } else if (CompressionStreamFallback && CompressionStreamFallback.requiresModule) {
2162
- return CompressionStreamFallback;
2281
+ function getGzipCodecStream(useCompressionStream, CodecStreamNative, CodecStreamFallback) {
2282
+ if (useCompressionStream && CodecStreamNative) {
2283
+ return CodecStreamNative;
2284
+ } else if (CodecStreamFallback && CodecStreamFallback.requiresModule) {
2285
+ return CodecStreamFallback;
2163
2286
  }
2164
2287
  }
2165
2288
 
@@ -2229,10 +2352,15 @@ function mapInflateStreamError(readable) {
2229
2352
  const reader = readable.getReader();
2230
2353
  return new ReadableStream({
2231
2354
  async pull(controller) {
2232
- let result;
2233
2355
  try {
2234
- result = await reader.read();
2356
+ const { value, done } = await reader.read();
2357
+ if (done) {
2358
+ controller.close();
2359
+ } else {
2360
+ controller.enqueue(value);
2361
+ }
2235
2362
  } catch (error) {
2363
+ await cancel(reader, error);
2236
2364
  if (error && error.message) {
2237
2365
  throw error;
2238
2366
  }
@@ -2240,12 +2368,6 @@ function mapInflateStreamError(readable) {
2240
2368
  mappedError.cause = error;
2241
2369
  throw mappedError;
2242
2370
  }
2243
- const { value, done } = result;
2244
- if (done) {
2245
- controller.close();
2246
- } else {
2247
- controller.enqueue(value);
2248
- }
2249
2371
  },
2250
2372
  cancel(reason) {
2251
2373
  return reader.cancel(reason);
@@ -3761,10 +3883,11 @@ class FetchReader extends Reader {
3761
3883
 
3762
3884
  createReadable(options) {
3763
3885
  const reader = this;
3764
- const { useRangeHeader, forceRangeRequests, size } = reader;
3886
+ const { useRangeHeader, forceRangeRequests, size, eocdCache } = reader;
3765
3887
  if ((useRangeHeader || forceRangeRequests) && size !== UNDEFINED_VALUE) {
3766
3888
  const { offset = 0, size: readSize = size - offset } = options || {};
3767
- if (readSize > 0 && offset < size) {
3889
+ const cached = eocdCache && offset >= size - eocdCache.length;
3890
+ if (readSize > 0 && offset < size && !cached) {
3768
3891
  return createRangeReadable(reader, offset, Math.min(readSize, size - offset));
3769
3892
  }
3770
3893
  }
@@ -5323,8 +5446,8 @@ let ZipEntry$1 = class ZipEntry {
5323
5446
  const passThroughCompression = Boolean(passThrough);
5324
5447
  const passThroughEncryption = passThrough === true;
5325
5448
  checkPasswordOption(password, rawPassword);
5326
- password = password && password.length && password;
5327
- rawPassword = rawPassword && rawPassword.length && rawPassword;
5449
+ password = password && password.length ? password : UNDEFINED_VALUE;
5450
+ rawPassword = rawPassword && rawPassword.length ? rawPassword : UNDEFINED_VALUE;
5328
5451
  if (extraFieldAES) {
5329
5452
  if (extraFieldAES.originalCompressionMethod != COMPRESSION_METHOD_AES) {
5330
5453
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
@@ -6085,7 +6208,7 @@ function getMaxAppendedDataSize(maxAppendedDataSize, strictness) {
6085
6208
  async function findEndOfCentralDirectory(reader, rejectAmbiguous, maxAppendedDataSize) {
6086
6209
  const { size } = reader;
6087
6210
  const anchoredLength = Math.min(size, END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS);
6088
- const remoteProbeBudget = { count: MAX_END_OF_CENTRAL_DIR_PROBES };
6211
+ const remoteProbeBudget = { remaining: MAX_END_OF_CENTRAL_DIR_PROBES };
6089
6212
  let endOfDirectoryInfo;
6090
6213
  let plausibleEndOfDirectoryInfo;
6091
6214
  let endOfDirectoryReachingEndCount = 0;
@@ -6178,8 +6301,8 @@ async function readSignature(reader, view, anchoredOffset, signatureOffset, size
6178
6301
  if (signatureOffset >= anchoredOffset) {
6179
6302
  return getUint32$1(view, signatureOffset - anchoredOffset);
6180
6303
  }
6181
- if (remoteProbeBudget.count > 0) {
6182
- remoteProbeBudget.count--;
6304
+ if (remoteProbeBudget.remaining > 0) {
6305
+ remoteProbeBudget.remaining--;
6183
6306
  const signatureArray = await readUint8Array(reader, signatureOffset, 4);
6184
6307
  return getUint32$1(getDataView(signatureArray), 0);
6185
6308
  }
@@ -7095,9 +7218,11 @@ function resolveMetadata(zipWriter, name, options) {
7095
7218
  const passThrough = checkPassThroughOption(getOptionValue(zipWriter, options, OPTION_PASS_THROUGH));
7096
7219
  const passThroughCompression = Boolean(passThrough);
7097
7220
  const passThroughEncryption = passThrough === true;
7098
- const password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
7099
- const rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
7221
+ let password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
7222
+ let rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
7100
7223
  checkPasswordOption(password, rawPassword);
7224
+ password = password && password.length ? password : UNDEFINED_VALUE;
7225
+ rawPassword = rawPassword && rawPassword.length ? rawPassword : UNDEFINED_VALUE;
7101
7226
  const encryptionStrength = getNumberOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
7102
7227
  const zipCrypto = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIPCRYPTO);
7103
7228
  const extendedTimestamp = getOptionValue(zipWriter, options, OPTION_EXTENDED_TIMESTAMP, true);
@@ -8893,7 +9018,7 @@ function formatSupported(StreamClass, format) {
8893
9018
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8894
9019
  */
8895
9020
 
8896
- const VERSION = "2.15.0";
9021
+ const VERSION = "2.16.1";
8897
9022
 
8898
9023
  /*
8899
9024
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -9370,7 +9495,7 @@ try {
9370
9495
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9371
9496
  */
9372
9497
 
9373
- /* global TransformStream */
9498
+ /* global ReadableStream, WritableStream */
9374
9499
 
9375
9500
  const FORMAT_DEFLATE = "deflate";
9376
9501
  const FORMAT_DEFLATE_RAW = "deflate-raw";
@@ -9405,137 +9530,195 @@ function _make(isCompress, type, options = {}) {
9405
9530
  const level = (typeof options.level === "number") ? options.level : -1;
9406
9531
  const outBufferSize = (typeof options.outBuffer === "number") ? options.outBuffer : 64 * 1024;
9407
9532
  const inBufferSize = (typeof options.inBufferSize === "number") ? options.inBufferSize : 64 * 1024;
9408
-
9409
- return new TransformStream({
9410
- start() {
9533
+ const state = { out: 0, in: 0, inBufferSize: 0, streamHandle: 0 };
9534
+ let readableController, writableController, resolveWrite, cancelReason;
9535
+ let backpressure = true;
9536
+ let cancelled = false;
9537
+ initStream();
9538
+ const readable = new ReadableStream({
9539
+ start(controller) {
9540
+ readableController = controller;
9541
+ },
9542
+ pull() {
9543
+ backpressure = false;
9544
+ resumeWrite();
9545
+ },
9546
+ cancel(reason) {
9547
+ cancelled = true;
9548
+ cancelReason = reason;
9549
+ disposeStream(state);
9550
+ writableController.error(reason);
9551
+ resumeWrite();
9552
+ }
9553
+ }, { highWaterMark: 0 });
9554
+ const writable = new WritableStream({
9555
+ start(controller) {
9556
+ writableController = controller;
9557
+ },
9558
+ async write(chunk) {
9559
+ if (backpressure) {
9560
+ await new Promise(resolve => resolveWrite = resolve);
9561
+ }
9562
+ if (cancelled) {
9563
+ throw cancelReason;
9564
+ }
9411
9565
  try {
9412
- let result;
9413
- this.out = malloc(outBufferSize);
9414
- this.in = malloc(inBufferSize);
9415
- this.inBufferSize = inBufferSize;
9416
- if (!this.out || !this.in) {
9417
- throw new Error("allocation failed");
9418
- }
9419
- if (isCompress) {
9420
- this._process = wasm$1.deflate_process;
9421
- this._last_consumed = wasm$1.deflate_last_consumed;
9422
- this._end = wasm$1.deflate_end;
9423
- this.streamHandle = wasm$1.deflate_new();
9424
- if (type === FORMAT_GZIP) {
9425
- result = wasm$1.deflate_init_gzip(this.streamHandle, level);
9426
- } else if (type === FORMAT_DEFLATE_RAW) {
9427
- result = wasm$1.deflate_init_raw(this.streamHandle, level);
9428
- } else {
9429
- result = wasm$1.deflate_init(this.streamHandle, level);
9430
- }
9431
- } else {
9432
- if (type === FORMAT_DEFLATE64_RAW) {
9433
- this._process = wasm$1.inflate9_process;
9434
- this._last_consumed = wasm$1.inflate9_last_consumed;
9435
- this._end = wasm$1.inflate9_end;
9436
- this.streamHandle = wasm$1.inflate9_new();
9437
- result = wasm$1.inflate9_init_raw(this.streamHandle);
9438
- } else {
9439
- this._process = wasm$1.inflate_process;
9440
- this._last_consumed = wasm$1.inflate_last_consumed;
9441
- this._end = wasm$1.inflate_end;
9442
- this.streamHandle = wasm$1.inflate_new();
9443
- if (type === FORMAT_DEFLATE_RAW) {
9444
- result = wasm$1.inflate_init_raw(this.streamHandle);
9445
- } else if (type === FORMAT_GZIP) {
9446
- result = wasm$1.inflate_init_gzip(this.streamHandle);
9447
- } else {
9448
- result = wasm$1.inflate_init(this.streamHandle);
9449
- }
9450
- }
9451
- }
9452
- if (result !== 0) {
9453
- throw new Error("init failed:" + result);
9454
- }
9566
+ processChunk(chunk);
9455
9567
  } catch (error) {
9456
- disposeStream(this);
9568
+ disposeStream(state);
9569
+ readableController.error(error);
9457
9570
  throw error;
9458
9571
  }
9459
9572
  },
9460
- transform(chunk, controller) {
9573
+ close() {
9574
+ if (cancelled) {
9575
+ return;
9576
+ }
9461
9577
  try {
9462
- const buffer = chunk;
9463
- const heap = new Uint8Array(memory.buffer);
9464
- const process = this._process;
9465
- const last_consumed = this._last_consumed;
9466
- const out = this.out;
9467
- let offset = 0;
9468
- while (offset < buffer.length) {
9469
- const toRead = Math.min(buffer.length - offset, 32 * 1024);
9470
- if (!this.in || this.inBufferSize < toRead) {
9471
- if (this.in && free) {
9472
- free(this.in);
9473
- this.in = 0;
9474
- }
9475
- this.in = malloc(toRead);
9476
- this.inBufferSize = toRead;
9477
- if (!this.in) {
9478
- throw new Error("allocation failed");
9479
- }
9480
- }
9481
- heap.set(buffer.subarray(offset, offset + toRead), this.in);
9482
- const result = process(this.streamHandle, this.in, toRead, out, outBufferSize, 0);
9483
- // checked before the byte count is used, so a status code can never be read as one
9484
- const code = (result >> 24) & 0xff;
9485
- const signedCode = (code & 0x80) ? code - 256 : code;
9486
- if (signedCode < 0) {
9487
- throw new Error("process error:" + signedCode);
9488
- }
9489
- const prod = result & 0x00ffffff;
9490
- if (prod) {
9491
- controller.enqueue(heap.slice(out, out + prod));
9492
- }
9493
- const consumed = last_consumed(this.streamHandle);
9494
- if (consumed === 0 && prod === 0) {
9495
- break;
9496
- }
9497
- offset += consumed;
9498
- }
9578
+ flushStream();
9499
9579
  } catch (error) {
9500
- disposeStream(this);
9501
- controller.error(error);
9580
+ disposeStream(state);
9581
+ readableController.error(error);
9582
+ throw error;
9583
+ }
9584
+ const result = disposeStream(state);
9585
+ if (result !== 0) {
9586
+ const error = new Error("end error:" + result);
9587
+ readableController.error(error);
9588
+ throw error;
9502
9589
  }
9590
+ readableController.close();
9503
9591
  },
9504
- flush(controller) {
9505
- try {
9506
- const heap = new Uint8Array(memory.buffer);
9507
- const process = this._process;
9508
- const out = this.out;
9509
- while (true) {
9510
- const result = process(this.streamHandle, 0, 0, out, outBufferSize, 4);
9511
- const code = (result >> 24) & 0xff;
9512
- const signedCode = (code & 0x80) ? code - 256 : code;
9513
- if (signedCode < 0) {
9514
- throw new Error("process error:" + signedCode);
9515
- }
9516
- const produced = result & 0x00ffffff;
9517
- if (produced) {
9518
- controller.enqueue(heap.slice(out, out + produced));
9519
- }
9520
- if (code === 1 || produced === 0) {
9521
- break;
9592
+ abort(reason) {
9593
+ disposeStream(state);
9594
+ readableController.error(reason);
9595
+ }
9596
+ }, { highWaterMark: 1 });
9597
+ return { readable, writable };
9598
+
9599
+ function initStream() {
9600
+ try {
9601
+ let result;
9602
+ state.out = malloc(outBufferSize);
9603
+ state.in = malloc(inBufferSize);
9604
+ state.inBufferSize = inBufferSize;
9605
+ if (!state.out || !state.in) {
9606
+ throw new Error("allocation failed");
9607
+ }
9608
+ if (isCompress) {
9609
+ state._process = wasm$1.deflate_process;
9610
+ state._last_consumed = wasm$1.deflate_last_consumed;
9611
+ state._end = wasm$1.deflate_end;
9612
+ state.streamHandle = wasm$1.deflate_new();
9613
+ if (type === FORMAT_GZIP) {
9614
+ result = wasm$1.deflate_init_gzip(state.streamHandle, level);
9615
+ } else if (type === FORMAT_DEFLATE_RAW) {
9616
+ result = wasm$1.deflate_init_raw(state.streamHandle, level);
9617
+ } else {
9618
+ result = wasm$1.deflate_init(state.streamHandle, level);
9619
+ }
9620
+ } else {
9621
+ if (type === FORMAT_DEFLATE64_RAW) {
9622
+ state._process = wasm$1.inflate9_process;
9623
+ state._last_consumed = wasm$1.inflate9_last_consumed;
9624
+ state._end = wasm$1.inflate9_end;
9625
+ state.streamHandle = wasm$1.inflate9_new();
9626
+ result = wasm$1.inflate9_init_raw(state.streamHandle);
9627
+ } else {
9628
+ state._process = wasm$1.inflate_process;
9629
+ state._last_consumed = wasm$1.inflate_last_consumed;
9630
+ state._end = wasm$1.inflate_end;
9631
+ state.streamHandle = wasm$1.inflate_new();
9632
+ if (type === FORMAT_DEFLATE_RAW) {
9633
+ result = wasm$1.inflate_init_raw(state.streamHandle);
9634
+ } else if (type === FORMAT_GZIP) {
9635
+ result = wasm$1.inflate_init_gzip(state.streamHandle);
9636
+ } else {
9637
+ result = wasm$1.inflate_init(state.streamHandle);
9522
9638
  }
9523
9639
  }
9524
- } catch (error) {
9525
- controller.error(error);
9526
- } finally {
9527
- const result = disposeStream(this);
9528
- if (result !== 0) {
9529
- controller.error(new Error("end error:" + result));
9640
+ }
9641
+ if (result !== 0) {
9642
+ throw new Error("init failed:" + result);
9643
+ }
9644
+ } catch (error) {
9645
+ disposeStream(state);
9646
+ throw error;
9647
+ }
9648
+ }
9649
+
9650
+ function processChunk(chunk) {
9651
+ const heap = new Uint8Array(memory.buffer);
9652
+ const process = state._process;
9653
+ const last_consumed = state._last_consumed;
9654
+ const out = state.out;
9655
+ let offset = 0;
9656
+ while (offset < chunk.length) {
9657
+ const toRead = Math.min(chunk.length - offset, 32 * 1024);
9658
+ if (!state.in || state.inBufferSize < toRead) {
9659
+ if (state.in && free) {
9660
+ free(state.in);
9661
+ state.in = 0;
9662
+ }
9663
+ state.in = malloc(toRead);
9664
+ state.inBufferSize = toRead;
9665
+ if (!state.in) {
9666
+ throw new Error("allocation failed");
9530
9667
  }
9531
9668
  }
9532
- },
9533
- cancel() {
9534
- // release the stream handle and buffers when the pipeline is aborted,
9535
- // they would be leaked in the process-lifetime wasm heap otherwise
9536
- disposeStream(this);
9669
+ heap.set(chunk.subarray(offset, offset + toRead), state.in);
9670
+ const result = process(state.streamHandle, state.in, toRead, out, outBufferSize, 0);
9671
+ // checked before the byte count is used, so a status code can never be read as one
9672
+ const code = (result >> 24) & 0xff;
9673
+ const signedCode = (code & 0x80) ? code - 256 : code;
9674
+ if (signedCode < 0) {
9675
+ throw new Error("process error:" + signedCode);
9676
+ }
9677
+ const prod = result & 0x00ffffff;
9678
+ if (prod) {
9679
+ enqueue(heap.slice(out, out + prod));
9680
+ }
9681
+ const consumed = last_consumed(state.streamHandle);
9682
+ if (consumed === 0 && prod === 0) {
9683
+ break;
9684
+ }
9685
+ offset += consumed;
9537
9686
  }
9538
- });
9687
+ }
9688
+
9689
+ function flushStream() {
9690
+ const heap = new Uint8Array(memory.buffer);
9691
+ const process = state._process;
9692
+ const out = state.out;
9693
+ while (true) {
9694
+ const result = process(state.streamHandle, 0, 0, out, outBufferSize, 4);
9695
+ const code = (result >> 24) & 0xff;
9696
+ const signedCode = (code & 0x80) ? code - 256 : code;
9697
+ if (signedCode < 0) {
9698
+ throw new Error("process error:" + signedCode);
9699
+ }
9700
+ const produced = result & 0x00ffffff;
9701
+ if (produced) {
9702
+ enqueue(heap.slice(out, out + produced));
9703
+ }
9704
+ if (code === 1 || produced === 0) {
9705
+ break;
9706
+ }
9707
+ }
9708
+ }
9709
+
9710
+ function enqueue(chunk) {
9711
+ backpressure = true;
9712
+ readableController.enqueue(chunk);
9713
+ }
9714
+
9715
+ function resumeWrite() {
9716
+ if (resolveWrite) {
9717
+ const resolve = resolveWrite;
9718
+ resolveWrite = null;
9719
+ resolve();
9720
+ }
9721
+ }
9539
9722
 
9540
9723
  function disposeStream(state) {
9541
9724
  let endResult = 0;
@@ -9865,7 +10048,11 @@ const DUPLICATES_VALUES = new Set([DUPLICATES_THROW, DUPLICATES_KEEP_FIRST, DUPL
9865
10048
  const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
9866
10049
  const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
9867
10050
  const ERR_UNSUPPORTED_PASS_THROUGH_VALUE = "The 'compressed' passThrough option is only supported by Entry#getData() and ZipWriter#add()";
10051
+ const ERR_INVALID_PASSWORDS = "Invalid passwords option (must be an array of strings)";
10052
+ const ERR_INVALID_REQUEST_PASSWORD = "Invalid requestPassword option (must be a function returning a string or undefined)";
9868
10053
  const ERR_ABORT_EXPORT = "zipjs-abort-export";
10054
+ const ABORT_ERROR_NAME = "AbortError";
10055
+ const EMPTY_RAW_PASSWORD = new Uint8Array(0);
9869
10056
 
9870
10057
  class ZipEntry {
9871
10058
 
@@ -10238,8 +10425,10 @@ class ZipDirectoryEntry extends ZipEntry {
10238
10425
  zipReader = new ZipReader(reader, options);
10239
10426
  }
10240
10427
  checkPassThroughValue(options.passThrough);
10428
+ checkPasswordCandidatesOptions(options);
10241
10429
  const duplicates = checkDuplicatesOption(options.duplicates);
10242
10430
  const importedEntries = [];
10431
+ const passwordState = { known: [] };
10243
10432
  const entries = await zipReader.getEntries(options);
10244
10433
  for (const entry of entries) {
10245
10434
  let parent = this;
@@ -10284,7 +10473,7 @@ class ZipDirectoryEntry extends ZipEntry {
10284
10473
  }
10285
10474
  importedEntries.push(addChild(parent, name, {
10286
10475
  data: entry,
10287
- Reader: getZipBlobReader(Object.assign({}, options)),
10476
+ Reader: getZipBlobReader(Object.assign({}, options), passwordState),
10288
10477
  uncompressedSize: options.passThrough ? entry.compressedSize : entry.uncompressedSize,
10289
10478
  passThrough: options.passThrough
10290
10479
  }));
@@ -10613,7 +10802,7 @@ function getReadableReader(readable) {
10613
10802
  };
10614
10803
  }
10615
10804
 
10616
- function getZipBlobReader(options) {
10805
+ function getZipBlobReader(options, passwordState) {
10617
10806
  return class extends Reader {
10618
10807
 
10619
10808
  constructor(entry, options = {}) {
@@ -10626,12 +10815,12 @@ function getZipBlobReader(options) {
10626
10815
  const zipBlobReader = this;
10627
10816
  const readerOptions = Object.assign({}, options, zipBlobReader.options);
10628
10817
  const { checkOverlappingEntry, checkOverlappingEntryOnly } = readerOptions;
10629
- const data = await zipBlobReader.entry.getData(new BlobWriter(), Object.assign(readerOptions, {
10818
+ const data = await readEntryData(zipBlobReader.entry, Object.assign(readerOptions, {
10630
10819
  checkPasswordOnly: false,
10631
10820
  checkOverlappingEntry: checkOverlappingEntryOnly || checkOverlappingEntry,
10632
10821
  checkOverlappingEntryOnly: false,
10633
10822
  preventClose: false
10634
- }));
10823
+ }), passwordState);
10635
10824
  zipBlobReader.data = data;
10636
10825
  zipBlobReader.blobReader = new BlobReader(data);
10637
10826
  zipBlobReader.size = data.size;
@@ -10693,10 +10882,9 @@ async function initReaders(entry, options) {
10693
10882
  await initStream(reader);
10694
10883
  } catch (error) {
10695
10884
  try {
10885
+ error.entry = child;
10696
10886
  error.entryId = child.id;
10697
- error.cause = {
10698
- entry: child
10699
- };
10887
+ error.entryName = child.getRelativeName(entry);
10700
10888
  } catch {
10701
10889
  // ignored
10702
10890
  }
@@ -10788,10 +10976,136 @@ function checkReaderOptions(readerOptions) {
10788
10976
  }
10789
10977
  if (readerOptions) {
10790
10978
  checkPassThroughValue(readerOptions.passThrough);
10979
+ checkPasswordCandidatesOptions(readerOptions);
10791
10980
  }
10792
10981
  return readerOptions;
10793
10982
  }
10794
10983
 
10984
+ function checkPasswordCandidatesOptions(options) {
10985
+ const { passwords, requestPassword } = options;
10986
+ if (passwords && (!Array.isArray(passwords) || passwords.some(password => typeof password != STRING_TYPE))) {
10987
+ throw new Error(ERR_INVALID_PASSWORDS);
10988
+ }
10989
+ if (requestPassword && typeof requestPassword != FUNCTION_TYPE) {
10990
+ throw new Error(ERR_INVALID_REQUEST_PASSWORD);
10991
+ }
10992
+ }
10993
+
10994
+ async function readEntryData(entry, options, passwordState) {
10995
+ checkPasswordCandidatesOptions(options);
10996
+ const { passwords, requestPassword, signal } = options;
10997
+ options = Object.assign({}, options);
10998
+ delete options.passwords;
10999
+ delete options.requestPassword;
11000
+ if (!entry.encrypted || options.passThrough || (!passwords && !requestPassword)) {
11001
+ return entry.getData(new BlobWriter(), options);
11002
+ }
11003
+ const tried = new Set();
11004
+ let error;
11005
+ while (true) {
11006
+ for (const candidate of getPasswordCandidates(options, passwordState, passwords)) {
11007
+ if (!tried.has(candidate.key)) {
11008
+ const result = await tryPasswordCandidate(candidate);
11009
+ if (result.done) {
11010
+ return result.data;
11011
+ }
11012
+ error = result.error;
11013
+ }
11014
+ }
11015
+ if (!requestPassword) {
11016
+ break;
11017
+ }
11018
+ if (passwordState.pendingPrompt) {
11019
+ await passwordState.pendingPrompt;
11020
+ continue;
11021
+ }
11022
+ let release;
11023
+ passwordState.pendingPrompt = new Promise(resolve => release = resolve);
11024
+ try {
11025
+ const answer = await requestPassword(entry, error);
11026
+ if (answer === UNDEFINED_VALUE || answer === null) {
11027
+ break;
11028
+ }
11029
+ // deno-lint-ignore valid-typeof
11030
+ if (typeof answer != STRING_TYPE) {
11031
+ throw new Error(ERR_INVALID_REQUEST_PASSWORD);
11032
+ }
11033
+ const result = await tryPasswordCandidate(getPasswordCandidate(answer));
11034
+ if (result.done) {
11035
+ return result.data;
11036
+ }
11037
+ error = result.error;
11038
+ } finally {
11039
+ passwordState.pendingPrompt = UNDEFINED_VALUE;
11040
+ release();
11041
+ }
11042
+ }
11043
+ throw new Error(tried.size ? ERR_INVALID_PASSWORD : ERR_ENCRYPTED);
11044
+
11045
+ async function tryPasswordCandidate(candidate) {
11046
+ tried.add(candidate.key);
11047
+ const candidateOptions = Object.assign({}, options, candidate.options);
11048
+ if (entry.zipCrypto) {
11049
+ candidateOptions.checkCrc32 = true;
11050
+ try {
11051
+ await entry.getData(null, Object.assign({}, candidateOptions, { checkPasswordOnly: true }));
11052
+ } catch (probeError) {
11053
+ if (isInvalidPasswordError(probeError)) {
11054
+ return { done: false, error: probeError };
11055
+ }
11056
+ throw probeError;
11057
+ }
11058
+ }
11059
+ try {
11060
+ const data = await entry.getData(new BlobWriter(), candidateOptions);
11061
+ rememberPassword(passwordState, candidate);
11062
+ return { done: true, data };
11063
+ } catch (readError) {
11064
+ if (isAbortError(readError, signal) || (!entry.zipCrypto && !isInvalidPasswordError(readError))) {
11065
+ throw readError;
11066
+ }
11067
+ return { done: false, error: readError };
11068
+ }
11069
+ }
11070
+ }
11071
+
11072
+ function isInvalidPasswordError(error) {
11073
+ return isErrorObject(error) && error.message == ERR_INVALID_PASSWORD;
11074
+ }
11075
+
11076
+ function isAbortError(error, signal) {
11077
+ return Boolean(signal && signal.aborted) || (isErrorObject(error) && error.name == ABORT_ERROR_NAME);
11078
+ }
11079
+
11080
+ function getPasswordCandidates(options, passwordState, passwords) {
11081
+ const candidates = [];
11082
+ const { password, rawPassword } = options;
11083
+ if (rawPassword && rawPassword.length) {
11084
+ candidates.push({ key: rawPassword, options: { password: "", rawPassword } });
11085
+ }
11086
+ if (password) {
11087
+ candidates.push(getPasswordCandidate(password));
11088
+ }
11089
+ candidates.push(...passwordState.known);
11090
+ if (passwords) {
11091
+ candidates.push(...passwords.filter(password => password).map(getPasswordCandidate));
11092
+ }
11093
+ return candidates;
11094
+ }
11095
+
11096
+ function getPasswordCandidate(password) {
11097
+ return { key: password, options: { password, rawPassword: EMPTY_RAW_PASSWORD } };
11098
+ }
11099
+
11100
+ function rememberPassword(passwordState, candidate) {
11101
+ const { known } = passwordState;
11102
+ const index = known.findIndex(knownCandidate => knownCandidate.key === candidate.key);
11103
+ if (index != -1) {
11104
+ known.splice(index, 1);
11105
+ }
11106
+ known.unshift(candidate);
11107
+ }
11108
+
10795
11109
  function checkPassThroughValue(passThrough) {
10796
11110
  if (checkPassThroughOption(passThrough) === PASS_THROUGH_COMPRESSED) {
10797
11111
  throw new Error(ERR_UNSUPPORTED_PASS_THROUGH_VALUE);
@@ -10929,6 +11243,7 @@ function addFileSystemHandle(zipEntry, handle, options) {
10929
11243
  async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
10930
11244
  const { onstart, onprogress, onend } = options;
10931
11245
  checkPassThroughValue(options.passThrough);
11246
+ checkPasswordCandidatesOptions(options);
10932
11247
  const readerOptions = checkReaderOptions(options.readerOptions);
10933
11248
  const abortController = new AbortController();
10934
11249
  const { signal } = abortController;
@@ -11242,4 +11557,4 @@ function decodeMimeTypes(data) {
11242
11557
  return mimeTypes;
11243
11558
  }
11244
11559
 
11245
- export { BlobReader, BlobWriter, Data64URIReader, Data64URIWriter, ERR_ABORTED, ERR_AMBIGUOUS_ARCHIVE, ERR_ANCESTOR_ENTRY, ERR_BAD_FORMAT, ERR_CENTRAL_DIRECTORY_NOT_FOUND, ERR_DUPLICATED_NAME, ERR_DUPLICATE_IMPORTED_ENTRY, ERR_ENCRYPTED, ERR_ENCRYPTED_CENTRAL_DIRECTORY, ERR_ENTRY_DATA_OUT_OF_BOUNDS, ERR_ENTRY_EXISTS, ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND, ERR_EOCDR_NOT_FOUND, ERR_EXTRAFIELD_ZIP64_NOT_FOUND, ERR_HTTP_RANGE, ERR_HTTP_RESOURCE_CHANGED, ERR_HTTP_STATUS, ERR_INVALID_AUTHENTICATION_CODE, ERR_INVALID_BASE_URI, 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_DUPLICATES, ERR_INVALID_ENCRYPTION_STRENGTH, ERR_INVALID_ENTRY, 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_PASS_THROUGH, ERR_INVALID_PASS_THROUGH_VALUE, ERR_INVALID_READER, ERR_INVALID_READER_OPTIONS, 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_URI, ERR_INVALID_VERSION, ERR_ITERATOR_COMPLETED_TOO_SOON, ERR_LOCAL_FILE_HEADER_NOT_FOUND, ERR_OVERLAPPING_ENTRY, ERR_PARENT_NOT_DIRECTORY, ERR_READABLE_CONSUMED, ERR_RESERVED_COMPRESSION_METHOD, ERR_ROOT_DIRECTORY_NOT_MOVABLE, ERR_SPLIT_ZIP_FILE, ERR_TARGET_NOT_DIRECTORY, ERR_UNDEFINED_COMPRESSION_METHOD, ERR_UNDEFINED_CRC32, 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_PASS_THROUGH_VALUE, ERR_UNSUPPORTED_SPLIT_USDZ, ERR_UNSUPPORTED_UINT64, ERR_WORKER_STARTUP_TIMEOUT, ERR_WRITER_NOT_INITIALIZED, ERR_WRITER_SIZE_NOT_WRITABLE, ERR_ZIP_CRYPTO_LAST_MOD_DATE, ERR_ZIP_NOT_EMPTY, HttpRangeReader, HttpReader, Reader, SplitDataReader, SplitDataWriter, TextReader, TextWriter, Uint8ArrayReader, Uint8ArrayWriter, VERSION, WARNING_APPENDED_DATA, WARNING_CLAMPED_LAST_MODIFICATION_DATE, WARNING_COMPRESSED_PATCHED_DATA, WARNING_COMPRESSION_UNAVAILABLE, 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_LOCAL_FILE_HEADER_FILENAME, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY, WARNING_MULTIPLE_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, ZipDirectoryEntry, ZipEntry, ZipFS, ZipFileEntry, ZipReader, ZipReaderStream, ZipWriter, ZipWriterStream, configure, createBlobTempStream, createOPFSTempStream, createSyncAccessHandleTempStream, fs, getMimeType, getRegisteredCodecs, getSupportedCompressionMethods, isZipFile, registerCodec, resetConfiguration, terminateWorkersAndModule as terminateWorkers, unregisterCodec };
11560
+ export { BlobReader, BlobWriter, Data64URIReader, Data64URIWriter, ERR_ABORTED, ERR_AMBIGUOUS_ARCHIVE, ERR_ANCESTOR_ENTRY, ERR_BAD_FORMAT, ERR_CENTRAL_DIRECTORY_NOT_FOUND, ERR_DUPLICATED_NAME, ERR_DUPLICATE_IMPORTED_ENTRY, ERR_ENCRYPTED, ERR_ENCRYPTED_CENTRAL_DIRECTORY, ERR_ENTRY_DATA_OUT_OF_BOUNDS, ERR_ENTRY_EXISTS, ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND, ERR_EOCDR_NOT_FOUND, ERR_EXTRAFIELD_ZIP64_NOT_FOUND, ERR_HTTP_RANGE, ERR_HTTP_RESOURCE_CHANGED, ERR_HTTP_STATUS, ERR_INVALID_AUTHENTICATION_CODE, ERR_INVALID_BASE_URI, 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_DUPLICATES, ERR_INVALID_ENCRYPTION_STRENGTH, ERR_INVALID_ENTRY, 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_PASSWORDS, ERR_INVALID_PASSWORD_TYPE, ERR_INVALID_PASS_THROUGH, ERR_INVALID_PASS_THROUGH_VALUE, ERR_INVALID_READER, ERR_INVALID_READER_OPTIONS, ERR_INVALID_REQUEST_PASSWORD, 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_URI, ERR_INVALID_VERSION, ERR_ITERATOR_COMPLETED_TOO_SOON, ERR_LOCAL_FILE_HEADER_NOT_FOUND, ERR_OVERLAPPING_ENTRY, ERR_PARENT_NOT_DIRECTORY, ERR_READABLE_CONSUMED, ERR_RESERVED_COMPRESSION_METHOD, ERR_ROOT_DIRECTORY_NOT_MOVABLE, ERR_SPLIT_ZIP_FILE, ERR_TARGET_NOT_DIRECTORY, ERR_UNDEFINED_COMPRESSION_METHOD, ERR_UNDEFINED_CRC32, 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_PASS_THROUGH_VALUE, ERR_UNSUPPORTED_SPLIT_USDZ, ERR_UNSUPPORTED_UINT64, ERR_WORKER_STARTUP_TIMEOUT, ERR_WRITER_NOT_INITIALIZED, ERR_WRITER_SIZE_NOT_WRITABLE, ERR_ZIP_CRYPTO_LAST_MOD_DATE, ERR_ZIP_NOT_EMPTY, HttpRangeReader, HttpReader, Reader, SplitDataReader, SplitDataWriter, TextReader, TextWriter, Uint8ArrayReader, Uint8ArrayWriter, VERSION, WARNING_APPENDED_DATA, WARNING_CLAMPED_LAST_MODIFICATION_DATE, WARNING_COMPRESSED_PATCHED_DATA, WARNING_COMPRESSION_UNAVAILABLE, 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_LOCAL_FILE_HEADER_FILENAME, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY, WARNING_MULTIPLE_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, ZipDirectoryEntry, ZipEntry, ZipFS, ZipFileEntry, ZipReader, ZipReaderStream, ZipWriter, ZipWriterStream, configure, createBlobTempStream, createOPFSTempStream, createSyncAccessHandleTempStream, fs, getMimeType, getRegisteredCodecs, getSupportedCompressionMethods, isZipFile, registerCodec, resetConfiguration, terminateWorkersAndModule as terminateWorkers, unregisterCodec };