npm @zip.js/zip.js 2.17.0

latest releases: 2.24.0, 2.23.0, 2.22.1...
one month ago

What's Changed in v2.17.0

New features

  • ERR_CODEC_OUT_OF_MEMORY is a new exported constant, thrown when the codec of an entry cannot allocate the memory it needs, e.g. when the 16 MB heap of the bundled WebAssembly module is exhausted by too many entries processed concurrently in the same worker, or on the page when workers are off, with the error of the codec as the cause. The failure is recognized by the code property of that error, "Z_MEM_ERROR", which the bundled zlib-streams 1.4.0 sets on its allocation failures and which the native DecompressionStream of Node.js would set. It is raised when reading an entry, both when the codec cannot allocate its state and when it fails while inflating, and when writing an entry whose codec cannot allocate its state; a failure while compressing keeps the error of the codec. Such a failure used to surface as the allocation failed error of the codec, so code matching that message should catch the constant instead

Bug fixes

  • A zip file whose compressedSize overshoots the deflate stream of an entry, which the bundled codecs used to read by dropping the extra bytes, now fails on every codec, as it already did with a native DecompressionStream. The error is ERR_INVALID_COMPRESSED_DATA, with the error of the codec as the cause; on Node.js with checkCrc32 set it is ERR_INVALID_CRC32, since its native inflater reports the extra bytes only once the gzip trailer built by zip.js has been written. The bundled codecs are zlib-streams 1.4.0, the WebAssembly one, and zlib-streams-ts 1.2.0, the pure-JS one of the *-native builds
  • A corrupted deflated entry rejects with ERR_INVALID_COMPRESSED_DATA whatever inflates it, with the error the codec raised as the cause: the TypeError of a native DecompressionStream, whose message depends on the engine, or the error of the bundled WebAssembly or pure-JS codec. Only the message-less error of Node.js used to be mapped, so the same corrupted zip file raised Invalid compressed data on Node.js, corrupt deflate stream on Deno, process error:-3 wherever the WebAssembly codec inflates, e.g. in a browser with useCompressionStream off, and inflate failed on Bun, and a caller comparing against the constant was right on Node.js only. An error raised while the data is being read, by the reader of the zip file or by the decryption of the entry, is not a codec failure and reaches the caller unchanged, with its own cause. On Safari and Bun, whose structured clone drops the cause of an error posted by a worker, the cause is rebuilt from its name and message
  • With checkCrc32 set, a stored uncompressed size larger than the data now fails with ERR_INVALID_CRC32 instead of ERR_INVALID_UNCOMPRESSED_SIZE, on every codec, with the error of the inflater as the cause: the inflater verifies the checksum through a gzip trailer built from the stored CRC-32 and size, and rejects that trailer as a whole. With checkCrc32 off it is still ERR_INVALID_UNCOMPRESSED_SIZE, and a size smaller than the data is ERR_INVALID_UNCOMPRESSED_SIZE either way, as soon as the output exceeds it
  • A signal aborted while an entry is being read by getData() or added by add() rejects the operation with the reason of the signal, whether the compressed data is still being consumed or the content still being written. The signal used to guard the pipe feeding the codec alone, so an abort landing once the input had been consumed, e.g. while a large content was still being written to the writer, was ignored and the operation completed. On the oldest supported engines, which ignore the signal option of pipeTo(), the data is written to the end before the operation is rejected
  • With the passwords and requestPassword options of the filesystem API, the ERR_INVALID_PASSWORD error raised when every candidate has failed, or when requestPassword gives up, carries the error raised by the last candidate as its cause. A ZipCrypto entry whose read fails for a reason other than a false accept, i.e. a wrong password slipping past the one-byte header check of ZipCrypto, as one in 256 does, reports that failure instead of trying the next candidate: only ERR_INVALID_CRC32, ERR_INVALID_COMPRESSED_DATA and ERR_INVALID_UNCOMPRESSED_SIZE count as a wrong password, since such a password produces content that the CRC-32 check or the inflater rejects, while a failure of the reader, e.g. a network error, or an ERR_CODEC_OUT_OF_MEMORY error reaches the caller as-is. Any failure of the read used to count as a wrong password, so a reader failure surfaced as ERR_INVALID_PASSWORD once the candidates were exhausted. A corrupted entry read with the right password is still reported as a wrong password, since nothing tells it from a wrong password passing the check
  • On Chrome before 103 or Node.js before 20.12, whose native inflater lacks "deflate-raw", when the WebAssembly codec cannot take over because its module failed to load, e.g. an *-external build deployed without zip-module.wasm next to it, an entry encrypted with AES that stores no CRC-32 (AE-2, what the writer emits for every encrypted entry) is inflated through a gzip container whose trailer carries the CRC-32 of the output received so far. A valid entry failed with ERR_INVALID_CRC32 now and then, more often on a loaded machine and on Node.js, because the trailer was written on a timing guess. It is now written once the inflater has returned everything, and the entry fails with ERR_INVALID_UNCOMPRESSED_SIZE when it inflates to fewer bytes as well as to more
  • The same wrapper serves the checkCrc32 route on every host, where the rewrite costs about 3 µs per entry with the native codec, measured with the benchmark harness; the tables of BENCHMARKS.md are unchanged
  • The WebAssembly codec (zlib-streams 1.4.0) and the pure-JS codec of the *-native builds (zlib-streams-ts 1.2.0) reject an unknown format with a TypeError when the stream is constructed, as the platform CompressionStream and DecompressionStream do; they used to treat it as "deflate". Nothing changes for a zip file; code that tests a format by constructing the stream now gets the right answer

Documentation

  • ERR_INVALID_COMPRESSED_DATA, ERR_INVALID_CRC32 and ERR_INVALID_UNCOMPRESSED_SIZE document when they are raised, the cause they carry and the routes on which one is raised in place of another, the signal option of the reader and of the writer documents the abort landing during the output, and PasswordCandidatesOptions documents the errors counted as a wrong password and the cause of the final ERR_INVALID_PASSWORD

Tests and continuous integration

  • New tests cover the corrupted deflated entry on every inflate route, with the native and the WebAssembly codecs, with and without workers and with and without checkCrc32, the bytes trailing a deflate stream, the memory failure of the codec on both sides with the heap of the WebAssembly module actually exhausted, the gzip route with a large AE-2 entry read several times, which is what caught the race, the abort landing during the output of a read and of a write, and the reader failure and the cause under the password candidates
  • The password candidates fixture is generated until none of the wrong candidates passes the one-byte check of ZipCrypto by chance, which one in 256 did and failed a job on Chrome 87 once
  • The README of the tests names both compression globals that the polyfill runner removes

Full Changelog: v2.16.1...v2.17.0

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

Don't miss a new zip.js release

NewReleases is sending notifications on new releases.