@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
@@ -1900,8 +1900,8 @@ class DeflateStream extends TransformStream {
1900
1900
  let crc32Stream, encryptionStream, gzipCrc32Stream;
1901
1901
  let readable = super.readable;
1902
1902
  const codecStreams = format && getCodecStreams(format);
1903
- const useGzipCrc32 = computeCrc32 && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) &&
1904
- Boolean(useCompressionStream && CompressionStream);
1903
+ const GzipCompressionStream = getGzipCompressionStream(useCompressionStream, CompressionStream, CompressionStreamFallback);
1904
+ const useGzipCrc32 = computeCrc32 && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) && Boolean(GzipCompressionStream);
1905
1905
  if ((!encrypted || zipCrypto) && computeCrc32 && !useGzipCrc32) {
1906
1906
  crc32Stream = new Crc32Stream();
1907
1907
  readable = pipeThrough(readable, crc32Stream);
@@ -1911,7 +1911,7 @@ class DeflateStream extends TransformStream {
1911
1911
  readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.CompressionStream, format, { level, chunkSize, compressionMethod, uncompressedSize: inputSize }));
1912
1912
  } else if (useGzipCrc32) {
1913
1913
  gzipCrc32Stream = new GzipToRawDeflateStream();
1914
- readable = pipeThroughBackpressured(readable, new CompressionStream(FORMAT_GZIP$1));
1914
+ readable = pipeThroughBackpressured(readable, new GzipCompressionStream(FORMAT_GZIP$1, { level, chunkSize }));
1915
1915
  readable = pipeThrough(readable, gzipCrc32Stream);
1916
1916
  } else {
1917
1917
  try {
@@ -1985,8 +1985,7 @@ class GzipToRawDeflateStream extends TransformStream {
1985
1985
  }
1986
1986
  }
1987
1987
 
1988
- function pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize) {
1989
- const crc32 = new Crc32();
1988
+ function pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, crc32) {
1990
1989
  let outputLength = 0;
1991
1990
  let inputDone = false;
1992
1991
  let watchdogTimeout;
@@ -2060,7 +2059,7 @@ class InflateStream extends TransformStream {
2060
2059
  constructor(options, { chunkSize, DecompressionStreamFallback, DecompressionStream }) {
2061
2060
  super({});
2062
2061
  const { zipCrypto, encrypted, checkCrc32, crc32, compressed, useCompressionStream, deflate64, format, compressionMethod, rawBitFlag, outputSize } = options;
2063
- let crc32Stream, decryptionStream;
2062
+ let crc32Stream, decryptionStream, gzipCrc32;
2064
2063
  let readable = super.readable;
2065
2064
  if (encrypted) {
2066
2065
  if (zipCrypto) {
@@ -2087,19 +2086,20 @@ class InflateStream extends TransformStream {
2087
2086
  } catch {
2088
2087
  throw error;
2089
2088
  }
2090
- readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize);
2089
+ gzipCrc32 = new Crc32();
2090
+ readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, gzipCrc32);
2091
2091
  }
2092
2092
  }
2093
2093
  readable = mapInflateStreamError(readable);
2094
2094
  }
