@zip.js/zip.js 2.14.1 → 2.16.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 (41) hide show
  1. package/BENCHMARKS.md +21 -11
  2. package/deno.json +1 -1
  3. package/dist/zip-core-external.js +217 -98
  4. package/dist/zip-core-external.min.js +1 -1
  5. package/dist/zip-core.js +217 -98
  6. package/dist/zip-core.min.js +1 -1
  7. package/dist/zip-fs-core-external.js +217 -98
  8. package/dist/zip-fs-core-external.min.js +1 -1
  9. package/dist/zip-fs-core.js +217 -98
  10. package/dist/zip-fs-core.min.js +1 -1
  11. package/dist/zip-fs-external.js +217 -98
  12. package/dist/zip-fs-external.min.js +1 -1
  13. package/dist/zip-fs-native.js +219 -100
  14. package/dist/zip-fs-native.min.js +1 -1
  15. package/dist/zip-fs.js +219 -100
  16. package/dist/zip-fs.min.js +1 -1
  17. package/dist/zip-legacy.js +219 -100
  18. package/dist/zip-legacy.min.js +1 -1
  19. package/dist/zip-native.js +219 -100
  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 +219 -100
  24. package/dist/zip.min.js +1 -1
  25. package/index-native.cjs +219 -100
  26. package/index-native.min.js +1 -1
  27. package/index.cjs +219 -100
  28. package/index.d.cts +46 -13
  29. package/index.d.ts +46 -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/io.js +3 -2
  34. package/lib/core/streams/zip-entry-stream.js +151 -66
  35. package/lib/core/util/decode-text.js +15 -1
  36. package/lib/core/version.js +1 -1
  37. package/lib/core/web-worker-inline-native.js +1 -1
  38. package/lib/core/web-worker-inline-wasm.js +1 -1
  39. package/lib/core/zip-reader.js +12 -10
  40. package/lib/core/zlib-streams-inline.js +1 -1
  41. package/package.json +1 -1
@@ -1889,7 +1889,6 @@ const FORMAT_GZIP$1 = "gzip";
1889
1889
  const GZIP_HEADER_LENGTH = 10;
1890
1890
  const GZIP_TRAILER_LENGTH = 8;
1891
1891
  const GZIP_HEADER_BYTES = [0x1f, 0x8b, 0x08];
1892
- const GZIP_OUTPUT_STALL_TIMEOUT = 5000;
1893
1892
 
1894
1893
  class DeflateStream extends TransformStream {
1895
1894
 
@@ -1900,8 +1899,8 @@ class DeflateStream extends TransformStream {
1900
1899
  let crc32Stream, encryptionStream, gzipCrc32Stream;
1901
1900
  let readable = super.readable;
1902
1901
  const codecStreams = format && getCodecStreams(format);
1903
- const useGzipCrc32 = computeCrc32 && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) &&
1904
- Boolean(useCompressionStream && CompressionStream);
1902
+ const GzipCompressionStream = getGzipCodecStream(useCompressionStream, CompressionStream, CompressionStreamFallback);
1903
+ const useGzipCrc32 = computeCrc32 && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) && Boolean(GzipCompressionStream);
1905
1904
  if ((!encrypted || zipCrypto) && computeCrc32 && !useGzipCrc32) {
1906
1905
  crc32Stream = new Crc32Stream();
1907
1906
  readable = pipeThrough(readable, crc32Stream);
@@ -1911,12 +1910,15 @@ class DeflateStream extends TransformStream {
1911
1910
  readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.CompressionStream, format, { level, chunkSize, compressionMethod, uncompressedSize: inputSize }));
