@zip.js/zip.js 2.19.0 → 2.20.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/index.cjs CHANGED
@@ -4803,6 +4803,8 @@ const WARNING_APPENDED_DATA = "appended data";
4803
4803
  const WARNING_PREPENDED_DATA = "prepended data";
4804
4804
  const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
4805
4805
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
4806
+ const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = "mismatched central directory offset";
4807
+ const WARNING_MISSING_ZIP64_EXTRA_FIELD = "missing zip64 extra field";
4806
4808
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
4807
4809
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
4808
4810
  const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
@@ -4965,6 +4967,8 @@ class ZipReader {
4965
4967
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4966
4968
  }
4967
4969
  directoryDataOffset = getDiskOffset$1(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
4970
+ } else if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || diskNumber == MAX_16_BITS) {
4971
+ throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
4968
4972
  }
4969
4973
  }
4970
4974
  let declaredDirectoryDataLength = directoryDataLength;
@@ -4987,25 +4991,44 @@ class ZipReader {
4987
4991
  if (directoryArray.length < 4) {
4988
4992
  throw new Error(ERR_BAD_FORMAT);
4989
4993
  }
4990
- const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4994
+ let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4991
4995
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4992
4996
  const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4993
4997
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4994
4998
  detectEncryptedCentralDirectory(directoryView);
4995
4999
  let reconcile = !storedPointsAtDirectory;
4996
- if (!reconcile && expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4997
- const expectedSignatureArray = await readUint8Array(reader, expectedDirectoryDataOffset, 4);
4998
- reconcile = getUint32$1(getDataView(expectedSignatureArray), 0) == CENTRAL_FILE_HEADER_SIGNATURE;
5000
+ if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
5001
+ const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5002
+ if (storedPointsAtDirectory) {
5003
+ reconcile = expectedPointsAtDirectory;
5004
+ } else if (!expectedPointsAtDirectory) {
5005
+ const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
5006
+ const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
5007
+ if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5008
+ await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5009
+ expectedDirectoryDataOffset = signedDirectoryDataOffset;
5010
+ }
5011
+ }
4999
5012
  }
5000
5013
  if (reconcile) {
5001
5014
  const originalDirectoryDataOffset = directoryDataOffset;
5002
5015
  directoryDataOffset = expectedDirectoryDataOffset;
5016
+ directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5017
+ directoryView = getDataView(directoryArray);
5003
5018
  if (directoryDataOffset > originalDirectoryDataOffset) {
5004
5019
  prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5005
5020
  prependedCentralDirectory = storedPointsAtDirectory;
5021
+ } else {
5022
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5023
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5024
+ if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
5025
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5026
+ if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
5027
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
5028
+ prependedDataLength += offsetDelta;
5029
+ }
5030
+ }
5006
5031
  }
5007
- directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5008
- directoryView = getDataView(directoryArray);
5009
5032
  }
5010
5033
  }
5011
5034
  }
@@ -5116,7 +5139,16 @@ class ZipReader {
5116
5139
  filename,
5117
5140
  comment
5118
5141
  });
5119
- const malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5142
+ let malformedExtraField;
5143
+ try {
5144
+ malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5145
+ } catch (error) {
5146
+ if (checkAmbiguity || error.message != ERR_EXTRAFIELD_ZIP64_NOT_FOUND) {
5147
+ throw error;
5148
+ }
5149
+ fileEntry.zip64ExtraFieldMissing = true;
5150
+ addWarning(warnings, WARNING_MISSING_ZIP64_EXTRA_FIELD, fileEntry.filename);
5151
+ }
5120
5152
  filename = fileEntry.filename;
