@zip.js/zip.js 2.14.1 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/BENCHMARKS.md +19 -11
  2. package/deno.json +1 -1
  3. package/dist/zip-core-external.js +82 -41
  4. package/dist/zip-core-external.min.js +1 -1
  5. package/dist/zip-core.js +82 -41
  6. package/dist/zip-core.min.js +1 -1
  7. package/dist/zip-fs-core-external.js +82 -41
  8. package/dist/zip-fs-core-external.min.js +1 -1
  9. package/dist/zip-fs-core.js +82 -41
  10. package/dist/zip-fs-core.min.js +1 -1
  11. package/dist/zip-fs-external.js +82 -41
  12. package/dist/zip-fs-external.min.js +1 -1
  13. package/dist/zip-fs-native.js +84 -43
  14. package/dist/zip-fs-native.min.js +1 -1
  15. package/dist/zip-fs.js +84 -43
  16. package/dist/zip-fs.min.js +1 -1
  17. package/dist/zip-legacy.js +84 -43
  18. package/dist/zip-legacy.min.js +1 -1
  19. package/dist/zip-native.js +84 -43
  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 +84 -43
  24. package/dist/zip.min.js +1 -1
  25. package/index-native.cjs +84 -43
  26. package/index-native.min.js +1 -1
  27. package/index.cjs +84 -43
  28. package/index.d.cts +20 -13
  29. package/index.d.ts +20 -13
  30. package/index.min.js +1 -1
  31. package/lib/core/codec-pool.js +35 -19
  32. package/lib/core/codec-worker.js +4 -2
  33. package/lib/core/streams/zip-entry-stream.js +18 -10
  34. package/lib/core/util/decode-text.js +15 -1
  35. package/lib/core/version.js +1 -1
  36. package/lib/core/web-worker-inline-native.js +1 -1
  37. package/lib/core/web-worker-inline-wasm.js +1 -1
  38. package/lib/core/zip-reader.js +12 -10
  39. package/lib/core/zlib-streams-inline.js +1 -1
  40. package/package.json +1 -1
package/dist/zip-core.js CHANGED
@@ -1869,8 +1869,8 @@
1869
1869
  let crc32Stream, encryptionStream, gzipCrc32Stream;
1870
1870
  let readable = super.readable;
1871
1871
  const codecStreams = format && getCodecStreams(format);
1872
- const useGzipCrc32 = computeCrc32 && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) &&
1873
- Boolean(useCompressionStream && CompressionStream);
1872
+ const GzipCompressionStream = getGzipCompressionStream(useCompressionStream, CompressionStream, CompressionStreamFallback);
1873
+ const useGzipCrc32 = computeCrc32 && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) && Boolean(GzipCompressionStream);
1874
1874
  if ((!encrypted || zipCrypto) && computeCrc32 && !useGzipCrc32) {
1875
1875
  crc32Stream = new Crc32Stream();
1876
1876
  readable = pipeThrough(readable, crc32Stream);
@@ -1880,7 +1880,7 @@
1880
1880
  readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.CompressionStream, format, { level, chunkSize, compressionMethod, uncompressedSize: inputSize }));
1881
1881
  } else if (useGzipCrc32) {
1882
1882
  gzipCrc32Stream = new GzipToRawDeflateStream();
1883
- readable = pipeThroughBackpressured(readable, new CompressionStream(FORMAT_GZIP));
1883
+ readable = pipeThroughBackpressured(readable, new GzipCompressionStream(FORMAT_GZIP, { level, chunkSize }));
1884
1884
  readable = pipeThrough(readable, gzipCrc32Stream);
1885
1885
  } else {
1886
1886
  try {
@@ -1954,8 +1954,7 @@
1954
1954
  }
1955
1955
  }
1956
1956
 