1912
1911
  } else if (useGzipCrc32) {
1913
1912
  gzipCrc32Stream = new GzipToRawDeflateStream();
1914
- readable = pipeThroughBackpressured(readable, new CompressionStream(FORMAT_GZIP$1));
1913
+ readable = pipeThroughBackpressured(readable, new GzipCompressionStream(FORMAT_GZIP$1, { level, chunkSize }));
1915
1914
  readable = pipeThrough(readable, gzipCrc32Stream);
1916
1915
  } else {
1917
1916
  try {
1918
1917
  readable = pipeThroughCompressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStream, CompressionStreamFallback);
1919
1918
  } catch (error) {
1919
+ if (!useCompressionStream && CompressionStreamFallback) {
1920
+ throw error;
1921
+ }
1920
1922
  let gzipStream;
1921
1923
  try {
1922
1924
  gzipStream = new CompressionStream(FORMAT_GZIP$1);
@@ -1985,82 +1987,144 @@ class GzipToRawDeflateStream extends TransformStream {
1985
1987
  }
1986
1988
  }
1987
1989
 
1988
- function pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize) {
1989
- const crc32 = new Crc32();
1990
+ function pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, crc32) {
1991
+ const writer = gzipStream.writable.getWriter();
1992
+ const reader = gzipStream.readable.getReader();
1993
+ const outputCrc32 = crc32 === UNDEFINED_VALUE ? new Crc32() : UNDEFINED_VALUE;
1990
1994
  let outputLength = 0;
1991
1995
  let inputDone = false;
1992
- let watchdogTimeout;
1996
+ let trailerWritten = false;
1997
+ let idleCheckArmed = false;
1998
+ let readCount = 0;
1999
+ let readPending = false;
1993
2000
  let resolveTrailerReady, rejectTrailerReady;
1994
2001
  const trailerReady = new Promise((resolve, reject) => {
1995
2002
  resolveTrailerReady = resolve;
1996
2003
  rejectTrailerReady = reject;
1997
2004
  });
1998
2005
  trailerReady.catch(() => { });
1999
- if (!outputSize) {
2000
- resolveTrailerReady();
2001
- }
2002
- const gzipWrapStream = new TransformStream({
2003
- start(controller) {
2006
+ pump();
2007
+ return new ReadableStream({
2008
+ async pull(controller) {
2009
+ let result;
2010
+ try {
2011
+ result = await read();
2012
+ } catch (error) {
2013
+ throw trailerWritten ? getTrailerError(error) : error;
2014
+ }
2015
+ const { value, done } = result;
2016
+ if (done) {
2017
+ controller.close();
2018
+ } else {
2019
+ outputLength += value.length;
2020
+ if (outputLength > outputSize) {
2021
+ const error = new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
2022
+ rejectTrailerReady(error);
2023
+ await cancel(reader, error);
2024
+ throw error;
2025
+ }
2026
+ if (outputCrc32) {
2027
+ outputCrc32.append(value);
2028
+ }
2029
+ controller.enqueue(value);
2030
+ }
2031
+ },
2032
+ cancel(reason) {
2033
+ rejectTrailerReady(reason);
2034
+ return reader.cancel(reason);
2035
+ }
2036
+ });
2037
+
2038
+ async function pump() {
2039
+ const inputReader = readable.getReader();
2040
+ try {
2004
2041
  const header = new Uint8Array(GZIP_HEADER_LENGTH);
2005
2042
  header.set(GZIP_HEADER_BYTES);
2006
- controller.enqueue(header);
2007
- },
2008
- transform(chunk, controller) {
2009
- controller.enqueue(chunk);
2010
- },
2011
- async flush(controller) {
2043
+ await writer.write(header);
2044
+ for (; ;) {
2045
+ await writer.ready;
2046
+ const { value, done } = await inputReader.read();
2047
+ if (done) {
2048
+ break;
2049
+ }
2050
+ await writer.write(value);
2051
+ }
2012
2052
  inputDone = true;
2013
- startWatchdog();
2014
- try {
2053
+ if (outputCrc32) {
2054
+ if (readPending) {
2055
+ armIdleCheck();
2056
+ }
2015
2057
  await trailerReady;
2016
- } finally {
2017
- stopWatchdog();
2018
2058
  }
2019
2059
  const trailer = new Uint8Array(GZIP_TRAILER_LENGTH);
2020
2060
  const dataView = getDataView(trailer);
2021
- dataView.setUint32(0, crc32.get(), true);
2061
+ dataView.setUint32(0, outputCrc32 ? outputCrc32.get() : crc32, true);
2022
2062
  dataView.setUint32(4, outputSize, true);
2023
- controller.enqueue(trailer);
2024
- },
2025
- cancel(reason) {
2026
- rejectTrailerReady(reason);
2063
+ trailerWritten = true;
2064
+ await writer.write(trailer);
2065
+ await writer.close();
2066
+ } catch (error) {
2067
+ await abort(writer, error);
2068
+ await cancel(inputReader, error);
2027
2069
  }
2028
- });
2029
- const outputStream = new TransformStream({
2030
- transform(chunk, controller) {
2031
- crc32.append(chunk);
2032
- outputLength += chunk.length;
2033
- if (outputLength >= outputSize) {
2034
- resolveTrailerReady();
2035
- } else if (inputDone) {
2036
- startWatchdog();
2037
- }
2038
- controller.enqueue(chunk);
2039
- },
2040
- cancel(reason) {
2041
- rejectTrailerReady(reason);
2070
+ }
2071
+
2072
+ function read() {
2073
+ readCount++;
2074
+ readPending = true;
2075
+ const result = reader.read();
2076
+ result.then(onReadSettled, onReadSettled);
2077
+ if (inputDone && outputCrc32) {
2078
+ armIdleCheck();
2042
2079
  }
2043
- });
2044
- readable = pipeThrough(readable, gzipWrapStream);
2045
- readable = pipeThroughBackpressured(readable, gzipStream);
2046
- return pipeThrough(readable, outputStream);
2080
+ return result;
2081
+ }
2047
2082
 
2048
- function startWatchdog() {
2049
- stopWatchdog();
2050
- watchdogTimeout = setTimeout(() => rejectTrailerReady(new Error(ERR_INVALID_UNCOMPRESSED_SIZE)), GZIP_OUTPUT_STALL_TIMEOUT);
2083
+ function onReadSettled() {
2084
+ readPending = false;
2051
2085
  }
2052
2086
 
2053
- function stopWatchdog() {
2054
- clearTimeout(watchdogTimeout);
2087
+ async function armIdleCheck() {
2088
+ if (!idleCheckArmed) {
2089
+ idleCheckArmed = true;
2090
+ const count = readCount;
2091
+ await nextTask();
2092
+ idleCheckArmed = false;
2093
+ if (readPending) {
2094
+ if (readCount == count) {
2095
+ resolveTrailerReady();
2096
+ } else {
2097
+ armIdleCheck();
2098
+ }
2099
+ }
2100
+ }
2101
+ }
2102
+
2103
+ function getTrailerError(error) {
2104
+ const trailerError = new Error(outputLength == outputSize ? ERR_INVALID_CRC32 : ERR_INVALID_UNCOMPRESSED_SIZE);
2105
+ trailerError.cause = error;
2106
+ return trailerError;
2055
2107
  }
2056
2108
  }
2057
2109
 
2110
+ function nextTask() {
2111
+ return new Promise(resolve => {
2112
+ const { port1, port2 } = new MessageChannel();
2113
+ port2.onmessage = () => {
2114
+ port1.close();
2115
+ port2.close();
2116
+ resolve();
2117
+ };
2118
+ port1.postMessage(UNDEFINED_VALUE);
2119
+ });
2120
+ }
2121
+
2058
2122
  class InflateStream extends TransformStream {
2059
2123
 
2060
2124
  constructor(options, { chunkSize, DecompressionStreamFallback, DecompressionStream }) {
2061
2125
  super({});
2062
2126
  const { zipCrypto, encrypted, checkCrc32, crc32, compressed, useCompressionStream, deflate64, format, compressionMethod, rawBitFlag, outputSize } = options;
2063
- let crc32Stream, decryptionStream;
2127
+ let crc32Stream, decryptionStream, gzipCrc32;
2064
2128
  let readable = super.readable;
2065
2129
  if (encrypted) {
2066
2130
  if (zipCrypto) {
@@ -2075,31 +2139,44 @@ class InflateStream extends TransformStream {
2075
2139
  if (codecStreams) {
2076
2140
  readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.DecompressionStream, format, { chunkSize, compressionMethod, rawBitFlag, uncompressedSize: outputSize }));
2077
2141
  } else {
2078
- try {
2079
- readable = pipeThroughCompressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamFallback);
2080
- } catch (error) {
2081
- if (deflate64 || outputSize === UNDEFINED_VALUE) {
2082
- throw error;
2083
- }
2084
- let gzipStream;
2142
+ let gzipStream;
2143
+ const GzipDecompressionStream = getGzipCodecStream(useCompressionStream, DecompressionStream, DecompressionStreamFallback);
2144
+ if (checkCrc32 && !deflate64 && crc32 !== UNDEFINED_VALUE && outputSize !== UNDEFINED_VALUE && GzipDecompressionStream) {
2085
2145
  try {
2086
- gzipStream = new DecompressionStream(FORMAT_GZIP$1);
2146
+ gzipStream = new GzipDecompressionStream(FORMAT_GZIP$1, { chunkSize });
2087
2147
  } catch {
2088
- throw error;
2148
+ gzipStream = UNDEFINED_VALUE;
2149
+ }
2150
+ }
2151
+ if (!gzipStream) {
2152
+ try {
2153
+ readable = pipeThroughCompressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamFallback);
2154
+ } catch (error) {
2155
+ if (deflate64 || outputSize === UNDEFINED_VALUE || (!useCompressionStream && DecompressionStreamFallback)) {
2156
+ throw error;
2157
+ }
2158
+ try {
2159
+ gzipStream = new DecompressionStream(FORMAT_GZIP$1);
2160
+ } catch {
2161
+ throw error;
2162
+ }
2089
2163
  }
2090
- readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize);
2164
+ }
2165
+ if (gzipStream) {
2166
+ gzipCrc32 = true;
2167
+ readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, crc32);
2091
2168
  }
2092
2169
  }
2093
2170
  readable = mapInflateStreamError(readable);
2094
2171
  }
2095
- if (checkCrc32) {
2172
+ if (checkCrc32 && !gzipCrc32) {
2096
2173
  crc32Stream = new Crc32Stream();
2097
2174
  readable = pipeThrough(readable, crc32Stream);
2098
2175
  }
2099
2176
  setReadable(this, readable, () => {
2100
- if (checkCrc32) {
2101
- const computedCrc32View = new DataView(crc32Stream.value.buffer);
2102
- if (crc32 != computedCrc32View.getUint32(0, false)) {
2177
+ if (crc32Stream) {
2178
+ const computedCrc32 = new DataView(crc32Stream.value.buffer).getUint32(0, false);
2179
+ if (crc32 != computedCrc32) {
2103
2180
  throw new Error(ERR_INVALID_CRC32);
2104
2181
  }
2105
2182
  }
@@ -2155,6 +2232,14 @@ function createCodecStream(CodecStreamClass, format, options) {
2155
2232
  return new CodecStreamClass(format, options);
2156
2233
  }
2157
2234
 
2235
+ function getGzipCodecStream(useCompressionStream, CodecStreamNative, CodecStreamFallback) {
2236
+ if (useCompressionStream && CodecStreamNative) {
2237
+ return CodecStreamNative;
2238
+ } else if (CodecStreamFallback && CodecStreamFallback.requiresModule) {
2239
+ return CodecStreamFallback;
2240
+ }
2241
+ }
2242
+
2158
2243
  function pipeThroughCompressionStream(readable, useCompressionStream, options, CompressionStreamNative, CompressionStreamFallback) {
2159
2244
  const Stream = useCompressionStream && CompressionStreamNative ?
2160
2245
  CompressionStreamNative :
@@ -2527,10 +2612,12 @@ class CodecWorker {
2527
2612
  workerData.terminated = true;
2528
2613
  worker.terminate();
2529
2614
  }
2530
- terminateResolvers.forEach(resolve => resolve());
2531
2615
  }
2532
2616
  workerData.busy = false;
2533
- onTaskFinished(workerData);
2617
+ const pendingTasks = onTaskFinished(workerData);
2618
+ if (terminateResolvers) {
2619
+ terminateResolvers.forEach(resolve => resolve(pendingTasks));
2620
+ }
2534
2621
  }
2535
2622
  }
2536
2623
  });
@@ -3215,7 +3302,10 @@ async function runWorker(stream, workerOptions) {
3215
3302
 
3216
3303
  function onTaskFinished(workerData) {
3217
3304
  clearStarvationTimeout();
3218
- if (pendingRequests.length) {
3305
+ if (workerData.terminated) {
3306
+ workerData.terminated = false;
3307
+ return runPendingRequestsInline();
3308
+ } else if (pendingRequests.length) {
3219
3309
  const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
3220
3310
  resolve(new CodecWorker(workerData, stream, workerOptions, onTaskFinished));
3221
3311
  armStarvationTimeout();
@@ -3253,12 +3343,26 @@ function onWorkerStarvation() {
3253
3343
  starvationTimeout = null;
3254
3344
  if (pendingRequests.length) {
3255
3345
  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));
3346
+ resolve(new CodecWorker({}, stream, getInlineWorkerOptions(workerOptions), onInlineTaskFinished));
3258
3347
  armStarvationTimeout();
3259
3348
  }
3260
3349
  }
3261
3350
 
3351
+ function runPendingRequestsInline() {
3352
+ const tasks = pendingRequests.splice(0).map(({ resolve, stream, workerOptions }) => new Promise(resolveTask => {
3353
+ resolve(new CodecWorker({}, stream, getInlineWorkerOptions(workerOptions), () => {
3354
+ onInlineTaskFinished();
3355
+ resolveTask();
3356
+ }));
3357
+ }));
3358
+ clearStarvationTimeout();
3359
+ return Promise.all(tasks);
3360
+ }
3361
+
3362
+ function getInlineWorkerOptions(workerOptions) {
3363
+ return Object.assign({}, workerOptions, { useWebWorkers: false, workerURI: UNDEFINED_VALUE, createWorker: UNDEFINED_VALUE });
3364
+ }
3365
+
3262
3366
  function onInlineTaskFinished() {
3263
3367
  clearStarvationTimeout();
3264
3368
  armStarvationTimeout();
@@ -3268,18 +3372,14 @@ function terminateWorker(workerData, workerOptions) {
3268
3372
  const { config } = workerOptions;
3269
3373
  const { terminateWorkerTimeout } = config;
3270
3374
  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
- }
3375
+ workerData.terminateTimeout = setTimeout(async () => {
3376
+ pool = pool.filter(data => data != workerData);
3377
+ try {
3378
+ await workerData.terminate();
3379
+ } catch {
3380
+ // ignored
3381
+ }
3382
+ }, terminateWorkerTimeout);
3283
3383
  }
3284
3384
  }
3285
3385
 
@@ -3292,10 +3392,13 @@ function clearTerminateTimeout(workerData) {
3292
3392
  }
3293
3393
 
3294
3394
  async function terminateWorkers() {
3295
- await Promise.allSettled(pool.map(workerData => {
3296
- clearTerminateTimeout(workerData);
3297
- return workerData.terminate();
3298
- }));
3395
+ await Promise.allSettled([
3396
+ runPendingRequestsInline(),
3397
+ ...pool.map(workerData => {
3398
+ clearTerminateTimeout(workerData);
3399
+ return workerData.terminate();
3400
+ })
3401
+ ]);
3299
3402
  resetWebWorkerSupport();
3300
3403
  }
3301
3404
 
@@ -3370,6 +3473,19 @@ function decodeText(value, encoding) {
3370
3473
  return decode(value, encoding, true);
3371
3474
  }
3372
3475
 
3476
+ function isUTF8Text(value) {
3477
+ if (value.some(byte => byte > 0x7f)) {
3478
+ try {
3479
+ new TextDecoder("utf-8", { fatal: true }).decode(value);
3480
+ return true;
3481
+ } catch {
3482
+ return false;
3483
+ }
3484
+ } else {
3485
+ return false;
3486
+ }
3487
+ }
3488
+
3373
3489
  function decodeTextRemovingBOM(value, encoding) {
3374
3490
  return decode(value, encoding, false);
3375
3491
  }
@@ -3722,10 +3838,11 @@ class FetchReader extends Reader {
3722
3838
 
3723
3839
  createReadable(options) {
3724
3840
  const reader = this;
3725
- const { useRangeHeader, forceRangeRequests, size } = reader;
3841
+ const { useRangeHeader, forceRangeRequests, size, eocdCache } = reader;
3726
3842
  if ((useRangeHeader || forceRangeRequests) && size !== UNDEFINED_VALUE) {
3727
3843
  const { offset = 0, size: readSize = size - offset } = options || {};
3728
- if (readSize > 0 && offset < size) {
3844
+ const cached = eocdCache && offset >= size - eocdCache.length;
3845
+ if (readSize > 0 && offset < size && !cached) {
3729
3846
  return createRangeReadable(reader, offset, Math.min(readSize, size - offset));
3730
3847
  }
3731
3848
  }
@@ -4679,6 +4796,7 @@ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched loca
4679
4796
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
4680
4797
  const MAX_KNOWN_VERSION = 63;
4681
4798
  const DRIVE_LETTER_REGEXP = /^[a-zA-Z]:/;
4799
+ const PARENT_DIRECTORY_REGEXP = /(^|[\\/])\.\.([\\/]|$)/;
4682
4800
  const CHARSET_UTF8 = "utf-8";
4683
4801
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
4684
4802
  const CHARSET_CP437 = "cp437";
@@ -4931,12 +5049,13 @@ class ZipReader {
4931
5049
  const versionMadeBy = getUint16$1(directoryView, offset + 4);
4932
5050
  const msDosCompatible = versionMadeBy >> 8 == 0;
4933
5051
  const unixCompatible = versionMadeBy >> 8 == 3;
4934
- const rawFilename = directoryArray.subarray(filenameOffset, extraFieldOffset);
4935
5052
  const commentLength = getUint16$1(directoryView, offset + 32);
4936
5053
  const endOffset = commentOffset + commentLength;
4937
- const rawComment = directoryArray.subarray(commentOffset, endOffset);
4938
- const filenameUTF8 = languageEncodingFlag;
4939
- const commentUTF8 = languageEncodingFlag;
5054
+ const rawEntryData = new Uint8Array(directoryArray.subarray(filenameOffset, endOffset));
5055
+ const rawFilename = rawEntryData.subarray(0, fileEntry.filenameLength);
5056
+ const rawComment = rawEntryData.subarray(fileEntry.filenameLength + fileEntry.extraFieldLength);
5057
+ const filenameUTF8 = languageEncodingFlag || (!filenameEncoding && isUTF8Text(rawFilename));
5058
+ const commentUTF8 = languageEncodingFlag || (!commentEncoding && isUTF8Text(rawComment));
4940
5059
  const externalFileAttributes = getUint32$1(directoryView, offset + 38);
4941
5060
  const msdosAttributesRaw = externalFileAttributes & MAX_8_BITS;
4942
5061
  const msdosAttributes = {
@@ -4987,7 +5106,7 @@ class ZipReader {
4987
5106
  rawFilename,
4988
5107
  filenameUTF8,
4989
5108
  commentUTF8,
4990
- rawExtraField: directoryArray.subarray(extraFieldOffset, commentOffset),
5109
+ rawExtraField: rawEntryData.subarray(fileEntry.filenameLength, fileEntry.filenameLength + fileEntry.extraFieldLength),
4991
5110
  rawComment,
4992
5111
  filename,
4993
5112
  comment
@@ -5495,7 +5614,7 @@ function readDigitalSignature(signatureRecordArray) {
5495
5614
  if (getUint32$1(signatureRecordView, 0) == DIGITAL_SIGNATURE_RECORD_SIGNATURE) {
5496
5615
  const signatureDataLength = getUint16$1(signatureRecordView, 4);
5497
5616
  if (6 + signatureDataLength <= signatureRecordArray.length) {
5498
- return signatureRecordArray.subarray(6, 6 + signatureDataLength);
5617
+ return new Uint8Array(signatureRecordArray.subarray(6, 6 + signatureDataLength));
5499
5618
  }
5500
5619
  }
5501
5620
  }
@@ -6018,10 +6137,10 @@ function isUnsafeFilename(filename, filenameValidation) {
6018
6137
  if (pathParts.length > 1 && pathParts[pathParts.length - 1] === "") {
6019
6138
  pathParts.pop();
6020
6139
  }
6021
- if (pathParts.includes("..") || filename.startsWith("/") || filename.startsWith("\\\\") || DRIVE_LETTER_REGEXP.test(filename)) {
6140
+ if (PARENT_DIRECTORY_REGEXP.test(filename) || filename.startsWith("/") || filename.startsWith("\\") || DRIVE_LETTER_REGEXP.test(filename)) {
6022
6141
  return true;
6023
6142
  }
6024
- return filenameValidation == STRICTNESS_STRICT && (pathParts.includes(".") || pathParts.includes(""));
6143
+ return filenameValidation == STRICTNESS_STRICT && (pathParts.includes(".") || pathParts.includes("") || filename.includes("\0"));
6025
6144
  }
6026
6145
 
6027
6146
  function getMaxAppendedDataSize(maxAppendedDataSize, strictness) {
@@ -6107,7 +6226,7 @@ async function* scanEndOfCentralDirectory(reader, scanLength) {
6107
6226
  }
6108
6227
 
6109
6228
  function getEndOfCentralDirectoryInfo(scanArray, indexByte, offset) {
6110
- return { offset, buffer: scanArray.slice(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH).buffer };
6229
+ return { offset, buffer: new Uint8Array(scanArray.subarray(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH)).buffer };
6111
6230
  }
6112
6231
 
6113
6232
  async function getCentralDirectoryReachability(reader, view, anchoredOffset, indexByte, offset, size, remoteProbeBudget) {
@@ -8852,7 +8971,7 @@ function formatSupported(StreamClass, format) {
8852
8971
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8853
8972
  */
8854
8973
 
8855
- const VERSION = "2.14.1";
8974
+ const VERSION = "2.16.0";
8856
8975
 
8857
8976
  /*
8858
8977
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.