2095
- if (checkCrc32) {
2095
+ if (checkCrc32 && !gzipCrc32) {
2096
2096
  crc32Stream = new Crc32Stream();
2097
2097
  readable = pipeThrough(readable, crc32Stream);
2098
2098
  }
2099
2099
  setReadable(this, readable, () => {
2100
2100
  if (checkCrc32) {
2101
- const computedCrc32View = new DataView(crc32Stream.value.buffer);
2102
- if (crc32 != computedCrc32View.getUint32(0, false)) {
2101
+ const computedCrc32 = gzipCrc32 ? gzipCrc32.get() >>> 0 : new DataView(crc32Stream.value.buffer).getUint32(0, false);
2102
+ if (crc32 != computedCrc32) {
2103
2103
  throw new Error(ERR_INVALID_CRC32);
2104
2104
  }
2105
2105
  }
@@ -2155,6 +2155,14 @@ function createCodecStream(CodecStreamClass, format, options) {
2155
2155
  return new CodecStreamClass(format, options);
2156
2156
  }
2157
2157
 
2158
+ function getGzipCompressionStream(useCompressionStream, CompressionStreamNative, CompressionStreamFallback) {
2159
+ if (useCompressionStream && CompressionStreamNative) {
2160
+ return CompressionStreamNative;
2161
+ } else if (CompressionStreamFallback && CompressionStreamFallback.requiresModule) {
2162
+ return CompressionStreamFallback;
2163
+ }
2164
+ }
2165
+
2158
2166
  function pipeThroughCompressionStream(readable, useCompressionStream, options, CompressionStreamNative, CompressionStreamFallback) {
2159
2167
  const Stream = useCompressionStream && CompressionStreamNative ?
2160
2168
  CompressionStreamNative :
@@ -2527,10 +2535,12 @@ class CodecWorker {
2527
2535
  workerData.terminated = true;
2528
2536
  worker.terminate();
2529
2537
  }
2530
- terminateResolvers.forEach(resolve => resolve());
2531
2538
  }
2532
2539
  workerData.busy = false;
2533
- onTaskFinished(workerData);
2540
+ const pendingTasks = onTaskFinished(workerData);
2541
+ if (terminateResolvers) {
2542
+ terminateResolvers.forEach(resolve => resolve(pendingTasks));
2543
+ }
2534
2544
  }
2535
2545
  }
2536
2546
  });