1957
- function pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize) {
1958
- const crc32 = new Crc32();
1957
+ function pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, crc32) {
1959
1958
  let outputLength = 0;
1960
1959
  let inputDone = false;
1961
1960
  let watchdogTimeout;
@@ -2029,7 +2028,7 @@
2029
2028
  constructor(options, { chunkSize, DecompressionStreamFallback, DecompressionStream }) {
2030
2029
  super({});
2031
2030
  const { zipCrypto, encrypted, checkCrc32, crc32, compressed, useCompressionStream, deflate64, format, compressionMethod, rawBitFlag, outputSize } = options;
2032
- let crc32Stream, decryptionStream;
2031
+ let crc32Stream, decryptionStream, gzipCrc32;
2033
2032
  let readable = super.readable;
2034
2033
  if (encrypted) {
2035
2034
  if (zipCrypto) {
@@ -2056,19 +2055,20 @@
2056
2055
  } catch {
2057
2056
  throw error;
2058
2057
  }
2059
- readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize);
2058
+ gzipCrc32 = new Crc32();
2059
+ readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, gzipCrc32);
2060
2060
  }
2061
2061
  }
2062
2062
  readable = mapInflateStreamError(readable);
2063
2063
  }
2064
- if (checkCrc32) {
2064
+ if (checkCrc32 && !gzipCrc32) {
2065
2065
  crc32Stream = new Crc32Stream();
2066
2066
  readable = pipeThrough(readable, crc32Stream);
2067
2067
  }
2068
2068
  setReadable(this, readable, () => {
2069
2069
  if (checkCrc32) {
2070
- const computedCrc32View = new DataView(crc32Stream.value.buffer);
2071
- if (crc32 != computedCrc32View.getUint32(0, false)) {
2070
+ const computedCrc32 = gzipCrc32 ? gzipCrc32.get() >>> 0 : new DataView(crc32Stream.value.buffer).getUint32(0, false);
2071
+ if (crc32 != computedCrc32) {
2072
2072
  throw new Error(ERR_INVALID_CRC32);
2073
2073
  }
2074
2074
  }
@@ -2124,6 +2124,14 @@
2124
2124
  return new CodecStreamClass(format, options);
2125
2125
  }
2126
2126
 
2127
+ function getGzipCompressionStream(useCompressionStream, CompressionStreamNative, CompressionStreamFallback) {
2128
+ if (useCompressionStream && CompressionStreamNative) {
2129
+ return CompressionStreamNative;
2130
+ } else if (CompressionStreamFallback && CompressionStreamFallback.requiresModule) {
2131
+ return CompressionStreamFallback;
2132
+ }
2133
+ }
2134
+
2127
2135
  function pipeThroughCompressionStream(readable, useCompressionStream, options, CompressionStreamNative, CompressionStreamFallback) {
2128
2136
  const Stream = useCompressionStream && CompressionStreamNative ?
2129
2137
  CompressionStreamNative :
@@ -2492,10 +2500,12 @@
2492
2500
  workerData.terminated = true;
2493
2501
  worker.terminate();
2494
2502
  }
2495
- terminateResolvers.forEach(resolve => resolve());
2496
2503
  }
2497
2504
  workerData.busy = false;
2498
- onTaskFinished(workerData);
2505
+ const pendingTasks = onTaskFinished(workerData);
2506
+ if (terminateResolvers) {
2507
+ terminateResolvers.forEach(resolve => resolve(pendingTasks));
2508
+ }
2499
2509
  }
2500
2510
  }
2501
2511
  });
@@ -3180,7 +3190,10 @@
3180
3190
 
