@zip.js/zip.js 2.15.0 → 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.
@@ -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,7 +1899,7 @@ 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 GzipCompressionStream = getGzipCompressionStream(useCompressionStream, CompressionStream, CompressionStreamFallback);
1902
+ const GzipCompressionStream = getGzipCodecStream(useCompressionStream, CompressionStream, CompressionStreamFallback);
1904
1903
  const useGzipCrc32 = computeCrc32 && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) && Boolean(GzipCompressionStream);
1905
1904
  if ((!encrypted || zipCrypto) && computeCrc32 && !useGzipCrc32) {
1906
1905
  crc32Stream = new Crc32Stream();
@@ -1917,6 +1916,9 @@ class DeflateStream extends TransformStream {
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);
@@ -1986,74 +1988,137 @@ class GzipToRawDeflateStream extends TransformStream {
1986
1988
  }
1987
1989
 
1988
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;
1989
1994
  let outputLength = 0;
1990
1995
  let inputDone = false;
1991
- let watchdogTimeout;
1996
+ let trailerWritten = false;
1997
+ let idleCheckArmed = false;
1998
+ let readCount = 0;
1999
+ let readPending = false;
1992
2000
  let resolveTrailerReady, rejectTrailerReady;
1993
2001
  const trailerReady = new Promise((resolve, reject) => {
1994
2002
  resolveTrailerReady = resolve;
1995
2003
  rejectTrailerReady = reject;
1996
2004
  });
1997
2005
  trailerReady.catch(() => { });
1998
- if (!outputSize) {
1999
- resolveTrailerReady();
2000
- }
2001
- const gzipWrapStream = new TransformStream({
2002
- 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 {
2003
2041
  const header = new Uint8Array(GZIP_HEADER_LENGTH);
2004
2042
  header.set(GZIP_HEADER_BYTES);
2005
- controller.enqueue(header);
2006
- },
2007
- transform(chunk, controller) {
2008
- controller.enqueue(chunk);
2009
- },
2010
- 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
+ }
2011
2052
  inputDone = true;
2012
- startWatchdog();
2013
- try {
2053
+ if (outputCrc32) {
2054
+ if (readPending) {
2055
+ armIdleCheck();
2056
+ }
2014
2057
  await trailerReady;
2015
- } finally {
2016
- stopWatchdog();
2017
2058
  }
2018
2059
  const trailer = new Uint8Array(GZIP_TRAILER_LENGTH);
2019
2060
  const dataView = getDataView(trailer);
2020
- dataView.setUint32(0, crc32.get(), true);
2061
+ dataView.setUint32(0, outputCrc32 ? outputCrc32.get() : crc32, true);
2021
2062
  dataView.setUint32(4, outputSize, true);
2022
- controller.enqueue(trailer);
2023
- },
2024
- cancel(reason) {
2025
- 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);
2026
2069
  }
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);
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();
2041
2079
  }
2042
- });
2043
- readable = pipeThrough(readable, gzipWrapStream);
2044
- readable = pipeThroughBackpressured(readable, gzipStream);
2045
- return pipeThrough(readable, outputStream);
2080
+ return result;
2081
+ }
2082
+
2083
+ function onReadSettled() {
2084
+ readPending = false;
2085
+ }
2046
2086
 
2047
- function startWatchdog() {
2048
- stopWatchdog();
2049
- watchdogTimeout = setTimeout(() => rejectTrailerReady(new Error(ERR_INVALID_UNCOMPRESSED_SIZE)), GZIP_OUTPUT_STALL_TIMEOUT);
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
+ }
2050
2101
  }
2051
2102
 
2052
- function stopWatchdog() {
2053
- clearTimeout(watchdogTimeout);
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;
2054
2107
  }
2055
2108
  }