5121
5153
  if (normalizeFilename) {
5122
5154
  const normalizedFilename = normalizeFilename(filename);
@@ -5158,11 +5190,12 @@ class ZipReader {
5158
5190
  const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
5159
5191
  const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
5160
5192
  const symlink = (unixType & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK;
5161
- const executable = !symlink && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5162
- ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5163
- : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5164
5193
  const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5165
5194
  const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5195
+ const directory = modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE);
5196
+ const executable = !symlink && !directory && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5197
+ ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5198
+ : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5166
5199
  Object.assign(fileEntry, {
5167
5200
  setuid,
5168
5201
  setgid,
@@ -5170,7 +5203,7 @@ class ZipReader {
5170
5203
  symlink,
5171
5204
  unixExternalUpper,
5172
5205
  executable,
5173
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
5206
+ directory,
5174
5207
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5175
5208
  });
5176
5209
  const entry = new Entry(fileEntry);
@@ -5226,10 +5259,10 @@ class ZipReader {
5226
5259
  const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
5227
5260
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
5228
5261
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
5229
- if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
5262
+ if (checkAmbiguity && (prependedDataLength > 0 || (filesLength && startOffset > splitZipSignatureLength))) {
5230
5263
  throwAmbiguousArchive(WARNING_PREPENDED_DATA);
5231
5264
  }
5232
- if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5265
+ if (prependedDataLength > 0 || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5233
5266
  addWarning(warnings, WARNING_PREPENDED_DATA);
5234
5267
  }
5235
5268
  if (prependedCentralDirectory) {
@@ -5389,6 +5422,9 @@ let ZipEntry$1 = class ZipEntry {
5389
5422
 
5390
5423
  async getData(writer, fileEntry, readRanges, options = {}) {
5391
5424
  const zipEntry = this;
5425
+ if (zipEntry.zip64ExtraFieldMissing) {
5426
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5427
+ }
5392
5428
  const config = getConfiguration();
5393
5429
  const {
5394
5430
  reader,
@@ -5626,6 +5662,30 @@ function getWrappedFilesLength(directoryView, directoryArray, offset) {
5626
5662
  return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
5627
5663
  }
5628
5664
 
5665
+ function pointsAtDirectory(reader, offset) {
5666
+ return startsWithSignature(reader, offset, CENTRAL_FILE_HEADER_SIGNATURE);
5667
+ }
5668
+
5669
+ async function startsWithSignature(reader, offset, signature) {
5670
+ if (offset < 0 || offset + 4 > reader.size) {
5671
+ return false;
5672
+ }
5673
+ const signatureArray = await readUint8Array(reader, offset, 4);
5674
+ return getUint32$1(getDataView(signatureArray), 0) == signature;
5675
+ }
5676
+
5677
+ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5678
+ const startOffset = Math.max(0, endOffset - (6 + MAX_16_BITS));
5679
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5680
+ const tailView = getDataView(tailArray);
5681
+ for (let indexByte = tailArray.length - 6; indexByte >= 0; indexByte--) {
5682
+ if (getUint32$1(tailView, indexByte) == DIGITAL_SIGNATURE_RECORD_SIGNATURE &&
5683
+ indexByte + 6 + getUint16$1(tailView, indexByte + 4) == tailArray.length) {
5684
+ return startOffset + indexByte;
5685
+ }
5686
+ }
5687
+ }
5688
+
5629
5689
  function readDigitalSignature(signatureRecordArray) {
5630
5690
  if (signatureRecordArray.length >= 6) {
5631
5691
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5757,6 +5817,11 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5757
5817
  readExtraFieldUnixDates(extraFieldUnixType1, directory);
5758
5818
  directory.extraFieldUnixType1 = extraFieldUnixType1;
5759
5819
  }
5820
+ const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5821
+ if (extraFieldExtendedTimestamp) {
5822
+ readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5823
+ directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5824
+ }
5760
5825
  const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS);
5761
5826
  if (extraFieldNTFS) {
5762
5827
  readExtraFieldNTFS(extraFieldNTFS, directory);
@@ -5773,11 +5838,6 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5773
5838
  readExtraFieldUnix(extraFieldUnix, unixIdsRead ? {} : directory, false);
5774
5839
  directory.extraFieldUnix = extraFieldUnix;
5775
5840
  }
5776
- const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5777
- if (extraFieldExtendedTimestamp) {
5778
- readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5779
- directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5780
- }
5781
5841
  const extraFieldUSDZ = extraField.get(EXTRAFIELD_TYPE_USDZ);
5782
5842
  if (extraFieldUSDZ) {
5783
5843
  directory.extraFieldUSDZ = extraFieldUSDZ;
@@ -6416,11 +6476,13 @@ var zipReader = /*#__PURE__*/Object.freeze({
6416
6476
  WARNING_COMPRESSED_PATCHED_DATA: WARNING_COMPRESSED_PATCHED_DATA,
6417
6477
  WARNING_DUPLICATE_FILENAME: WARNING_DUPLICATE_FILENAME,
6418
6478
  WARNING_MALFORMED_EXTRA_FIELD: WARNING_MALFORMED_EXTRA_FIELD,
6479
+ WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET: WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET,
6419
6480
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG: WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
6420
6481
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD: WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
6421
6482
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES: WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES,
6422
6483
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME: WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
6423
6484
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY: WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
6485
+ WARNING_MISSING_ZIP64_EXTRA_FIELD: WARNING_MISSING_ZIP64_EXTRA_FIELD,
6424
6486
  WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY: WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
6425
6487
  WARNING_PREPENDED_CENTRAL_DIRECTORY: WARNING_PREPENDED_CENTRAL_DIRECTORY,
6426
6488
  WARNING_PREPENDED_DATA: WARNING_PREPENDED_DATA,
@@ -6553,13 +6615,14 @@ class ZipWriter {
6553
6615
  return watchPromiseError(this, prependZipEntries(this, reader));
6554
6616
  }
6555
6617
 
6556
- appendZip(reader) {
6557
- return watchPromiseError(this, this.appendZipEntries(reader));
6618
+ appendZip(reader, options = {}) {
6619
+ return watchPromiseError(this, this.appendZipEntries(reader, options));
6558
6620
  }
6559
6621
 
6560
- async appendZipEntries(reader) {
6622
+ async appendZipEntries(reader, options = {}) {
6561
6623
  const zipWriter = this;
6562
6624
  const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6625
+ const filter = checkFunctionOption(options.filter);
6563
6626
  while (pendingAddFileCalls.size) {
6564
6627
  await Promise.allSettled(Array.from(pendingAddFileCalls));
6565
6628
  }
@@ -6578,10 +6641,16 @@ class ZipWriter {
6578
6641
  const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6579
6642
  const zipReader$1 = new ZipReader(reader);
6580
6643
  const entries = await zipReader$1.getEntries();
6644
+ const keptEntries = [];
6645
+ for (const entry of entries) {
6646
+ if (!filter || await filter(entry)) {
6647
+ keptEntries.push(entry);
6648
+ }
6649
+ }
6581
6650
  await zipReader$1.close();
6582
6651
  await initStream(zipWriter.writer);
6583
6652
  const { directoryOffset } = zipReader$1;
6584
- entries.forEach(({ filename }) => {
6653
+ keptEntries.forEach(({ filename }) => {
6585
6654
  if (filenames.has(filename)) {
6586
6655
  throw new Error(ERR_DUPLICATED_NAME);
6587
6656
  }
@@ -6602,8 +6671,8 @@ class ZipWriter {
6602
6671
  zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6603
6672
  }
6604
6673
  }
6605
- const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
6606
- entries.forEach(entry => {
6674
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
6675
+ keptEntries.forEach(entry => {
6607
6676
  const {
6608
6677
  version,
6609
6678
  rawLastModDate,
@@ -8614,26 +8683,42 @@ function removeExtraFieldZip64(rawExtraField) {
8614
8683
  return rawExtraField;
8615
8684
  }
8616
8685
 
8617
- async function copyZipData(zipWriter, reader, entries, directoryOffset) {
8686
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
8618
8687
  const { writer } = zipWriter;
8619
8688
  const entryPositions = new Map();
8620
- if (writer.closeDisk) {
8689
+ if (writer.closeDisk || compact) {
8621
8690
  const sortedEntries = Array.from(entries).sort((firstEntry, secondEntry) =>
8622
8691
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8623
- let copiedLength = 0;
8624
- for (const entry of sortedEntries) {
8625
- const sourceOffset = getSourceOffset(reader, entry);
8626
- await copyData(zipWriter, reader, copiedLength, sourceOffset - copiedLength);
8627
- if (exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8628
- await writer.closeDisk();
8629
- }
8630
- entryPositions.set(entry, {
8631
- offset: getSegmentOffset(zipWriter, writer),
8632
- diskNumberStart: getDiskNumber(writer)
8633
- });
8634
- copiedLength = sourceOffset;
8692
+ const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8693
+ const keptEntrySet = new Set(keptEntries);
8694
+ let copiedOffset = 0;
8695
+ let lastPosition;
8696
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8697
+ const entry = sortedEntries[indexEntry];
8698
+ if (keptEntrySet.has(entry)) {
8699
+ const sourceOffset = getSourceOffset(reader, entry);
8700
+ if (sourceOffset < copiedOffset) {
8701
+ entryPositions.set(entry, lastPosition);
8702
+ } else {
8703
+ if (!compact) {
8704
+ await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8705
+ }
8706
+ if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8707
+ await writer.closeDisk();
8708
+ }
8709
+ lastPosition = {
8710
+ offset: getSegmentOffset(zipWriter, writer),
8711
+ diskNumberStart: getDiskNumber(writer)
8712
+ };
8713
+ entryPositions.set(entry, lastPosition);
8714
+ await copyData(zipWriter, reader, sourceOffset, regionEnds[indexEntry] - sourceOffset);
8715
+ copiedOffset = regionEnds[indexEntry];
8716
+ }
8717
+ }
8718
+ }
8719
+ if (!compact) {
8720
+ await copyData(zipWriter, reader, copiedOffset, directoryOffset - copiedOffset);
8635
8721
  }
8636
- await copyData(zipWriter, reader, copiedLength, directoryOffset - copiedLength);
8637
8722
  } else {
8638
8723
  const baseOffset = zipWriter.offset;
8639
8724
  await copyData(zipWriter, reader, 0, directoryOffset);
@@ -8645,6 +8730,20 @@ async function copyZipData(zipWriter, reader, entries, directoryOffset) {
8645
8730
  return entryPositions;
8646
8731
  }
8647
8732
 
8733
+ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8734
+ const regionEnds = new Array(sortedEntries.length);
8735
+ let regionEnd = directoryOffset;
8736
+ for (let indexEntry = sortedEntries.length - 1; indexEntry >= 0; indexEntry--) {
8737
+ const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8738
+ const nextEntry = sortedEntries[indexEntry + 1];
8739
+ if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8740
+ regionEnd = getSourceOffset(reader, nextEntry);
8741
+ }
8742
+ regionEnds[indexEntry] = regionEnd;
8743
+ }
8744
+ return regionEnds;
8745
+ }
8746
+
8648
8747
  async function copyData(zipWriter, reader, offset, size) {
8649
8748
  if (size > 0) {
8650
8749
  const { writer } = zipWriter;
@@ -9022,7 +9121,7 @@ function formatSupported(StreamClass, format) {
9022
9121
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9023
9122
  */
9024
9123
 
9025
- const VERSION = "2.19.0";
9124
+ const VERSION = "2.20.0";
9026
9125
 
9027
9126
  /*
9028
9127
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -11762,11 +11861,13 @@ exports.WARNING_COMPRESSED_PATCHED_DATA = WARNING_COMPRESSED_PATCHED_DATA;
11762
11861
  exports.WARNING_COMPRESSION_UNAVAILABLE = WARNING_COMPRESSION_UNAVAILABLE;
11763
11862
  exports.WARNING_DUPLICATE_FILENAME = WARNING_DUPLICATE_FILENAME;
11764
11863
  exports.WARNING_MALFORMED_EXTRA_FIELD = WARNING_MALFORMED_EXTRA_FIELD;
11864
+ exports.WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET;
11765
11865
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG;
11766
11866
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD;
11767
11867
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES;
11768
11868
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME;
11769
11869
  exports.WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY;
11870
+ exports.WARNING_MISSING_ZIP64_EXTRA_FIELD = WARNING_MISSING_ZIP64_EXTRA_FIELD;
11770
11871
  exports.WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY;
11771
11872
  exports.WARNING_PREPENDED_CENTRAL_DIRECTORY = WARNING_PREPENDED_CENTRAL_DIRECTORY;
11772
11873
  exports.WARNING_PREPENDED_DATA = WARNING_PREPENDED_DATA;
package/index.d.cts CHANGED
@@ -1543,9 +1543,12 @@ export class ZipReader<Type> {
1543
1543
  * `strictness: "strict"` rejects with {@link ERR_AMBIGUOUS_ARCHIVE}: when the effective strictness tolerates
1544
1544
  * one of them and the evidence is already in hand, the same reason string is deposited as a warning instead —
1545
1545
  * {@link WARNING_APPENDED_DATA}, {@link WARNING_PREPENDED_DATA}, {@link WARNING_TRAILING_CENTRAL_DIRECTORY_DATA},
1546
- * {@link WARNING_DUPLICATE_FILENAME} and {@link WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY}.
1546
+ * {@link WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET}, {@link WARNING_DUPLICATE_FILENAME} and
1547
+ * {@link WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY}.
1547
1548
  * {@link WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY} is the one reason of that group which is never tolerated,
1548
- * so it is only ever the reason of an error.
1549
+ * so it is only ever the reason of an error. {@link WARNING_MISSING_ZIP64_EXTRA_FIELD} is deposited when an
1550
+ * entry cannot be read because its central directory record lacks a Zip64 extra field, and `"strict"` throws
1551
+ * {@link ERR_EXTRAFIELD_ZIP64_NOT_FOUND} for it.
1549
1552
  *
1550
1553
  * The warnings related to the local file header of an entry are deposited on
1551
1554
  * {@link EntryMetaData#warnings} when its data is read, not here.
@@ -1758,9 +1761,10 @@ export interface GetEntriesOptions {
1758
1761
  * systems, and it also occurs as the trail byte of legitimate double-byte filenames (e.g. CP932) decoded with
1759
1762
  * another charset.
1760
1763
  *
1761
- * Names are validated, never rewritten, so the filename reported for an entry always matches its central
1762
- * directory record. The name validated is the final one, i.e. the name of a valid Unicode Path extra field
1763
- * (see {@link EntryMetaData#extraFieldUnicodePath}) when the entry carries one.
1764
+ * Names are validated, never rewritten: the filename reported for an entry is the one its central directory
1765
+ * record stores, decoded, or the one of its Unicode Path extra field when the entry carries a valid one (see
1766
+ * {@link EntryMetaData#extraFieldUnicodePath}), and the bytes of the record stay available in
1767
+ * {@link EntryMetaData#rawFilename}. The name validated is that final name.
1764
1768
  *
1765
1769
  * @defaultValue The value of {@link GetEntriesOptions#strictness}.
1766
1770
  */
@@ -2521,7 +2525,9 @@ export interface EntryMetaData {
2521
2525
  * `true` if the entry is an executable file
2522
2526
  *
2523
2527
  * Always `false` when {@link EntryMetaData#symlink} is `true`: the permissions of a symbolic link
2524
- * are not meaningful, Unix systems store them as `0o777`.
2528
+ * are not meaningful, Unix systems store them as `0o777`. Always `false` when
2529
+ * {@link EntryMetaData#directory} is `true` too: the execute bits of a directory mean that it can be
2530
+ * searched, and every directory carries them; read {@link EntryMetaData#unixMode} for the bits themselves.
2525
2531
  */
2526
2532
  executable: boolean;
2527
2533
  /**
@@ -2569,8 +2575,9 @@ export interface EntryMetaData {
2569
2575
  creationDate?: Date;
2570
2576
  /**
2571
2577
  * The last modification date (raw), as the MS-DOS date and time stored in the header. Unlike
2572
- * {@link EntryMetaData#lastModDate}, it is not replaced by the value of the NTFS extra field when that field
2573
- * is present; read {@link EntryMetaData#extraFieldNTFS} for the raw NTFS value.
2578
+ * {@link EntryMetaData#lastModDate}, it is not replaced by the value of the extended timestamp or NTFS extra
2579
+ * field when one is present (the NTFS value wins over the extended timestamp one, being the finer of the two);
2580
+ * read {@link EntryMetaData#extraFieldNTFS} for the raw NTFS value.
2574
2581
  */
2575
2582
  rawLastModDate: number | bigint;
2576
2583
  /**
@@ -3079,7 +3086,13 @@ export class ZipWriter<Type> {
3079
3086
  * The returned promise can safely be left un-awaited: {@link ZipWriter#close} waits for the copy
3080
3087
  * and throws its error if it was not caught.
3081
3088
  *
3089
+ * With the {@link ZipWriterAppendZipOptions#filter} option, only the entries the function keeps are copied.
3090
+ * Combined with {@link ZipWriter#add}, this edits an existing zip file into a new one without decompressing
3091
+ * its data: the entries to keep are copied as-is, the entries to delete or replace are left out, and the
3092
+ * replacements and additions are added afterwards.
3093
+ *
3082
3094
  * @param reader The {@link Reader} instance used to read the content of the zip file.
3095
+ * @param options The options.
3083
3096
  * @returns A promise resolving when the zip file has been added.
3084
3097
  */
3085
3098
  appendZip<ReaderType>(
@@ -3089,7 +3102,8 @@ export class ZipWriter<Type> {
3089
3102
  | ReadableStream
3090
3103
  | Reader<unknown>[]
3091
3104
  | ReadableReader[]
3092
- | ReadableStream[]
3105
+ | ReadableStream[],
3106
+ options?: ZipWriterAppendZipOptions
3093
3107
  ): Promise<void>;
3094
3108
 
3095
3109
  /**
@@ -3307,6 +3321,29 @@ export interface ZipWriterAddDataOptions
3307
3321
  /**
3308
3322
  * Represents the options passed to {@link ZipWriter#close}.
3309
3323
  */
3324
+ /**
3325
+ * Represents the options passed to {@link ZipWriter#appendZip}.
3326
+ */
3327
+ export interface ZipWriterAppendZipOptions {
3328
+ /**
3329
+ * Selects the entries of the zip file to copy: the function is called once per entry, in the order of the
3330
+ * central directory, and the entry is copied when it returns (or resolves to) `true`. The function can read
3331
+ * the data of the entry with {@link Entry#getData} to decide, the zip file is closed after the last call.
3332
+ *
3333
+ * @remarks
3334
+ * When the option is set, the data of the zip file is copied entry by entry and the entries left out leave no
3335
+ * bytes behind in the output, unlike {@link ZipWriter#remove}, which drops an entry from the central directory
3336
+ * after its data has been written. The bytes of the zip file outside its entries, e.g. a self-extracting stub
3337
+ * before the first entry, are not copied either. Without the option, the data of the zip file is copied as a
3338
+ * whole. The duplicate filename check applies to the entries kept only, so an entry can be replaced by
3339
+ * leaving it out and adding its replacement with {@link ZipWriter#add}.
3340
+ *
3341
+ * @param entry The entry read from the zip file.
3342
+ * @returns `true` to copy the entry.
3343
+ */
3344
+ filter?: (entry: Entry) => boolean | Promise<boolean>;
3345
+ }
3346
+
3310
3347
  export interface ZipWriterCloseOptions extends EntryOnprogressOptions {
3311
3348
  /**
3312
3349
  * `true` to use Zip64 to write the entries directory.
@@ -4998,7 +5035,9 @@ export const ERR_BAD_FORMAT: string;
4998
5035
  */
4999
5036
  export const ERR_EOCDR_NOT_FOUND: string;
5000
5037
  /**
5001
- * Zip64 End of Central Directory Locator not found error
5038
+ * Zip64 End of Central Directory Locator not found error: the end of central directory record holds a Zip64
5039
+ * sentinel in its offset, size or disk number field but no Zip64 locator precedes it, or the locator does not
5040
+ * point at a Zip64 end of central directory record
5002
5041
  */
5003
5042
  export const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND: string;
5004
5043
  /**
@@ -5597,6 +5636,27 @@ export const WARNING_PREPENDED_CENTRAL_DIRECTORY: string;
5597
5636
  * the reason of {@link ERR_AMBIGUOUS_ARCHIVE} under `strictness: "strict"`
5598
5637
  */
5599
5638
  export const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA: string;
5639
+ /**
5640
+ * Warning reason: the end of central directory record stores a central directory offset that points past the
5641
+ * central directory actually found before it, so the archive was read from the directory found rather than from
5642
+ * the stored offset (see {@link ZipReader#warnings}); the reason of {@link ERR_AMBIGUOUS_ARCHIVE} under
5643
+ * `strictness: "strict"`
5644
+ *
5645
+ * @remarks
5646
+ * Such an archive is typically one written with absolute offsets for a prefix that is no longer there, e.g. a
5647
+ * self-extracting archive whose stub was removed. When the local file header of the first entry is found at
5648
+ * the same shifted position, the entries are read from the shifted positions; otherwise the offsets stored in
5649
+ * the central directory are used as they are.
5650
+ */
5651
+ export const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET: string;
5652
+ /**
5653
+ * Warning reason: a central directory record holds the Zip64 sentinel in a size, offset or disk number field
5654
+ * but carries no Zip64 extra field resolving it (see {@link ZipReader#warnings}). The entry is listed, its
5655
+ * sizes and offset are unusable, and reading its data throws {@link ERR_EXTRAFIELD_ZIP64_NOT_FOUND}; the
5656
+ * other entries are unaffected. Under `strictness: "strict"`, {@link ZipReader#getEntries} throws
5657
+ * {@link ERR_EXTRAFIELD_ZIP64_NOT_FOUND} instead.
5658
+ */
5659
+ export const WARNING_MISSING_ZIP64_EXTRA_FIELD: string;
5600
5660
  /**
5601
5661
  * Warning reason: several entries share the same filename (see {@link ZipReader#warnings}); the reason of
5602
5662
  * {@link ERR_AMBIGUOUS_ARCHIVE} under `strictness: "strict"`
package/index.d.ts CHANGED
@@ -1543,9 +1543,12 @@ export class ZipReader<Type> {
1543
1543
  * `strictness: "strict"` rejects with {@link ERR_AMBIGUOUS_ARCHIVE}: when the effective strictness tolerates
1544
1544
  * one of them and the evidence is already in hand, the same reason string is deposited as a warning instead —
1545
1545
  * {@link WARNING_APPENDED_DATA}, {@link WARNING_PREPENDED_DATA}, {@link WARNING_TRAILING_CENTRAL_DIRECTORY_DATA},
1546
- * {@link WARNING_DUPLICATE_FILENAME} and {@link WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY}.
1546
+ * {@link WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET}, {@link WARNING_DUPLICATE_FILENAME} and
1547
+ * {@link WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY}.
1547
1548
  * {@link WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY} is the one reason of that group which is never tolerated,
1548
- * so it is only ever the reason of an error.
1549
+ * so it is only ever the reason of an error. {@link WARNING_MISSING_ZIP64_EXTRA_FIELD} is deposited when an
1550
+ * entry cannot be read because its central directory record lacks a Zip64 extra field, and `"strict"` throws
1551
+ * {@link ERR_EXTRAFIELD_ZIP64_NOT_FOUND} for it.
1549
1552
  *
1550
1553
  * The warnings related to the local file header of an entry are deposited on
1551
1554
  * {@link EntryMetaData#warnings} when its data is read, not here.
@@ -1758,9 +1761,10 @@ export interface GetEntriesOptions {
1758
1761
  * systems, and it also occurs as the trail byte of legitimate double-byte filenames (e.g. CP932) decoded with
1759
1762
  * another charset.
1760
1763
  *
1761
- * Names are validated, never rewritten, so the filename reported for an entry always matches its central
1762
- * directory record. The name validated is the final one, i.e. the name of a valid Unicode Path extra field
1763
- * (see {@link EntryMetaData#extraFieldUnicodePath}) when the entry carries one.
1764
+ * Names are validated, never rewritten: the filename reported for an entry is the one its central directory
1765
+ * record stores, decoded, or the one of its Unicode Path extra field when the entry carries a valid one (see
1766
+ * {@link EntryMetaData#extraFieldUnicodePath}), and the bytes of the record stay available in
1767
+ * {@link EntryMetaData#rawFilename}. The name validated is that final name.
1764
1768
  *
1765
1769
  * @defaultValue The value of {@link GetEntriesOptions#strictness}.
1766
1770
  */
@@ -2521,7 +2525,9 @@ export interface EntryMetaData {
2521
2525
  * `true` if the entry is an executable file
2522
2526
  *
2523
2527
  * Always `false` when {@link EntryMetaData#symlink} is `true`: the permissions of a symbolic link
2524
- * are not meaningful, Unix systems store them as `0o777`.
2528
+ * are not meaningful, Unix systems store them as `0o777`. Always `false` when
2529
+ * {@link EntryMetaData#directory} is `true` too: the execute bits of a directory mean that it can be
2530
+ * searched, and every directory carries them; read {@link EntryMetaData#unixMode} for the bits themselves.
2525
2531
  */
2526
2532
  executable: boolean;
2527
2533
  /**
@@ -2569,8 +2575,9 @@ export interface EntryMetaData {
2569
2575
  creationDate?: Date;
2570
2576
  /**
2571
2577
  * The last modification date (raw), as the MS-DOS date and time stored in the header. Unlike
2572
- * {@link EntryMetaData#lastModDate}, it is not replaced by the value of the NTFS extra field when that field
2573
- * is present; read {@link EntryMetaData#extraFieldNTFS} for the raw NTFS value.
2578
+ * {@link EntryMetaData#lastModDate}, it is not replaced by the value of the extended timestamp or NTFS extra
2579
+ * field when one is present (the NTFS value wins over the extended timestamp one, being the finer of the two);
2580
+ * read {@link EntryMetaData#extraFieldNTFS} for the raw NTFS value.
2574
2581
  */
2575
2582
  rawLastModDate: number | bigint;
2576
2583
  /**
@@ -3079,7 +3086,13 @@ export class ZipWriter<Type> {
3079
3086
  * The returned promise can safely be left un-awaited: {@link ZipWriter#close} waits for the copy
3080
3087
  * and throws its error if it was not caught.
3081
3088
  *
3089
+ * With the {@link ZipWriterAppendZipOptions#filter} option, only the entries the function keeps are copied.
3090
+ * Combined with {@link ZipWriter#add}, this edits an existing zip file into a new one without decompressing
3091
+ * its data: the entries to keep are copied as-is, the entries to delete or replace are left out, and the
3092
+ * replacements and additions are added afterwards.
3093
+ *
3082
3094
  * @param reader The {@link Reader} instance used to read the content of the zip file.
3095
+ * @param options The options.
3083
3096
  * @returns A promise resolving when the zip file has been added.
3084
3097
  */
3085
3098
  appendZip<ReaderType>(
@@ -3089,7 +3102,8 @@ export class ZipWriter<Type> {
3089
3102
  | ReadableStream
3090
3103
  | Reader<unknown>[]
3091
3104
  | ReadableReader[]
3092
- | ReadableStream[]
3105
+ | ReadableStream[],
3106
+ options?: ZipWriterAppendZipOptions
3093
3107
  ): Promise<void>;
3094
3108
 
3095
3109
  /**
@@ -3307,6 +3321,29 @@ export interface ZipWriterAddDataOptions
3307
3321
  /**
3308
3322
  * Represents the options passed to {@link ZipWriter#close}.
3309
3323
  */
3324
+ /**
3325
+ * Represents the options passed to {@link ZipWriter#appendZip}.
3326
+ */
3327
+ export interface ZipWriterAppendZipOptions {
3328
+ /**
3329
+ * Selects the entries of the zip file to copy: the function is called once per entry, in the order of the
3330
+ * central directory, and the entry is copied when it returns (or resolves to) `true`. The function can read
3331
+ * the data of the entry with {@link Entry#getData} to decide, the zip file is closed after the last call.
3332
+ *
3333
+ * @remarks
3334
+ * When the option is set, the data of the zip file is copied entry by entry and the entries left out leave no
3335
+ * bytes behind in the output, unlike {@link ZipWriter#remove}, which drops an entry from the central directory
3336
+ * after its data has been written. The bytes of the zip file outside its entries, e.g. a self-extracting stub
3337
+ * before the first entry, are not copied either. Without the option, the data of the zip file is copied as a
3338
+ * whole. The duplicate filename check applies to the entries kept only, so an entry can be replaced by
3339
+ * leaving it out and adding its replacement with {@link ZipWriter#add}.
3340
+ *
3341
+ * @param entry The entry read from the zip file.
3342
+ * @returns `true` to copy the entry.
3343
+ */
3344
+ filter?: (entry: Entry) => boolean | Promise<boolean>;
3345
+ }
3346
+
3310
3347
  export interface ZipWriterCloseOptions extends EntryOnprogressOptions {
3311
3348
  /**
3312
3349
  * `true` to use Zip64 to write the entries directory.
@@ -4998,7 +5035,9 @@ export const ERR_BAD_FORMAT: string;
4998
5035
  */
4999
5036
  export const ERR_EOCDR_NOT_FOUND: string;
5000
5037
  /**
5001
- * Zip64 End of Central Directory Locator not found error
5038
+ * Zip64 End of Central Directory Locator not found error: the end of central directory record holds a Zip64
5039
+ * sentinel in its offset, size or disk number field but no Zip64 locator precedes it, or the locator does not
5040
+ * point at a Zip64 end of central directory record
5002
5041
  */
5003
5042
  export const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND: string;
5004
5043
  /**
@@ -5597,6 +5636,27 @@ export const WARNING_PREPENDED_CENTRAL_DIRECTORY: string;
5597
5636
  * the reason of {@link ERR_AMBIGUOUS_ARCHIVE} under `strictness: "strict"`
5598
5637
  */
5599
5638
  export const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA: string;
5639
+ /**
5640
+ * Warning reason: the end of central directory record stores a central directory offset that points past the
5641
+ * central directory actually found before it, so the archive was read from the directory found rather than from
5642
+ * the stored offset (see {@link ZipReader#warnings}); the reason of {@link ERR_AMBIGUOUS_ARCHIVE} under
5643
+ * `strictness: "strict"`
5644
+ *
5645
+ * @remarks
5646
+ * Such an archive is typically one written with absolute offsets for a prefix that is no longer there, e.g. a
5647
+ * self-extracting archive whose stub was removed. When the local file header of the first entry is found at
5648
+ * the same shifted position, the entries are read from the shifted positions; otherwise the offsets stored in
5649
+ * the central directory are used as they are.
5650
+ */
5651
+ export const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET: string;
5652
+ /**
5653
+ * Warning reason: a central directory record holds the Zip64 sentinel in a size, offset or disk number field
5654
+ * but carries no Zip64 extra field resolving it (see {@link ZipReader#warnings}). The entry is listed, its
5655
+ * sizes and offset are unusable, and reading its data throws {@link ERR_EXTRAFIELD_ZIP64_NOT_FOUND}; the
5656
+ * other entries are unaffected. Under `strictness: "strict"`, {@link ZipReader#getEntries} throws
5657
+ * {@link ERR_EXTRAFIELD_ZIP64_NOT_FOUND} instead.
5658
+ */
5659
+ export const WARNING_MISSING_ZIP64_EXTRA_FIELD: string;
5600
5660
  /**
5601
5661
  * Warning reason: several entries share the same filename (see {@link ZipReader#warnings}); the reason of
5602
5662
  * {@link ERR_AMBIGUOUS_ARCHIVE} under `strictness: "strict"`