Releases: gildas-lormeau/zip.js
Releases · gildas-lormeau/zip.js
Release list
v2.8.53
What's Changed in v2.8.53
New features
- New
checkLocalDirectoryoption in the reader options. It compares the local file header of an entry against its central directory record whenFileEntry#getData()is called, and throwsERR_AMBIGUOUS_ARCHIVEwhen the two disagree.truecompares the filename, the general purpose bit flag, the compression method, the CRC-32 checksum and the sizes, likestrictnessset to"strict";falsecompares nothing, like"tolerant". Setting it explicitly always wins overstrictness, whetherstrictnesswas passed to the constructor ofZipReaderor to the call, so it is the way to ask for this one check without the archive-level checks ofcheckAmbiguity, and the way to drop it without giving up the other checksstrictnessperforms. It is also the only way to validate the local file headers of a self-extracting archive, sincecheckAmbiguityrejects prepended data outright - The local file header of an entry now reports the two records the reader had already read and dropped.
LocalDirectory#rawFilenameholds the filename stored in the local file header, which is allowed to differ fromEntryMetaData#rawFilename, and is defined whenstrictnessis"strict"orcheckLocalDirectoryistrue.LocalDirectory#dataDescriptorholds the data descriptor record written after the content, described by the newLocalDataDescriptorinterface, and is defined whencheckOverlappingEntryorcheckOverlappingEntryOnlyis set. It carries the CRC-32 checksum and the sizes stored in the record, each of which is allowed to differ from the central directory, and asignatureflag telling whether the record is preceded by its optional signature. That signature is not part of the original format, it is a later convention writers are free to follow. When the four bytes look like the signature but the values behind them disagree with the central directory, the flag isfalseand the record is read as starting at those four bytes instead - The parsed extra field records are now typed instead of being declared as the bare
EntryExtraField.EntryExtraFieldZip64,EntryExtraFieldNTFS,EntryExtraFieldExtendedTimestampandEntryExtraFieldUnixdescribe the members the reader fills in,EntryExtraFieldUnicodegainsversion,filenameandcomment, andEntryExtraFieldAESgainscompressionMethod, the real compression method of the entry, next tooriginalCompressionMethod, which is the99a WinZip AES header is required to carry in its place SplitDataReadernow accepts an array ofReaderinstances, ofReadableReaderinstances or ofReadableStreaminstances. The last two were declared in the TypeScript definitions and worked nowhere: reading a split archive requires the size of every disk to map a global offset onto one of them, so an element that only provides a stream is now buffered when the reader is initialized. This applies wherever an array of readers is accepted, i.e. the constructor ofZipReader,ZipWriter#add(),ZipWriter#prependZip(),ZipDirectoryEntry#importZip()and thereaderproperty of aZipFileEntryinstanceZipWriter#prependZip()now accepts a reader that only provides aReadableStream. It reads the central directory of the archive it prepends before piping it, so passing a stream used to fail withTypeError: ReadableStream is already locked. The stream is buffered once, like the disks above- New
ERR_INVALID_COMMENT_TYPEerror constant
Behavior changes
- The local file header of an entry is now compared against its central directory record by default, except for the filename.
strictnessset to"balanced", the default, used to trust the central directory record entirely;getData()now throwsERR_AMBIGUOUS_ARCHIVEwhen the general purpose bit flag, the compression method, the CRC-32 checksum or the sizes disagree.getEntries()is unaffected, the local file header is only read when the data is. This costs nothing: every one of those fields is read from the local file header anyway to locate the entry data. Only the filename is left out, because comparing it reads the filename bytes as well, which costs one extra read per entry whenever the local file header carries no extra field, the common case."strict"still compares the filename too,"tolerant"still compares nothing, andcheckLocalDirectoryset tofalserestores the previous behavior. The new default was verified against 458,000 entries of real archives, where it rejects none of them - An explicit
checkAmbiguitynow wins over an inheritedstrictness.checkAmbiguityis the boolean form ofstrictness,truemeaning"strict", and the two used to be resolved without regard to where they came from, so acheckAmbiguitypassed togetEntries()or togetData()could not relax astrictnesspassed to the constructor ofZipReader. A value passed to the call now wins over a value passed to the constructor, andstrictnessstill wins overcheckAmbiguitywhen both are passed to the same one.checkAmbiguityset tofalsemeans "not strict" rather than "trust everything", so it downgrades an inherited"strict"to"balanced"and leaves an inherited"tolerant"alone; passstrictnessset to"tolerant"to compare nothing. Code that passesstrictnessand nevercheckAmbiguityresolves exactly as before - The platform byte of the "Version made by" field is now forced instead of being merged into the value given by the
versionMadeByoption. It is set to Unix (3) when the entry carries Unix metadata, i.e. whenuid,gid,unixModeorunixExtraFieldTypeis set, and to MS-DOS (0) whenmsdosAttributesormsdosAttributesRawis set. Only the lower byte of the given value survives in both cases. It used to be combined with the byte already present, so aversionMadeBycarrying another platform produced a value belonging to neither ZipWriter#close()now throws the newERR_INVALID_COMMENT_TYPEerror when the comment it is given is not aUint8Array. Passing a string, the natural mistake, used to fail deep inside the writer withTypeError: Cannot read properties of undefined (reading 'byteLength'), after the entries had been written.getExportedSize()performs the same check on theglobalCommentoption
Bug fixes
- The Unix user and group ids are now read from the local file header when the central directory has none. The Info-ZIP Unix type 2 extra field (0x7855) stores them in the local file header only and leaves a zero-length copy in the central directory, so
uidandgidwere undefined on every archive written by Info-ZIP. They are filled in when the data of the entry is read: they are still undefined aftergetEntries()and appear oncegetData()has run, since that is when the local file header is read, and they are also readable onEntryMetaData#localDirectory. A value read from the central directory is never overwritten by the local file header, since the type 2 field truncates the ids to 16 bits while the New Unix field (0x7875) does not - An empty Info-ZIP Unix type 2 extra field no longer hides the ids of the Info-ZIP New Unix extra field next to it. The reader looked at 0x7875 only when 0x7855 was absent, so an entry carrying both, which is what Info-ZIP writes, reported no ids at all although 0x7875 held them
EntryMetaData#rawLastAccessDateandEntryMetaData#rawCreationDateare now filled from the NTFS extra field. They were declared but never set: the rawFILETIMEvalues were stored on the extra field record only.EntryMetaData#rawLastModDateis unaffected, it remains the MS-DOS date and time stored in the header- The entries returned by
ZipReader#getEntries()now carryrawBitFlag,filenameLength,extraFieldLengthandunixExternalUpper. The four properties were declared onEntryMetaDataand read from the central directory, they were simply dropped when the entry object was built unixExternalUpperis now the upper half of theexternalFileAttributesthe entry was written with, on the entry returned byZipWriter#add(). It was computed before theunixModeoption and the Unix file type were folded in, so it reported the default0o644for every entry, whatever the mode: an entry written with0o120777disagreed both with its ownexternalFileAttributesand with what the reader reports for it- A worker that fails to load now falls back to the main scope instead of throwing a
TypeError. The codec pool builds the worker and its interface, then calls it back one turn later; when theerrorevent of the worker arrived in that interval, the error was dropped and the pool went on to post a message to a worker it had already discarded, which failed withCannot read properties of null (reading 'postMessage'). The designed fallback now runs in that case too, with the error of the worker as the reason. This affects the engines where a worker cannot be started at all, e.g. Firefox extensions using manifest v2 and Chromium 76 to 79 - The entry returned by
ZipWriter#add()now defines the same members as the entries returned byZipReader#getEntries():zip64,symlink,encrypted,zipCryptoandmsDosCompatiblewere left undefined instead offalseon one side or the other, and the deprecatedinternalFileAttributeandexternalFileAttributealiases were missing from it
Documentation
- The
strictnessoption now lists the fields each level compares, and states which of them are read from the local file header anyway - The
versionMadeBy,msDosCompatibleandunixModeoptions now describe how the platform byte and the Unix file type are chosen, including the fact that a folder entry is always written withS_IFDIRwhatever type the mode carries - The
symlinkproperty now points at the option that writes a symbolic link, since there is no option of that name: the file type goes in `unixMo...
v2.8.52
What's Changed in v2.8.52
New features
- New
getExportedSize()method onZipDirectoryEntryandFSinstances. It returns the exact size of the zip file the matchingexport*()call would write, without writing it. It takes the same options as theexport*()methods, so the value it returns is the one the export produces. It is meant for theContent-Lengthheader of a streamed download. It throws the newERR_UNDETERMINED_SIZEerror when the size cannot be known before writing, i.e. when an entry is compressed, when an entry has no known size, whensignCentralDirectoryis set, and when thebufferedWriteoption 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
onentryprogressoption in theexport*()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 whereasonprogressreports the bytes. It is called after the entry has been written. WhenbufferedWriteis 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
globalCommentoption in theexport*()methods of the filesystem API. It sets the comment of the zip file. The options of these methods are applied to every entry, so settingcommentthere comments each entry instead of the archive, exactly aslastModDatethere sets the date of each entry. The zip file comment therefore needed a name of its own importZip()now accepts aZipReaderinstance 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 readprependedData,appendedData,comment,digitalSignature,directoryOffsetanddirectoryLength, 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
symlinkproperty on entries. It istruewhen 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. withgetData(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 theunixModeoption to a mode carrying theS_IFLNKtype, e.g.0o120777, with the path of the target as content exportFileSystemHandle()now accepts thereaderOptionsoption. Thepasswordoption must be set there to export the entries of an encrypted zip file, since thepasswordoption of the export encrypts the written entries instead- The
readerOptionsoption of the filesystem export now acceptspassThrough. 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
signCentralDirectoryoption is now declared in the export options of the filesystem API. It was already forwarded to theZipWriterinstance, it was simply missing from the TypeScript definitions - New
ERR_UNDETERMINED_SIZE,ERR_INVALID_PASS_THROUGHandERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGHerror 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.onstartandonendare called once, with the total size of the entries and with the number of bytes written. They used to be called once per entry, whileonprogresswas already reporting the archive as a whole, so the three callbacks disagreed with each other.onprogressis unchanged. Use the newonentryprogressoption 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,gidor the file attributes in the export options used to have no effect on those entries, althoughunixModeandmsdosAttributesdid 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. Alevelor acompressionMethodwinning 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 theReadableStreaminstance 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 aReaderinstance, which belongs to the caller. The entries stay readable after the call- The
preventCloseoption is now honored only when the caller owns the writable, i.e. when aWritableWriterinstance is passed toexportZip()orexportWritable(). It is ignored by the otherexport*()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
passThroughoption and a password now throwsERR_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
passThroughoption is now ignored for the entries with no content.add("dir/", undefined, { directory: true, passThrough: true })used to throwERR_UNDEFINED_READER, so aZipWriterinstance created withpassThroughset totruecould not write a single directory - Writing an entry with the
unixModeoption now stamps the type of the file in the external file attributes. zip.js used to write a mode with no type, e.g.0o000644where Info-ZIP writes0o100644, sounzip -llisted the entry as?rw-r--r--. A mode already carrying a type is left untouched, and theexternalFileAttributesoption still writes the value verbatim - The
executableproperty of an entry is nowfalsefor symbolic links. The permission bits of a link are always0o777, 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()andgetData64URI()now honor the MIME type they are given. A filesystem entry holding aBlobinstance 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
checkOverlappingEntryOnlyis 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 tocheckOverlappingEntry, 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,checkOverlappingEntryOnlyandpreventCloseused 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
onprogressnever 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 fileaddFileSystemHandle()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
unixModeoption. The type of the file was combined with the type already present in the mode instead of replacing it, so0o100644became a socket and0o120777became an invalid type. Both were then read back as neither a directory nor a symbolic link - The deprecated
externalFileAttributeandinternalFileAttributeoptions work again. They were removed when they were renamed toexternalFileAttributesandinternalFileAttributes. They are back as deprecated aliases, and an option passed toadd()now takes precedence over the option of theZipWriterinstance whatever the spelling of each addText()now stores the size of the text in bytes instead of its number of UTF-16 code units. OnlygetExportedSize()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. Passingonprogresson an archive of 10,...
v2.8.51
What's Changed in v2.8.51
New features
getChildren()returns the children of a directory as an array, and all its descendants when therecursiveoption is set totrue. It is available onZipDirectoryEntryandFSinstances. The descendants are ordered level by level, like the result ofreaddir(path, { recursive: true })in Node.js, which is also the order in which the entries are written by theexport*()methods. Unlike theentriesproperty ofFS, the array excludes the root directory, leaves no empty slot for removed entries, and can start from any directory. It is a snapshot taken when the method is called, so the tree can be modified while the array is being iterated
Behavior changes
- The
export*()methods of the filesystem API now write the entries in the same order whatever the value of thebufferedWriteoption. Setting it tofalseused to write each branch of the tree entirely before moving to the next one, whereas the default writes the entries level by level, so an archive exported withbufferedWriteset tofalsedoes not have the same entry order as in the previous versions. Only the order changes: the entries, their content and their metadata are identical, and a directory entry still precedes the entries it contains
Compatibility improvements
exportFileSystemHandle()called withconcurrentset totruenow reports the failure that stopped the export on browsers which do not support thereasonargument ofAbortController#abort(), e.g. Firefox 79 and Chromium 87. zip.js used to recognize the cancellation of the sibling entries by the reason it had passed toabort(). These browsers discard that reason and report a plainAbortErrorinstead, so the cancellation was reported as the cause of the failure and the original error was demoted intoentryErrors. The cancellation is now tracked by zip.js itself and never read back from the platform (see #669)exportFileSystemHandle()now rejects with an error instead of rejecting withundefinedwhen the export is aborted through thesignaloption on these browsers. The reason passed toAbortController#abort()is discarded by the platform and cannot be recovered, so aDOMExceptionnamedAbortErroris thrown in its place. Its message is exposed as the newERR_ABORTEDconstant. Testingerror.name == "AbortError"now identifies an aborted export on every supported platform, whereas these browsers used to report a plainErrorwhen the export was aborted before it started and anAbortErrorwhen it was aborted while an entry was streaming
Documentation
- The API documentation of
exportFileSystemHandle()now states that an entry flagged as a symbolic link is written as a regular file whose content is the path of the link target, since the File System Access API cannot create symbolic links
Tests
- The test verifying that the abort reason of the caller is forwarded is now skipped on browsers without support for
AbortSignal#reasoninstead of being reported as a failure. It moved to its own file and covers aborting before the export as well as aborting while an entry is streaming
Credits
- @danny0838 reported the failure on Firefox 79 and Chromium 87 and ran the test suite on these browsers
Full Changelog: v2.8.50...v2.8.51
v2.8.50
What's Changed in v2.8.50
New features
- New
filenameValidationoption inZipReaderandgetEntries(). It rejects entry names that do not map safely to a file path. It accepts"strict","balanced"and"tolerant", and defaults to the value of thestrictnessoption. Rejected names throw the newERR_UNSAFE_FILENAMEerror, which carries the offending name in itsfilenameproperty - New
normalizeFilenameoption inZipReaderandgetEntries(). It is called with the decoded name of each entry and its result replaces that name. It runs after decoding and before validation, so repairing a name is enough to make it passfilenameValidation. Returningundefinedkeeps the decoded name. The filesystem API inherits the option fromZipReaderConstructorOptions - The
decodeTextandencodeTextoptions now receive the type of the text they handle,"filename"or"comment", as their last argument. Hooks declaring fewer parameters keep working exportFileSystemHandle()now reports what happened when an export fails. The newentryNameproperty ofEntryErrorholds the name of the entry that failed, relative to the exported entry. The newexportedEntryNamesproperty lists the files that were completely written before the failure. Every other file of the export is either missing or empty, so this is the only way to tell a file the export completed from one it created but never filledexportFileSystemHandle()called withconcurrentset totruenow collects every failure instead of reporting only the first one. The other failures are listed in the newentryErrorsproperty ofEntryError. Failures raised deeper in the tree are flattened into that list- New
ERR_INVALID_LEVEL,ERR_INVALID_PASSWORD_TYPE,ERR_INVALID_STRICTNESS,ERR_INVALID_FILENAME_VALIDATIONandERR_INVALID_MAX_APPENDED_DATA_SIZEerror constants
Breaking changes
- Entry names containing a
..path component, or starting with/, with a drive letter likeC:, or with\\, are now rejected when reading an archive. They throwERR_UNSAFE_FILENAME. SetfilenameValidationto"tolerant"to restore the previous behavior. This default was verified against a corpus of 572 archives holding 257,333 entries. None of them was rejected, so the new default costs nothing on real archives. A backslash is never treated as a path separator. It is legal on UNIX file systems, and it also occurs as the trail byte of double-byte filenames in Shift-JIS, Big5 and GBK, where converting it would corrupt the name - The
strictnessoption set to"strict"now also rejects empty and.path components, e.g.a//b.txtand./cur.txt - Invalid option values now throw instead of falling back silently to a default. A
leveloutside the integers 0 to 9 throwsERR_INVALID_LEVEL, and used to disable compression entirely when it was negative or not a number. Apasswordthat is not a string, or arawPasswordthat is not aUint8Array, throwsERR_INVALID_PASSWORD_TYPE. A value of another type used to produce an unencrypted archive, and arawPasswordpassed as a string used to produce an archive that its equivalentpasswordcannot open. An unknownstrictnessorfilenameValidationthrows instead of behaving as"balanced". An invalidmaxAppendedDataSizethrows instead of being accepted. A non-integerencryptionStrength,uid,gidorunixModeis now rejected by the guard whose message already announced it. Values meaning "no password", i.e.undefined,null, an empty string and an emptyUint8Array, keep working as before. Numeric options also keep accepting the strings that represent them, e.g.levelset to"9", because form controls, query strings and environment variables all yield strings - The filesystem API now ignores empty and
.path components when importing an archive. Names such asa//b.txtand./cur.txtno longer create entries with an empty name or named. - When reading an entry fails, the
writableof the writer is now aborted with the error instead of being closed. A custom writer used to observe a successful close although the data was truncated
Bug fixes
- Errors raised by
addFileSystemHandle()andexportFileSystemHandle()are now rethrown unmodified instead of being wrapped. Theirmessageis comparable to the exportedERR_*constants again, and theirentryNameproperty identifies the handle or the entry that failed exportFileSystemHandle()called withconcurrentset totruenow cancels the entries that have not started yet when an entry fails, instead of letting the rest of the export run to completion
Build and packaging
- Web worker support is now tree-shakable. The web worker backend was moved to a separate module which registers itself when imported. Custom builds that do not import it no longer include the web worker plumbing and run codecs inline. The standard entry points import it, so the published builds are unaffected
- The most frequently used globals are destructured in the intro of the bundles, which reduces the size of the minified builds
Full Changelog: v2.8.49...v2.8.50
v2.8.49
What's Changed in v2.8.49
Compatibility improvements
- The embedded JavaScript deflate engine is now compiled with an ES2019 target. It no longer contains nullish coalescing operators, which require Chromium 80. As a result, the WebAssembly and "native" builds now work down to Chromium 76 instead of 80 (see #669). The compatibility table on https://gildas-lormeau.github.io/zip.js/ has been updated accordingly
- Fixed a regression introduced in v2.8.18: when the worker script passed via
workerURIcould not be started as a module worker, zip.js fell back silently to inline workers instead of retrying with a classic worker. Browsers without module worker support, e.g. Chromium 76 to 79, now load external worker scripts again - Codecs registered with
registerCodec()and acodecURInow run inline transparently when the web worker cannot import the codec module, e.g. on browsers which do not supportimport()in workers like Chromium 76 to 79 and Firefox 102 to 113. SettinguseWebWorkerstofalseis no longer needed on these browsers
These changes were verified by running the test suite on real Chromium 76, 79 and 80 builds. Known limitation on Chromium 76 to 79: aborting operations in progress has no effect because these versions ignore the signal option of pipeTo(). The data read or written remains correct.
Full Changelog: v2.8.48...v2.8.49
v2.8.48
Bug fixes
- Fixed reading and writing data when the global stream classes are polyfilled, e.g. with web-streams-polyfill on Firefox 79. Reading a zip file from a stream,
Entry#arrayBuffer(),createBlobTempStream()and the filesystem API could fail or hang because zip.js consumed streams internally with the nativeResponseconstructor orReadableStream#pipeTo(), which do not accept polyfilled streams. With the polyfill loaded, the whole test suite now passes in an environment withoutTransformStream. Thanks to @danny0838 for running the test suite on older browsers (#669) - Fixed the generation of temporary file names in
createOPFSTempStream()andcreateSyncAccessHandleTempStream()whencrypto.randomUUIDis unavailable, e.g. in Chrome 67 to 91 or Safari 14.1 to 15.3
Other changes
- Browser tests requiring a feature the browser does not support, e.g.
CompressionStreamor OPFS, are now reported as skipped instead of failing, which makes the test results meaningful on older browsers - New
npm run serve-testsscript to run the tests manually in a browser, and a new README documenting how to run and write tests
Full Changelog: v2.8.47...v2.8.48
v2.8.47
What's Changed in v2.8.47
New features
- New
signCentralDirectoryoption inZipWriter#close()to sign zip files. The function receives the raw data of the central directory records and returns the data of a digital signature record, e.g. a PKCS#7 signature, written between the central directory and the end of central directory record. The signature is exposed by the newdigitalSignatureproperty ofZipReader, along with the newdirectoryOffsetanddirectoryLengthproperties to locate the signed data and verify it. zip.js stores the data as-is and does not implement the signature computation itself - New
decryptCentralDirectoryoption inZipReaderto read zip files with an encrypted central directory, as defined in the Strong Encryption Specification of the ZIP format specification. The function receives the raw data stored in place of the central directory and the encryption metadata exposed in aDirectoryEncryptionInfoobject, and returns the decrypted central directory records. Without this option, reading such zip files throws anERR_ENCRYPTED_CENTRAL_DIRECTORYerror. zip.js provides the encrypted data and the related metadata but does not implement the decryption itself - All the user-facing error message constants are now exported, e.g.
ERR_INVALID_UID,ERR_INVALID_MSDOS_ATTRIBUTES,ERR_ENTRY_EXISTS,ERR_UNSUPPORTED_CRYPTO_APIandERR_WORKER_STARTUP_TIMEOUT. This allows comparingerror.messagewith the constants instead of hardcoding the messages
Other changes
- Improved browser testing, thanks to @danny0838 (#672): module import errors are now reported in the test results, and the new
withStreamsPolyfillURL parameter runs the test suite with web-streams-polyfill on browsers missingTransformStream
Full Changelog: v2.8.46...v2.8.47
v2.8.46
What's Changed in v2.8.46
New features
- New
createWorkeroption inconfigure()to create the web workers, taking precedence overworkerURI. It enables the standard bundler patternnew Worker(new URL("./zip-worker.js", import.meta.url), { type: "module" }), letting bundlers like webpack and Vite detect the worker script statically and compile it with its imports into a separate asset - New
initWorker()function exposed by the new@zip.js/zip.js/workerentry point to write custom worker scripts. It can register alternative implementations ofCompressionStream/DecompressionStreamused to compress/decompress data, e.g. based on fflate, and aninithook called with the configuration, e.g. to load a WebAssembly module. The API reference ofinitWorker()includes a complete example based on fflate. Existing custom worker scripts relying on theinitModuleconvention keep working - New
@zip.js/zip.js/lib/zip-core-custom.jsentry point offering the full API without embedding any web worker code or deflate implementation. Combined withcreateWorkerand a custom worker script, the compression engine of your choice ships only once, in the worker script - New
CompressionStreamFallbackandDecompressionStreamFallbackoptions inconfigure()replacing the deprecatedCompressionStreamZlibandDecompressionStreamZliboptions. The new names reflect the role of these implementations: the fallback used whenuseCompressionStreamis set tofalseor when the Compression Streams API is unavailable
Other changes
- New section in the documentation covering custom web workers and compression engines: https://gildas-lormeau.github.io/zip.js/#custom-workers
- The files embedding the web worker code and the WebAssembly module are now generated with rollup plugins instead of placeholder replacements, and the development and release build configurations have been merged
Full Changelog: v2.8.45...v2.8.46
v2.8.45
What's Changed in v2.8.45
New features
- New
localExtraFieldoption inZipWriter#add()to write extra field records only in the local file header, complementing theextraFieldoption which writes them in both the local file header and the central directory. This helps reproducing entries whose records appear only in the local file header, as written by some tools - The Info-ZIP Unix type 1 extra field (0x5855), written notably by macOS Archive Utility and
ditto, and the PKWARE Unix extra field (0x000d) are now read. They are exposed via the newextraFieldUnixType1andextraFieldPkwareUnixproperties of entries, and populate thelastModDate,lastAccessDate,uidandgidproperties. The extended timestamp and NTFS extra fields still take precedence for the dates when present
Bug fixes
- AES-encrypted entries added with
passThroughset totrueand a providedcrc32value are now marked as AE-1 and store the CRC-32 in the entry headers, instead of being written as AE-2 entries with zeroed CRC-32 fields. Copying an AE-1 entry read with thepassThroughoption no longer loses the stored CRC-32. Without thecrc32option, entries are still written as AE-2 - The export methods of the filesystem API, e.g.
FS#exportBlob(), now forward the internal file attributes, theuid/gidvalues (written as an Info-ZIP Unix extra field, or as configured withunixExtraFieldType), and the custom extra field records of imported entries instead of dropping them - The
rawLastAccessDateandextraFieldUSDZproperties are now copied to the entries returned byZipReader#getEntries(); they were parsed but missing from the entry objects
Other changes
- The MIME type table is now generated at build time from a compact string encoding, reducing the builds embedding it by approximately 8.5KB when minified, e.g.
index.min.js, without changing the results ofgetMimeType() - The JSR package now only contains the files needed at runtime, shrinking it from 7.8MB (404 files) to 0.7MB (76 files), and all its entry points are now associated with the type declarations
- The browser test suites now run headlessly on Chrome, Firefox and WebKit with playwright. The WebKit suite adds Safari-engine coverage that did not exist before
Full Changelog: v2.8.44...v2.8.45
v2.8.44
What's Changed in v2.8.44
New features
- New
checkCrc32option inZipReaderandgetData()replacing the deprecatedcheckSignatureoption. It also verifies the CRC-32 of entries encrypted with AES when the zip file uses the AE-1 format, which stores the real CRC-32 value - New
checkAuthenticationCodeoption, enabled by default, controlling the verification of the authentication code of entries encrypted with AES - New
crc32option replacing the deprecatedsignatureoption when adding entries withpassThroughset totrue - New
crc32property on entries replacing the deprecatedsignatureproperty. It isundefinedwhen the zip file does not store the CRC-32 value, e.g. for entries encrypted with AES in AE-2 format - New
ERR_INVALID_CRC32andERR_INVALID_AUTHENTICATION_CODEerror constants replacing the deprecatedERR_INVALID_SIGNATUREconstant. The CRC-32 verification throws the former, the verification of the authentication code of entries encrypted with AES throws the latter. The three constants share the same string value for backward compatibility, the two new constants will become distinct strings in the next minor version
Bug fixes
- Dates outside the range representable in the NTFS extra field are now clamped to its bounds, i.e. 1601-01-01 and the year 30828 (the maximum signed 64-bit
FILETIMEvalue, chosen because Windows and 7-Zip reject or misread values beyond it), instead of overflowing silently to an arbitrary value - The last modification date is now truncated to the whole second before rounding odd seconds up to the next even second in the DOS date. Sub-second fractions no longer round the DOS date up, aligning it with Info-ZIP and ditto and keeping it within 1 second of the extended timestamp (see #671)
- The deprecated
signatureproperty of entries encrypted with AES returned byZipWriter#add()is nowundefined, like the newcrc32property, instead of exposing the raw authentication code as aUint8Arraydespite being typed as anumber
Full Changelog: v2.8.43...v2.8.44