@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.
package/dist/zip-core.js CHANGED
@@ -1858,7 +1858,6 @@
1858
1858
  const GZIP_HEADER_LENGTH = 10;
1859
1859
  const GZIP_TRAILER_LENGTH = 8;
1860
1860
  const GZIP_HEADER_BYTES = [0x1f, 0x8b, 0x08];
1861
- const GZIP_OUTPUT_STALL_TIMEOUT = 5000;
1862
1861
 
1863
1862
  class DeflateStream extends TransformStream {
1864
1863
 
@@ -1869,7 +1868,7 @@
1869
1868
  let crc32Stream, encryptionStream, gzipCrc32Stream;
1870
1869
  let readable = super.readable;
1871
1870
  const codecStreams = format && getCodecStreams(format);
1872
- const GzipCompressionStream = getGzipCompressionStream(useCompressionStream, CompressionStream, CompressionStreamFallback);
1871
+ const GzipCompressionStream = getGzipCodecStream(useCompressionStream, CompressionStream, CompressionStreamFallback);
1873
1872
  const useGzipCrc32 = computeCrc32 && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) && Boolean(GzipCompressionStream);
1874
1873
  if ((!encrypted || zipCrypto) && computeCrc32 && !useGzipCrc32) {
1875
1874
  crc32Stream = new Crc32Stream();
@@ -1886,6 +1885,9 @@
1886
1885
  try {
1887
1886
  readable = pipeThroughCompressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStream, CompressionStreamFallback);
1888
1887
  } catch (error) {
1888
+ if (!useCompressionStream && CompressionStreamFallback) {
1889
+ throw error;
1890
+ }
1889
1891
  let gzipStream;
1890
1892
  try {
1891
1893
  gzipStream = new CompressionStream(FORMAT_GZIP);
@@ -1955,74 +1957,137 @@
1955
1957
  }
1956
1958
 
1957
1959
  function pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, crc32) {
1960
+ const writer = gzipStream.writable.getWriter();
1961
+ const reader = gzipStream.readable.getReader();
1962
+ const outputCrc32 = crc32 === UNDEFINED_VALUE ? new Crc32() : UNDEFINED_VALUE;
1958
1963
  let outputLength = 0;
1959
1964
  let inputDone = false;
1960
- let watchdogTimeout;
1965
+ let trailerWritten = false;
1966
+ let idleCheckArmed = false;
1967
+ let readCount = 0;
1968
+ let readPending = false;
1961
1969
  let resolveTrailerReady, rejectTrailerReady;
1962
1970
  const trailerReady = new Promise((resolve, reject) => {
1963
1971
  resolveTrailerReady = resolve;
1964
1972
  rejectTrailerReady = reject;
1965
1973
  });
1966
1974
  trailerReady.catch(() => { });
1967
- if (!outputSize) {
1968
- resolveTrailerReady();
1969
- }
1970
- const gzipWrapStream = new TransformStream({
1971
- start(controller) {
1975
+ pump();
1976
+ return new ReadableStream({
1977
+ async pull(controller) {
1978
+ let result;
1979
+ try {
1980
+ result = await read();
1981
+ } catch (error) {
1982
+ throw trailerWritten ? getTrailerError(error) : error;
1983
+ }
1984
+ const { value, done } = result;
1985
+ if (done) {
1986
+ controller.close();
1987
+ } else {
1988
+ outputLength += value.length;
1989
+ if (outputLength > outputSize) {
1990
+ const error = new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
1991
+ rejectTrailerReady(error);
1992
+ await cancel(reader, error);
1993
+ throw error;
1994
+ }
1995
+ if (outputCrc32) {
1996
+ outputCrc32.append(value);
1997
+ }
1998
+ controller.enqueue(value);
1999
+ }
2000
+ },
2001
+ cancel(reason) {
2002
+ rejectTrailerReady(reason);
2003
+ return reader.cancel(reason);
2004
+ }
2005
+ });
2006
+
2007
+ async function pump() {
2008
+ const inputReader = readable.getReader();
2009
+ try {
1972
2010
  const header = new Uint8Array(GZIP_HEADER_LENGTH);
1973
2011
  header.set(GZIP_HEADER_BYTES);
1974
- controller.enqueue(header);
1975
- },
1976
- transform(chunk, controller) {
1977
- controller.enqueue(chunk);
1978
- },
1979
- async flush(controller) {
2012
+ await writer.write(header);
2013
+ for (; ;) {
2014
+ await writer.ready;
2015
+ const { value, done } = await inputReader.read();
2016
+ if (done) {
2017
+ break;
2018
+ }
2019
+ await writer.write(value);
2020
+ }
1980
2021
  inputDone = true;
1981
- startWatchdog();
1982
- try {
2022
+ if (outputCrc32) {
2023
+ if (readPending) {
2024
+ armIdleCheck();
2025
+ }
1983
2026
  await trailerReady;
1984
- } finally {
1985
- stopWatchdog();
1986
2027
  }
1987
2028
  const trailer = new Uint8Array(GZIP_TRAILER_LENGTH);
1988
2029
  const dataView = getDataView(trailer);
1989
- dataView.setUint32(0, crc32.get(), true);
2030
+ dataView.setUint32(0, outputCrc32 ? outputCrc32.get() : crc32, true);
1990
2031
  dataView.setUint32(4, outputSize, true);
1991
- controller.enqueue(trailer);
1992
- },
1993
- cancel(reason) {
1994
- rejectTrailerReady(reason);
2032
+ trailerWritten = true;
2033
+ await writer.write(trailer);
2034
+ await writer.close();
2035
+ } catch (error) {
2036
+ await abort(writer, error);
2037
+ await cancel(inputReader, error);
1995
2038
  }
1996
- });
1997
- const outputStream = new TransformStream({
1998
- transform(chunk, controller) {
1999
- crc32.append(chunk);
2000
- outputLength += chunk.length;
2001
- if (outputLength >= outputSize) {
2002
- resolveTrailerReady();
2003
- } else if (inputDone) {
2004
- startWatchdog();
2005
- }
2006
- controller.enqueue(chunk);
2007
- },
2008
- cancel(reason) {
2009
- rejectTrailerReady(reason);
2039
+ }
2040
+
2041
+ function read() {
2042
+ readCount++;
2043
+ readPending = true;
2044
+ const result = reader.read();
2045
+ result.then(onReadSettled, onReadSettled);
2046
+ if (inputDone && outputCrc32) {
2047
+ armIdleCheck();
2010
2048
  }
2011
- });
2012
- readable = pipeThrough(readable, gzipWrapStream);
2013
- readable = pipeThroughBackpressured(readable, gzipStream);
2014
- return pipeThrough(readable, outputStream);
2049
+ return result;
2050
+ }
2051
+
2052
+ function onReadSettled() {
2053
+ readPending = false;
2054
+ }
2015
2055
 
2016
- function startWatchdog() {
2017
- stopWatchdog();
2018
- watchdogTimeout = setTimeout(() => rejectTrailerReady(new Error(ERR_INVALID_UNCOMPRESSED_SIZE)), GZIP_OUTPUT_STALL_TIMEOUT);
2056
+ async function armIdleCheck() {
2057
+ if (!idleCheckArmed) {
2058
+ idleCheckArmed = true;
2059
+ const count = readCount;
2060
+ await nextTask();
2061
+ idleCheckArmed = false;
2062
+ if (readPending) {
2063
+ if (readCount == count) {
2064
+ resolveTrailerReady();
2065
+ } else {
2066
+ armIdleCheck();
2067
+ }
2068
+ }
2069
+ }
2019
2070
  }
2020
2071
 
2021
- function stopWatchdog() {
2022
- clearTimeout(watchdogTimeout);
2072
+ function getTrailerError(error) {
2073
+ const trailerError = new Error(outputLength == outputSize ? ERR_INVALID_CRC32 : ERR_INVALID_UNCOMPRESSED_SIZE);
2074
+ trailerError.cause = error;
2075
+ return trailerError;
2023
2076
  }
2024
2077
  }
2025
2078
 
2079
+ function nextTask() {
2080
+ return new Promise(resolve => {
2081
+ const { port1, port2 } = new MessageChannel();
2082
+ port2.onmessage = () => {
2083
+ port1.close();
2084
+ port2.close();
2085
+ resolve();
2086
+ };
2087
+ port1.postMessage(UNDEFINED_VALUE);
2088
+ });
2089
+ }
2090
+
2026
2091
  class InflateStream extends TransformStream {
2027
2092
 
2028
2093
  constructor(options, { chunkSize, DecompressionStreamFallback, DecompressionStream }) {
@@ -2043,20 +2108,32 @@
2043
2108
  if (codecStreams) {
2044
2109
  readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.DecompressionStream, format, { chunkSize, compressionMethod, rawBitFlag, uncompressedSize: outputSize }));
2045
2110
  } else {
2046
- try {
2047
- readable = pipeThroughCompressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamFallback);
2048
- } catch (error) {
2049
- if (deflate64 || outputSize === UNDEFINED_VALUE) {
2050
- throw error;
2051
- }
2052
- let gzipStream;
2111
+ let gzipStream;
2112
+ const GzipDecompressionStream = getGzipCodecStream(useCompressionStream, DecompressionStream, DecompressionStreamFallback);
2113
+ if (checkCrc32 && !deflate64 && crc32 !== UNDEFINED_VALUE && outputSize !== UNDEFINED_VALUE && GzipDecompressionStream) {
2053
2114
  try {
2054
- gzipStream = new DecompressionStream(FORMAT_GZIP);
2115
+ gzipStream = new GzipDecompressionStream(FORMAT_GZIP, { chunkSize });
2055
2116
  } catch {
2056
- throw error;
2117
+ gzipStream = UNDEFINED_VALUE;
2118
+ }
2119
+ }
2120
+ if (!gzipStream) {
2121
+ try {
2122
+ readable = pipeThroughCompressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamFallback);
2123
+ } catch (error) {
2124
+ if (deflate64 || outputSize === UNDEFINED_VALUE || (!useCompressionStream && DecompressionStreamFallback)) {
2125
+ throw error;
2126
+ }
2127
+ try {
2128
+ gzipStream = new DecompressionStream(FORMAT_GZIP);
2129
+ } catch {
2130
+ throw error;
2131
+ }
2057
2132
  }
2058
- gzipCrc32 = new Crc32();
2059
- readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, gzipCrc32);
2133
+ }
2134
+ if (gzipStream) {
2135
+ gzipCrc32 = true;
2136
+ readable = pipeThroughGzipDecompressionStream(readable, gzipStream, outputSize, crc32);
2060
2137
  }
2061
2138
  }
2062
2139
  readable = mapInflateStreamError(readable);
@@ -2066,8 +2143,8 @@
2066
2143
  readable = pipeThrough(readable, crc32Stream);
2067
2144
  }
2068
2145
  setReadable(this, readable, () => {
2069
- if (checkCrc32) {
2070
- const computedCrc32 = gzipCrc32 ? gzipCrc32.get() >>> 0 : new DataView(crc32Stream.value.buffer).getUint32(0, false);
2146
+ if (crc32Stream) {
2147
+ const computedCrc32 = new DataView(crc32Stream.value.buffer).getUint32(0, false);
2071
2148
  if (crc32 != computedCrc32) {
2072
2149
  throw new Error(ERR_INVALID_CRC32);
2073
2150
  }
@@ -2124,11 +2201,11 @@
2124
2201
  return new CodecStreamClass(format, options);
2125
2202
  }
2126
2203
 
2127
- function getGzipCompressionStream(useCompressionStream, CompressionStreamNative, CompressionStreamFallback) {
2128
- if (useCompressionStream && CompressionStreamNative) {
2129
- return CompressionStreamNative;
2130
- } else if (CompressionStreamFallback && CompressionStreamFallback.requiresModule) {
2131
- return CompressionStreamFallback;
2204
+ function getGzipCodecStream(useCompressionStream, CodecStreamNative, CodecStreamFallback) {
2205
+ if (useCompressionStream && CodecStreamNative) {
2206
+ return CodecStreamNative;
2207
+ } else if (CodecStreamFallback && CodecStreamFallback.requiresModule) {
2208
+ return CodecStreamFallback;
2132
2209
  }
2133
2210
  }
2134
2211
 
@@ -3706,10 +3783,11 @@
3706
3783
 
3707
3784
  createReadable(options) {
3708
3785
  const reader = this;
3709
- const { useRangeHeader, forceRangeRequests, size } = reader;
3786
+ const { useRangeHeader, forceRangeRequests, size, eocdCache } = reader;
3710
3787
  if ((useRangeHeader || forceRangeRequests) && size !== UNDEFINED_VALUE) {
3711
3788
  const { offset = 0, size: readSize = size - offset } = options || {};
3712
- if (readSize > 0 && offset < size) {
3789
+ const cached = eocdCache && offset >= size - eocdCache.length;
3790
+ if (readSize > 0 && offset < size && !cached) {
3713
3791
  return createRangeReadable(reader, offset, Math.min(readSize, size - offset));
3714
3792
  }
3715
3793
  }
@@ -8740,7 +8818,7 @@
8740
8818
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8741
8819
  */
8742
8820
 
8743
- const VERSION = "2.15.0";
8821
+ const VERSION = "2.16.0";
8744
8822
 
8745
8823
  /*
8746
8824
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.