3181
3191
  function onTaskFinished(workerData) {
3182
3192
  clearStarvationTimeout();
3183
- if (pendingRequests.length) {
3193
+ if (workerData.terminated) {
3194
+ workerData.terminated = false;
3195
+ return runPendingRequestsInline();
3196
+ } else if (pendingRequests.length) {
3184
3197
  const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
3185
3198
  resolve(new CodecWorker(workerData, stream, workerOptions, onTaskFinished));
3186
3199
  armStarvationTimeout();
@@ -3218,12 +3231,26 @@
3218
3231
  starvationTimeout = null;
3219
3232
  if (pendingRequests.length) {
3220
3233
  const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
3221
- const inlineWorkerOptions = Object.assign({}, workerOptions, { useWebWorkers: false, workerURI: UNDEFINED_VALUE, createWorker: UNDEFINED_VALUE });
3222
- resolve(new CodecWorker({}, stream, inlineWorkerOptions, onInlineTaskFinished));
3234
+ resolve(new CodecWorker({}, stream, getInlineWorkerOptions(workerOptions), onInlineTaskFinished));
3223
3235
  armStarvationTimeout();
3224
3236
  }
3225
3237
  }
3226
3238
 
3239
+ function runPendingRequestsInline() {
3240
+ const tasks = pendingRequests.splice(0).map(({ resolve, stream, workerOptions }) => new Promise(resolveTask => {
3241
+ resolve(new CodecWorker({}, stream, getInlineWorkerOptions(workerOptions), () => {
3242
+ onInlineTaskFinished();
3243
+ resolveTask();
3244
+ }));
3245
+ }));
3246
+ clearStarvationTimeout();
3247
+ return Promise.all(tasks);
3248
+ }
3249
+
3250
+ function getInlineWorkerOptions(workerOptions) {
3251
+ return Object.assign({}, workerOptions, { useWebWorkers: false, workerURI: UNDEFINED_VALUE, createWorker: UNDEFINED_VALUE });
3252
+ }
3253
+
3227
3254
  function onInlineTaskFinished() {
3228
3255
  clearStarvationTimeout();
3229
3256
  armStarvationTimeout();
@@ -3233,18 +3260,14 @@
3233
3260
  const { config } = workerOptions;
3234
3261
  const { terminateWorkerTimeout } = config;
3235
3262
  if (Number.isFinite(terminateWorkerTimeout) && terminateWorkerTimeout >= 0) {
3236
- if (workerData.terminated) {
3237
- workerData.terminated = false;
3238
- } else {
3239
- workerData.terminateTimeout = setTimeout(async () => {
3240
- pool = pool.filter(data => data != workerData);
3241
- try {
3242
- await workerData.terminate();
3243
- } catch {
3244
- // ignored
3245
- }
3246
- }, terminateWorkerTimeout);
3247
- }
3263
+ workerData.terminateTimeout = setTimeout(async () => {
3264
+ pool = pool.filter(data => data != workerData);
3265
+ try {
3266
+ await workerData.terminate();
3267
+ } catch {
3268
+ // ignored
3269
+ }
3270
+ }, terminateWorkerTimeout);
3248
3271
  }
3249
3272
  }
3250
3273
 
@@ -3257,10 +3280,13 @@
3257
3280
  }
3258
3281
 
3259
3282
  async function terminateWorkers() {
3260
- await Promise.allSettled(pool.map(workerData => {
3261
- clearTerminateTimeout(workerData);
3262
- return workerData.terminate();
3263
- }));
3283
+ await Promise.allSettled([
3284
+ runPendingRequestsInline(),
3285
+ ...pool.map(workerData => {
3286
+ clearTerminateTimeout(workerData);
3287
+ return workerData.terminate();
3288
+ })
3289
+ ]);
3264
3290
  resetWebWorkerSupport();
3265
3291
  }
3266
3292
 
@@ -3335,6 +3361,19 @@
3335
3361
  return decode(value, encoding, true);
3336
3362
  }
3337
3363
 
3364
+ function isUTF8Text(value) {
3365
+ if (value.some(byte => byte > 0x7f)) {
3366
+ try {
3367
+ new TextDecoder("utf-8", { fatal: true }).decode(value);
3368
+ return true;
3369
+ } catch {
3370
+ return false;
3371
+ }
3372
+ } else {
3373
+ return false;
3374
+ }
3375
+ }
3376
+
3338
3377
  function decodeTextRemovingBOM(value, encoding) {
3339
3378
  return decode(value, encoding, false);
3340
3379
  }
@@ -4624,6 +4663,7 @@
4624
4663
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
4625
4664
  const MAX_KNOWN_VERSION = 63;
4626
4665
  const DRIVE_LETTER_REGEXP = /^[a-zA-Z]:/;
4666
+ const PARENT_DIRECTORY_REGEXP = /(^|[\\/])\.\.([\\/]|$)/;
4627
4667
  const CHARSET_UTF8 = "utf-8";
4628
4668
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
4629
4669
  const CHARSET_CP437 = "cp437";
@@ -4876,12 +4916,13 @@
4876
4916
  const versionMadeBy = getUint16$1(directoryView, offset + 4);
4877
4917
  const msDosCompatible = versionMadeBy >> 8 == 0;
4878
4918
  const unixCompatible = versionMadeBy >> 8 == 3;