@@ -3215,7 +3225,10 @@ async function runWorker(stream, workerOptions) {
3215
3225
 
3216
3226
  function onTaskFinished(workerData) {
3217
3227
  clearStarvationTimeout();
3218
- if (pendingRequests.length) {
3228
+ if (workerData.terminated) {
3229
+ workerData.terminated = false;
3230
+ return runPendingRequestsInline();
3231
+ } else if (pendingRequests.length) {
3219
3232
  const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
3220
3233
  resolve(new CodecWorker(workerData, stream, workerOptions, onTaskFinished));
3221
3234
  armStarvationTimeout();
@@ -3253,12 +3266,26 @@ function onWorkerStarvation() {
3253
3266
  starvationTimeout = null;
3254
3267
  if (pendingRequests.length) {
3255
3268
  const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
3256
- const inlineWorkerOptions = Object.assign({}, workerOptions, { useWebWorkers: false, workerURI: UNDEFINED_VALUE, createWorker: UNDEFINED_VALUE });
3257
- resolve(new CodecWorker({}, stream, inlineWorkerOptions, onInlineTaskFinished));
3269
+ resolve(new CodecWorker({}, stream, getInlineWorkerOptions(workerOptions), onInlineTaskFinished));
3258
3270
  armStarvationTimeout();
3259
3271
  }
3260
3272
  }
3261
3273
 
3274
+ function runPendingRequestsInline() {
3275
+ const tasks = pendingRequests.splice(0).map(({ resolve, stream, workerOptions }) => new Promise(resolveTask => {
3276
+ resolve(new CodecWorker({}, stream, getInlineWorkerOptions(workerOptions), () => {
3277
+ onInlineTaskFinished();
3278
+ resolveTask();
3279
+ }));
3280
+ }));
3281
+ clearStarvationTimeout();
3282
+ return Promise.all(tasks);
3283
+ }
3284
+
3285
+ function getInlineWorkerOptions(workerOptions) {
3286
+ return Object.assign({}, workerOptions, { useWebWorkers: false, workerURI: UNDEFINED_VALUE, createWorker: UNDEFINED_VALUE });
3287
+ }
3288
+
3262
3289
  function onInlineTaskFinished() {
3263
3290
  clearStarvationTimeout();
3264
3291
  armStarvationTimeout();
@@ -3268,18 +3295,14 @@ function terminateWorker(workerData, workerOptions) {
3268
3295
  const { config } = workerOptions;
3269
3296
  const { terminateWorkerTimeout } = config;
3270
3297
  if (Number.isFinite(terminateWorkerTimeout) && terminateWorkerTimeout >= 0) {
3271
- if (workerData.terminated) {
3272
- workerData.terminated = false;
3273
- } else {
3274
- workerData.terminateTimeout = setTimeout(async () => {
3275
- pool = pool.filter(data => data != workerData);
3276
- try {
3277
- await workerData.terminate();
3278
- } catch {
3279
- // ignored
3280
- }
3281
- }, terminateWorkerTimeout);
3282
- }
3298
+ workerData.terminateTimeout = setTimeout(async () => {
3299
+ pool = pool.filter(data => data != workerData);
3300
+ try {
3301
+ await workerData.terminate();
3302
+ } catch {
3303
+ // ignored
3304
+ }
3305
+ }, terminateWorkerTimeout);
3283
3306
  }
3284
3307
  }
3285
3308
 
@@ -3292,10 +3315,13 @@ function clearTerminateTimeout(workerData) {
3292
3315
  }
3293
3316
 
3294
3317
  async function terminateWorkers() {
3295
- await Promise.allSettled(pool.map(workerData => {
3296
- clearTerminateTimeout(workerData);
3297
- return workerData.terminate();
3298
- }));
3318
+ await Promise.allSettled([
3319
+ runPendingRequestsInline(),
3320
+ ...pool.map(workerData => {
3321
+ clearTerminateTimeout(workerData);
3322
+ return workerData.terminate();
3323
+ })
3324
+ ]);
3299
3325
  resetWebWorkerSupport();
3300
3326
  }
3301
3327
 
@@ -3370,6 +3396,19 @@ function decodeText(value, encoding) {
3370
3396
  return decode(value, encoding, true);
3371
3397
  }
3372
3398
 
3399
+ function isUTF8Text(value) {
3400
+ if (value.some(byte => byte > 0x7f)) {
3401
+ try {
3402
+ new TextDecoder("utf-8", { fatal: true }).decode(value);
3403
+ return true;
3404
+ } catch {
3405
+ return false;
3406
+ }
3407
+ } else {
3408
+ return false;
3409
+ }
3410
+ }
3411
+
3373
3412
  function decodeTextRemovingBOM(value, encoding) {
3374
3413
  return decode(value, encoding, false);
3375
3414
  }
@@ -4679,6 +4718,7 @@ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched loca
4679
4718
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
4680
4719
  const MAX_KNOWN_VERSION = 63;
4681
4720
  const DRIVE_LETTER_REGEXP = /^[a-zA-Z]:/;
4721
+ const PARENT_DIRECTORY_REGEXP = /(^|[\\/])\.\.([\\/]|$)/;
4682
4722
  const CHARSET_UTF8 = "utf-8";
4683
4723
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
4684
4724
  const CHARSET_CP437 = "cp437";
@@ -4931,12 +4971,13 @@ class ZipReader {
4931
4971
  const versionMadeBy = getUint16$1(directoryView, offset + 4);
4932
4972
  const msDosCompatible = versionMadeBy >> 8 == 0;
4933
4973
  const unixCompatible = versionMadeBy >> 8 == 3;
4934
- const rawFilename = directoryArray.subarray(filenameOffset, extraFieldOffset);
4935
4974
  const commentLength = getUint16$1(directoryView, offset + 32);
4936
4975
  const endOffset = commentOffset + commentLength;
4937
- const rawComment = directoryArray.subarray(commentOffset, endOffset);
4938
- const filenameUTF8 = languageEncodingFlag;
4939
- const commentUTF8 = languageEncodingFlag;
4976
+ const rawEntryData = new Uint8Array(directoryArray.subarray(filenameOffset, endOffset));
4977
+ const rawFilename = rawEntryData.subarray(0, fileEntry.filenameLength);
4978
+ const rawComment = rawEntryData.subarray(fileEntry.filenameLength + fileEntry.extraFieldLength);
4979
+ const filenameUTF8 = languageEncodingFlag || (!filenameEncoding && isUTF8Text(rawFilename));
4980
+ const commentUTF8 = languageEncodingFlag || (!commentEncoding && isUTF8Text(rawComment));
4940
4981
  const externalFileAttributes = getUint32$1(directoryView, offset + 38);
4941
4982
  const msdosAttributesRaw = externalFileAttributes & MAX_8_BITS;
4942
4983
  const msdosAttributes = {
@@ -4987,7 +5028,7 @@ class ZipReader {
4987
5028
  rawFilename,
4988
5029
  filenameUTF8,
4989
5030
  commentUTF8,
4990
- rawExtraField: directoryArray.subarray(extraFieldOffset, commentOffset),
5031
+ rawExtraField: rawEntryData.subarray(fileEntry.filenameLength, fileEntry.filenameLength + fileEntry.extraFieldLength),
4991
5032
  rawComment,
4992
5033
  filename,
4993
5034
  comment
@@ -5495,7 +5536,7 @@ function readDigitalSignature(signatureRecordArray) {
5495
5536
  if (getUint32$1(signatureRecordView, 0) == DIGITAL_SIGNATURE_RECORD_SIGNATURE) {
5496
5537
  const signatureDataLength = getUint16$1(signatureRecordView, 4);
5497
5538
  if (6 + signatureDataLength <= signatureRecordArray.length) {
5498
- return signatureRecordArray.subarray(6, 6 + signatureDataLength);
5539
+ return new Uint8Array(signatureRecordArray.subarray(6, 6 + signatureDataLength));
5499
5540
  }
5500
5541
  }
5501
5542
  }
@@ -6018,10 +6059,10 @@ function isUnsafeFilename(filename, filenameValidation) {
6018
6059
  if (pathParts.length > 1 && pathParts[pathParts.length - 1] === "") {
6019
6060
  pathParts.pop();
6020
6061
  }
6021
- if (pathParts.includes("..") || filename.startsWith("/") || filename.startsWith("\\\\") || DRIVE_LETTER_REGEXP.test(filename)) {
6062
+ if (PARENT_DIRECTORY_REGEXP.test(filename) || filename.startsWith("/") || filename.startsWith("\\") || DRIVE_LETTER_REGEXP.test(filename)) {
6022
6063
  return true;
6023
6064
  }
6024
- return filenameValidation == STRICTNESS_STRICT && (pathParts.includes(".") || pathParts.includes(""));
6065
+ return filenameValidation == STRICTNESS_STRICT && (pathParts.includes(".") || pathParts.includes("") || filename.includes("\0"));
6025
6066
  }
6026
6067
 
6027
6068
  function getMaxAppendedDataSize(maxAppendedDataSize, strictness) {
@@ -6107,7 +6148,7 @@ async function* scanEndOfCentralDirectory(reader, scanLength) {
6107
6148
  }
6108
6149
 
6109
6150
  function getEndOfCentralDirectoryInfo(scanArray, indexByte, offset) {
6110
- return { offset, buffer: scanArray.slice(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH).buffer };
6151
+ return { offset, buffer: new Uint8Array(scanArray.subarray(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH)).buffer };
6111
6152
  }
6112
6153
 
6113
6154
  async function getCentralDirectoryReachability(reader, view, anchoredOffset, indexByte, offset, size, remoteProbeBudget) {
@@ -8852,7 +8893,7 @@ function formatSupported(StreamClass, format) {
8852
8893
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8853
8894
  */
8854
8895
 
8855
- const VERSION = "2.14.1";
8896
+ const VERSION = "2.15.0";
8856
8897
 
8857
8898
  /*
8858
8899
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.