Skip to content

v2.8.52

Latest

Choose a tag to compare

@gildas-lormeau gildas-lormeau released this 18 Aug 01:58
· 1 commit to master since this release

What's Changed in v2.8.52

New features

  • New getExportedSize() method on ZipDirectoryEntry and FS instances. It returns the exact size of the zip file the matching export*() call would write, without writing it. It takes the same options as the export*() methods, so the value it returns is the one the export produces. It is meant for the Content-Length header of a streamed download. It throws the new ERR_UNDETERMINED_SIZE error when the size cannot be known before writing, i.e. when an entry is compressed, when an entry has no known size, when signCentralDirectory is set, and when the bufferedWrite option lets the physical layout depend on the order in which the entries are written. Encryption does not prevent the prediction, its overhead is a fixed number of bytes
  • New onentryprogress option in the export*() methods of the filesystem API. It is called once per written entry with the number of entries written, the total number of entries, and the entry itself. It reports the entries whereas onprogress reports the bytes. It is called after the entry has been written. When bufferedWrite is enabled the entries are written concurrently, so it counts the entries written instead of giving the position of the entry in the zip file
  • New globalComment option in the export*() methods of the filesystem API. It sets the comment of the zip file. The options of these methods are applied to every entry, so setting comment there comments each entry instead of the archive, exactly as lastModDate there sets the date of each entry. The zip file comment therefore needed a name of its own
  • importZip() now accepts a ZipReader instance in addition to the data of a zip file. The caller builds the reader, passes it, and keeps it after the import. This is the way to read prependedData, appendedData, comment, digitalSignature, directoryOffset and directoryLength, which are only filled once the entries have been read. The options of the reader are merged with the options of the import, and the options of the import win
  • New symlink property on entries. It is true when the entry is a symbolic link. The target of the link is the content of the entry, which is read like any other entry, e.g. with getData(new TextWriter()). The target is not validated, it can be an absolute path or escape the directory of the entry, so it must be checked before being used. Writing a symbolic link is done by setting the unixMode option to a mode carrying the S_IFLNK type, e.g. 0o120777, with the path of the target as content
  • exportFileSystemHandle() now accepts the readerOptions option. The password option must be set there to export the entries of an encrypted zip file, since the password option of the export encrypts the written entries instead
  • The readerOptions option of the filesystem export now accepts passThrough. The entries imported from a zip file are then written as-is, without being decompressed and decrypted, exactly as importing them with this option does. The entries added to the filesystem are compressed as usual
  • The signCentralDirectory option is now declared in the export options of the filesystem API. It was already forwarded to the ZipWriter instance, it was simply missing from the TypeScript definitions
  • New ERR_UNDETERMINED_SIZE, ERR_INVALID_PASS_THROUGH and ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH error constants

Behavior changes

  • The export*() methods of the filesystem API now report the progress of the whole archive instead of the progress of each entry. onstart and onend are called once, with the total size of the entries and with the number of bytes written. They used to be called once per entry, while onprogress was already reporting the archive as a whole, so the three callbacks disagreed with each other. onprogress is unchanged. Use the new onentryprogress option to be notified for each entry
  • The export options of the filesystem API now take precedence over the metadata of the entries imported from a zip file. Setting lastModDate, comment, versionMadeBy, uid, gid or the file attributes in the export options used to have no effect on those entries, although unixMode and msdosAttributes did reach them, so the same attribute word was writable one way and not the other. The order is now the metadata of the source entry, then the export options, then the description of the entries copied as-is, then the options of the entry. The description of the entries copied as-is stays above the export options because it describes the bytes being copied, not an intent. A level or a compressionMethod winning over it would write headers that do not match the content. An export using the default options is unchanged, so round trips keep their fidelity
  • ZipReader#close() now cancels the ReadableStream instance passed to the constructor when nothing has been read from it. It used to do nothing at all. The stream of a reader whose entries have been read is left alone, and so is a Reader instance, which belongs to the caller. The entries stay readable after the call
  • The preventClose option is now honored only when the caller owns the writable, i.e. when a WritableWriter instance is passed to exportZip() or exportWritable(). It is ignored by the other export*() methods of the filesystem API, whose Writer instance can only return its data once its writable is closed. Setting it there used to prevent the export from ever resolving
  • Writing an entry with the passThrough option and a password now throws ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH. The data is copied as-is, so it cannot be encrypted. The password used to be ignored silently and the entry was written unencrypted although its header announced encryption
  • The passThrough option is now ignored for the entries with no content. add("dir/", undefined, { directory: true, passThrough: true }) used to throw ERR_UNDEFINED_READER, so a ZipWriter instance created with passThrough set to true could not write a single directory
  • Writing an entry with the unixMode option now stamps the type of the file in the external file attributes. zip.js used to write a mode with no type, e.g. 0o000644 where Info-ZIP writes 0o100644, so unzip -l listed the entry as ?rw-r--r--. A mode already carrying a type is left untouched, and the externalFileAttributes option still writes the value verbatim
  • The executable property of an entry is now false for symbolic links. The permission bits of a link are always 0o777, so the flag was meaningless on every link