4879
- const rawFilename = directoryArray.subarray(filenameOffset, extraFieldOffset);
4880
4919
  const commentLength = getUint16$1(directoryView, offset + 32);
4881
4920
  const endOffset = commentOffset + commentLength;
4882
- const rawComment = directoryArray.subarray(commentOffset, endOffset);
4883
- const filenameUTF8 = languageEncodingFlag;
4884
- const commentUTF8 = languageEncodingFlag;
4921
+ const rawEntryData = new Uint8Array(directoryArray.subarray(filenameOffset, endOffset));
4922
+ const rawFilename = rawEntryData.subarray(0, fileEntry.filenameLength);
4923
+ const rawComment = rawEntryData.subarray(fileEntry.filenameLength + fileEntry.extraFieldLength);
4924
+ const filenameUTF8 = languageEncodingFlag || (!filenameEncoding && isUTF8Text(rawFilename));
4925
+ const commentUTF8 = languageEncodingFlag || (!commentEncoding && isUTF8Text(rawComment));
4885
4926
  const externalFileAttributes = getUint32$1(directoryView, offset + 38);
4886
4927
  const msdosAttributesRaw = externalFileAttributes & MAX_8_BITS;
4887
4928
  const msdosAttributes = {
@@ -4932,7 +4973,7 @@
4932
4973
  rawFilename,
4933
4974
  filenameUTF8,
4934
4975
  commentUTF8,
4935
- rawExtraField: directoryArray.subarray(extraFieldOffset, commentOffset),
4976
+ rawExtraField: rawEntryData.subarray(fileEntry.filenameLength, fileEntry.filenameLength + fileEntry.extraFieldLength),
4936
4977
  rawComment,
4937
4978
  filename,
4938
4979
  comment
@@ -5440,7 +5481,7 @@
5440
5481
  if (getUint32$1(signatureRecordView, 0) == DIGITAL_SIGNATURE_RECORD_SIGNATURE) {
5441
5482
  const signatureDataLength = getUint16$1(signatureRecordView, 4);
5442
5483
  if (6 + signatureDataLength <= signatureRecordArray.length) {
5443
- return signatureRecordArray.subarray(6, 6 + signatureDataLength);
5484
+ return new Uint8Array(signatureRecordArray.subarray(6, 6 + signatureDataLength));
5444
5485
  }
5445
5486
  }
5446
5487
  }
@@ -5963,10 +6004,10 @@
5963
6004
  if (pathParts.length > 1 && pathParts[pathParts.length - 1] === "") {
5964
6005
  pathParts.pop();
5965
6006
  }
5966
- if (pathParts.includes("..") || filename.startsWith("/") || filename.startsWith("\\\\") || DRIVE_LETTER_REGEXP.test(filename)) {
6007
+ if (PARENT_DIRECTORY_REGEXP.test(filename) || filename.startsWith("/") || filename.startsWith("\\") || DRIVE_LETTER_REGEXP.test(filename)) {
5967
6008
  return true;
5968
6009
  }
5969
- return filenameValidation == STRICTNESS_STRICT && (pathParts.includes(".") || pathParts.includes(""));
6010
+ return filenameValidation == STRICTNESS_STRICT && (pathParts.includes(".") || pathParts.includes("") || filename.includes("\0"));
5970
6011
  }
5971
6012
 
5972
6013
  function getMaxAppendedDataSize(maxAppendedDataSize, strictness) {
@@ -6052,7 +6093,7 @@
6052
6093
  }
6053
6094
 
6054
6095
  function getEndOfCentralDirectoryInfo(scanArray, indexByte, offset) {
6055
- return { offset, buffer: scanArray.slice(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH).buffer };
6096
+ return { offset, buffer: new Uint8Array(scanArray.subarray(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH)).buffer };
6056
6097
  }
6057
6098
 
6058
6099
  async function getCentralDirectoryReachability(reader, view, anchoredOffset, indexByte, offset, size, remoteProbeBudget) {
@@ -8699,7 +8740,7 @@
8699
8740
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8700
8741
  */
8701
8742
 
8702
- const VERSION = "2.14.1";
8743
+ const VERSION = "2.15.0";
8703
8744
 
8704
8745
  /*
8705
8746
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.