2056
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
+
2057
2122
  class InflateStream extends TransformStream {
2058
2123
 
2059
2124
  constructor(options, { chunkSize, DecompressionStreamFallback, DecompressionStream }) {
@@ -2074,20 +2139,32 @@ class InflateStream extends TransformStream {
2074
2139
  if (codecStreams) {
2075
2140
  readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.DecompressionStream, format, { chunkSize, compressionMethod, rawBitFlag, uncompressedSize: outputSize }));
2076
2141
  } 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;
2142
+ let gzipStream;
2143
+ const GzipDecompressionStream = getGzipCodecStream(useCompressionStream, DecompressionStream, DecompressionStreamFallback);
2144
+ if (checkCrc32 && !deflate64 && crc32 !== UNDEFINED_VALUE && outputSize !== UNDEFINED_VALUE && GzipDecompressionStream) {
2084
2145
  try {
2085
- gzipStream = new DecompressionStream(FORMAT_GZIP$1);
2146
+ gzipStream = new GzipDecompressionStream(FORMAT_GZIP$1, { chunkSize });
2086
2147
  } catch {
2087
- 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
+ }
2088
2163
  }
2089
- gzipCrc32 = new Crc32();
2090
- readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, gzipCrc32);
2164
+ }
2165
+ if (gzipStream) {
2166
+ gzipCrc32 = true;
2167
+ readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, crc32);
2091
2168
  }
2092
2169
  }
2093
2170
  readable = mapInflateStreamError(readable);
@@ -2097,8 +2174,8 @@ class InflateStream extends TransformStream {
2097
2174
  readable = pipeThrough(readable, crc32Stream);
2098
2175
  }
2099
2176
  setReadable(this, readable, () => {
2100
- if (checkCrc32) {
2101
- const computedCrc32 = gzipCrc32 ? gzipCrc32.get() >>> 0 : new DataView(crc32Stream.value.buffer).getUint32(0, false);
2177
+ if (crc32Stream) {
2178
+ const computedCrc32 = new DataView(crc32Stream.value.buffer).getUint32(0, false);
2102
2179
  if (crc32 != computedCrc32) {
2103
2180
  throw new Error(ERR_INVALID_CRC32);
2104
2181
  }
@@ -2155,11 +2232,11 @@ function createCodecStream(CodecStreamClass, format, options) {
2155
2232
  return new CodecStreamClass(format, options);
2156
2233
  }
2157
2234
 
2158
- function getGzipCompressionStream(useCompressionStream, CompressionStreamNative, CompressionStreamFallback) {
2159
- if (useCompressionStream && CompressionStreamNative) {
2160
- return CompressionStreamNative;
2161
- } else if (CompressionStreamFallback && CompressionStreamFallback.requiresModule) {
2162
- return CompressionStreamFallback;
2235
+ function getGzipCodecStream(useCompressionStream, CodecStreamNative, CodecStreamFallback) {
2236
+ if (useCompressionStream && CodecStreamNative) {
2237
+ return CodecStreamNative;
2238
+ } else if (CodecStreamFallback && CodecStreamFallback.requiresModule) {
2239
+ return CodecStreamFallback;
2163
2240
  }
2164
2241
  }
2165
2242
 
@@ -3761,10 +3838,11 @@ class FetchReader extends Reader {
3761
3838
 
3762
3839
  createReadable(options) {
3763
3840
  const reader = this;
3764
- const { useRangeHeader, forceRangeRequests, size } = reader;
3841
+ const { useRangeHeader, forceRangeRequests, size, eocdCache } = reader;
3765
3842
  if ((useRangeHeader || forceRangeRequests) && size !== UNDEFINED_VALUE) {
3766
3843
  const { offset = 0, size: readSize = size - offset } = options || {};
3767
- if (readSize > 0 && offset < size) {
3844
+ const cached = eocdCache && offset >= size - eocdCache.length;
3845
+ if (readSize > 0 && offset < size && !cached) {
3768
3846
  return createRangeReadable(reader, offset, Math.min(readSize, size - offset));
3769
3847
  }
3770
3848
  }
@@ -8893,7 +8971,7 @@ function formatSupported(StreamClass, format) {
8893
8971
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8894
8972
  */
8895
8973
 
8896
- const VERSION = "2.15.0";
8974
+ const VERSION = "2.16.0";
8897
8975
 
8898
8976
  /*
8899
8977
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.