Bug fixes

  • Reading a range of data no longer emits empty chunks. Reader#createReadable() enqueued an empty chunk at the end of every entry whose size is known, and one extra read was issued for an entry of unknown size. The data was correct, the stream simply contained a chunk of length 0
  • getBlob() and getData64URI() now honor the MIME type they are given. A filesystem entry holding a Blob instance returned it as-is, with the MIME type it was created with, and ignored the requested one
  • The filesystem API now runs the overlapping entry check when checkOverlappingEntryOnly is set in its reader options. The option means "run the check and stop before reading the content" in the core API. The filesystem API needs the content, so it used to drop the option and skip the check altogether. It is now mapped to checkOverlappingEntry, so the check runs and the content is still read
  • The reader options that withhold the content of an entry are now ignored by the filesystem API. checkPasswordOnly, checkOverlappingEntryOnly and preventClose used to be forwarded to the entries read from a zip file, which returned no data
  • Progress is now reported for the filesystem entries that were not imported from a zip file. Reading such an entry pipes the reader to the writer without going through a codec, so onprogress never fired. It is now reported for every entry
  • exportFileSystemHandle() now counts the bytes it writes. It used to report the compressed size of each entry against the uncompressed size of the archive, so the progress stopped around 1% of the total, and it reported nothing at all for the entries that were not imported from a zip file
  • addFileSystemHandle() now applies the entry options to the directories it creates. They were applied to the files only
  • The dates of the Info-ZIP unix extra field are now read as signed timestamps. A date before 1970 was read as a date in 2106
  • Writing an entry whose name ends with a slash no longer corrupts the unixMode option. The type of the file was combined with the type already present in the mode instead of replacing it, so 0o100644 became a socket and 0o120777 became an invalid type. Both were then read back as neither a directory nor a symbolic link
  • The deprecated externalFileAttribute and internalFileAttribute options work again. They were removed when they were renamed to externalFileAttributes and internalFileAttributes. They are back as deprecated aliases, and an option passed to add() now takes precedence over the option of the ZipWriter instance whatever the spelling of each
  • addText() now stores the size of the text in bytes instead of its number of UTF-16 code units. Only getExportedSize() read that value, so the size was under-reported for any text containing non-ASCII characters. The export itself was correct
  • The buffer reserved for the expansion of the deflate algorithm is now allocated only for the entries that are really compressed. A stored entry reserved the same margin as a deflated one

Performance

  • The export*() methods of the filesystem API now keep a running total instead of summing the progress of every entry on every tick. The cost was quadratic in the number of entries. Passing onprogress on an archive of 10,000 entries added 50% to the duration of the export, and now adds nothing measurable

Documentation

  • The useCompressionStream option now states that the native API is used for compression only when level is undefined or equal to 6. CompressionStream does not support compression levels, so any other value compresses the data with the embedded implementation. It also states that the data produced at a given level can vary between platforms, and that useCompressionStream must be set to false to get the same output everywhere
  • The uid and gid options now state which unix extra field carries them
  • ZipReader#close() and the createTempStream option now describe what they do. A temporary stream must be able to hold a whole entry, because the local header written before it holds the size and the CRC-32 of the entry. Its readable side is therefore consumed only once its writable side has been closed. A factory returning new TransformStream() deadlocks, whereas the default buffers everything

Tests and continuous integration

  • The browser test runner uses Selenium instead of Playwright. It accepts --exe-path, --url-search, --build, --headful and --help, and the test-webkit script was renamed to test-safari
  • A workflow runs the test suite on every push, including the native build
  • A workflow checks that the markdown documentation is up to date with index.d.ts
  • The API documentation is published to the GitHub Pages site on each release
  • The web runner accepts a maxParallelTests parameter. Chromium 87 loses the wake-up of the backpressure of a stream when 16 tests run in parallel, so its jobs cap the parallelism at 4
  • The tests that check the abort reason are skipped on the browsers that ignore the signal of pipeTo() instead of being reported as failures

Credits

  • @danny0838 contributed the switch to Selenium, the test workflow and the arguments of the browser runner (#673)
  • Claude (Opus 5) contributed to every change listed above

Full Changelog: v2.8.51...v2.8.52