@zip.js/zip.js 2.19.0 → 2.21.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
@@ -120,6 +120,10 @@ const MIN_DATE = new Date(1980, 0, 1);
120
120
 
121
121
  const UNDEFINED_VALUE = undefined;
122
122
  const INFINITY_VALUE = Infinity;
123
+ const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
124
+ const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
125
+ const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
126
+
123
127
  const UNDEFINED_TYPE = "undefined";
124
128
  const FUNCTION_TYPE = "function";
125
129
  const OBJECT_TYPE = "object";
@@ -4779,12 +4783,9 @@ const ERR_BAD_FORMAT = "File format is not recognized";
4779
4783
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4780
4784
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4781
4785
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4782
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4783
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4784
4786
  const ERR_ENCRYPTED = "File contains encrypted entry";
4785
4787
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4786
4788
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4787
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4788
4789
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4789
4790
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4790
4791
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4803,6 +4804,8 @@ const WARNING_APPENDED_DATA = "appended data";
4803
4804
  const WARNING_PREPENDED_DATA = "prepended data";
4804
4805
  const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
4805
4806
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
4807
+ const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = "mismatched central directory offset";
4808
+ const WARNING_MISSING_ZIP64_EXTRA_FIELD = "missing zip64 extra field";
4806
4809
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
4807
4810
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
4808
4811
  const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
@@ -4847,7 +4850,8 @@ class ZipReader {
4847
4850
  Object.assign(this, {
4848
4851
  reader: new GenericReader(reader),
4849
4852
  options,
4850
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4853
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4854
+ entriesMissingZip64ExtraField: new WeakSet()
4851
4855
  });
4852
4856
  }
4853
4857
 
@@ -4918,16 +4922,22 @@ class ZipReader {
4918
4922
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4919
4923
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4920
4924
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4921
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4922
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4923
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4924
- const originalDirectoryDataOffset = directoryDataOffset;
4925
- directoryDataOffset = expectedDirectoryDataOffset;
4926
- if (directoryDataOffset > originalDirectoryDataOffset) {
4927
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4925
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4926
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4927
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4928
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4929
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4930
+ }
4931
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4932
+ directoryDataOffset != expectedDirectoryDataOffset) {
4933
+ const originalDirectoryDataOffset = directoryDataOffset;
4934
+ directoryDataOffset = expectedDirectoryDataOffset;
4935
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4936
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4937
+ }
4938
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4939
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4928
4940
  }
4929
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4930
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4931
4941
  }
4932
4942
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4933
4943
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4965,15 +4975,13 @@ class ZipReader {
4965
4975
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4966
4976
  }
4967
4977
  directoryDataOffset = getDiskOffset$1(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
4978
+ } else if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || diskNumber == MAX_16_BITS) {
4979
+ throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
4968
4980
  }
4969
4981
  }
4970
4982
  let declaredDirectoryDataLength = directoryDataLength;
4971
4983
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4972
4984
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4973
- if (directoryDataOffset >= reader.size) {
4974
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4975
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4976
- }
4977
4985
  if (expectedLastDiskNumber != lastDiskNumber) {
4978
4986
  throw new Error(ERR_SPLIT_ZIP_FILE);
4979
4987
  }
@@ -4981,33 +4989,64 @@ class ZipReader {
4981
4989
  throw new Error(ERR_BAD_FORMAT);
4982
4990
  }
4983
4991
  let offset = 0;
4984
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4992
+ let directoryArray = directoryDataOffset < reader.size ?
4993
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
4994
+ EMPTY_UINT8_ARRAY;
4985
4995
  let directoryView = getDataView(directoryArray);
4986
4996
  if (directoryDataLength) {
4987
- if (directoryArray.length < 4) {
4988
- throw new Error(ERR_BAD_FORMAT);
4989
- }
4990
- const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4997
+ let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4991
4998
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4992
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4999
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
5000
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4993
5001
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4994
- detectEncryptedCentralDirectory(directoryView);
5002
+ detectEncryptedCentralDirectory(directoryView));
5003
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
5004
+ throw new Error(ERR_BAD_FORMAT);
5005
+ }
4995
5006
  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;
5007
+ if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
5008
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5009
+ if (!expectedPointsAtDirectory) {
5010
+ const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
5011
+ const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
5012
+ if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5013
+ await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5014
+ expectedDirectoryDataOffset = signedDirectoryDataOffset;
5015
+ expectedPointsAtDirectory = true;
5016
+ }
5017
+ }
5018
+ if (storedPointsAtDirectory) {
5019
+ reconcile = expectedPointsAtDirectory;
5020
+ }
4999
5021
  }
5000
- if (reconcile) {
5022
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
5001
5023
  const originalDirectoryDataOffset = directoryDataOffset;
5002
5024
  directoryDataOffset = expectedDirectoryDataOffset;
5003
- if (directoryDataOffset > originalDirectoryDataOffset) {
5004
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5005
- prependedCentralDirectory = storedPointsAtDirectory;
5006
- }
5007
5025
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5008
5026
  directoryView = getDataView(directoryArray);
5027
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5028
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5029
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5030
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5031
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5032
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5033
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5034
+ prependedDataLength += offsetDelta;
5035
+ prependedCentralDirectory = storedPointsAtDirectory;
5036
+ } else {
5037
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5038
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5039
+ prependedDataLength += offsetDelta;
5040
+ }
5041
+ }
5009
5042
  }
5010
5043
  }
5044
+ if (directoryArray.length < 4) {
5045
+ throw new Error(ERR_BAD_FORMAT);
5046
+ }
5047
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5048
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5049
+ directoryDataOffset = centralDirectoryEndOffset;
5011
5050
  }
5012
5051
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5013
5052
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5049,8 +5088,10 @@ class ZipReader {
5049
5088
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5050
5089
  }
5051
5090
  }
5091
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5092
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5052
5093
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5053
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5094
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5054
5095
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5055
5096
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5056
5097
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5116,7 +5157,16 @@ class ZipReader {
5116
5157
  filename,
5117
5158
  comment
5118
5159
  });
5119
- const malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5160
+ let malformedExtraField;
5161
+ try {
5162
+ malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5163
+ } catch (error) {
5164
+ if (checkAmbiguity || error.message != ERR_EXTRAFIELD_ZIP64_NOT_FOUND) {
5165
+ throw error;
5166
+ }
5167
+ fileEntry.zip64ExtraFieldMissing = true;
5168
+ addWarning(warnings, WARNING_MISSING_ZIP64_EXTRA_FIELD, fileEntry.filename);
5169
+ }
5120
5170
  filename = fileEntry.filename;
5121
5171
  if (normalizeFilename) {
5122
5172
  const normalizedFilename = normalizeFilename(filename);
@@ -5132,13 +5182,17 @@ class ZipReader {
5132
5182
  if (malformedExtraField) {
5133
5183
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5134
5184
  }
5135
- fileEntry.offset += prependedDataLength;
5136
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5137
- startOffset = Math.min(entryPosition, startOffset);
5138
- if (entryPosition < previousEntryPosition) {
5139
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5185
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5186
+ startOffset = 0;
5187
+ } else {
5188
+ fileEntry.offset += prependedDataLength;
5189
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5190
+ startOffset = Math.min(entryPosition, startOffset);
5191
+ if (entryPosition < previousEntryPosition) {
5192
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5193
+ }
5194
+ previousEntryPosition = entryPosition;
5140
5195
  }
5141
- previousEntryPosition = entryPosition;
5142
5196
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5143
5197
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5144
5198
  }
@@ -5158,11 +5212,12 @@ class ZipReader {
5158
5212
  const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
5159
5213
  const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
5160
5214
  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
5215
  const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5165
5216
  const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5217
+ const directory = modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE);
5218
+ const executable = !symlink && !directory && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5219
+ ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5220
+ : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5166
5221
  Object.assign(fileEntry, {
5167
5222
  setuid,
5168
5223
  setgid,
@@ -5170,16 +5225,19 @@ class ZipReader {
5170
5225
  symlink,
5171
5226
  unixExternalUpper,
5172
5227
  executable,
5173
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
5228
+ directory,
5174
5229
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5175
5230
  });
5176
5231
  const entry = new Entry(fileEntry);
5177
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5232
+ if (fileEntry.zip64ExtraFieldMissing) {
5233
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5234
+ }
5235
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5178
5236
  entry.arrayBuffer = async options => {
5179
5237
  const writer = new TransformStream();
5180
5238
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5181
5239
  arrayBufferPromise.catch(() => { });
5182
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5240
+ await fileEntry.getData(writer, entry, zipReader,
5183
5241
  Object.assign({}, options, { preventClose: false }));
5184
5242
  return arrayBufferPromise;
5185
5243
  };
@@ -5226,10 +5284,10 @@ class ZipReader {
5226
5284
  const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
5227
5285
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
5228
5286
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
5229
- if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
5287
+ if (checkAmbiguity && (prependedDataLength > 0 || (filesLength && startOffset > splitZipSignatureLength))) {
5230
5288
  throwAmbiguousArchive(WARNING_PREPENDED_DATA);
5231
5289
  }
5232
- if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5290
+ if (prependedDataLength > 0 || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5233
5291
  addWarning(warnings, WARNING_PREPENDED_DATA);
5234
5292
  }
5235
5293
  if (prependedCentralDirectory) {
@@ -5387,8 +5445,12 @@ let ZipEntry$1 = class ZipEntry {
5387
5445
  });
5388
5446
  }
5389
5447
 
5390
- async getData(writer, fileEntry, readRanges, options = {}) {
5448
+ async getData(writer, fileEntry, zipReader, options = {}) {
5391
5449
  const zipEntry = this;
5450
+ const { readRanges, directoryOffset } = zipReader;
5451
+ if (zipEntry.zip64ExtraFieldMissing) {
5452
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5453
+ }
5392
5454
  const config = getConfiguration();
5393
5455
  const {
5394
5456
  reader,
@@ -5459,10 +5521,10 @@ let ZipEntry$1 = class ZipEntry {
5459
5521
  }
5460
5522
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5461
5523
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5462
- if (lastAccessDate) {
5524
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5463
5525
  fileEntry.lastAccessDate = lastAccessDate;
5464
5526
  }
5465
- if (creationDate) {
5527
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5466
5528
  fileEntry.creationDate = creationDate;
5467
5529
  }
5468
5530
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5491,7 +5553,7 @@ let ZipEntry$1 = class ZipEntry {
5491
5553
  throw new Error(ERR_ENCRYPTED);
5492
5554
  }
5493
5555
  }
5494
- if (dataOffset + compressedSize > reader.size) {
5556
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5495
5557
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5496
5558
  }
5497
5559
  const size = compressedSize;
@@ -5557,6 +5619,7 @@ let ZipEntry$1 = class ZipEntry {
5557
5619
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5558
5620
  readRanges
5559
5621
  });
5622
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5560
5623
  }
5561
5624
  let writable, abortError, aborted;
5562
5625
  try {
@@ -5576,10 +5639,7 @@ let ZipEntry$1 = class ZipEntry {
5576
5639
  writer.size += writtenSize;
5577
5640
  }
5578
5641
  } catch (error) {
5579
- const { outputSize: failedOutputSize } = workerOptions;
5580
- if (failedOutputSize !== UNDEFINED_VALUE) {
5581
- writer.size += failedOutputSize;
5582
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5642
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5583
5643
  writer.size += error.outputSize;
5584
5644
  }
5585
5645
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5607,13 +5667,7 @@ let ZipEntry$1 = class ZipEntry {
5607
5667
  };
5608
5668
 
5609
5669
  function detectEncryptedCentralDirectory(directoryView) {
5610
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5611
- for (let offset = 0; offset < maxOffset; offset++) {
5612
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5613
- return true;
5614
- }
5615
- }
5616
- return false;
5670
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5617
5671
  }
5618
5672
 
5619
5673
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5626,6 +5680,70 @@ function getWrappedFilesLength(directoryView, directoryArray, offset) {
5626
5680
  return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
5627
5681
  }
5628
5682
 
5683
+ function pointsAtDirectory(reader, offset) {
5684
+ return startsWithSignature(reader, offset, CENTRAL_FILE_HEADER_SIGNATURE);
5685
+ }
5686
+
5687
+ async function startsWithSignature(reader, offset, signature) {
5688
+ if (offset < 0 || offset + 4 > reader.size) {
5689
+ return false;
5690
+ }
5691
+ const signatureArray = await readUint8Array(reader, offset, 4);
5692
+ return getUint32$1(getDataView(signatureArray), 0) == signature;
5693
+ }
5694
+
5695
+ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5696
+ const startOffset = Math.max(0, endOffset - (6 + MAX_16_BITS));
5697
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5698
+ const tailView = getDataView(tailArray);
5699
+ for (let indexByte = tailArray.length - 6; indexByte >= 0; indexByte--) {
5700
+ if (getUint32$1(tailView, indexByte) == DIGITAL_SIGNATURE_RECORD_SIGNATURE &&
5701
+ indexByte + 6 + getUint16$1(tailView, indexByte + 4) == tailArray.length) {
5702
+ return startOffset + indexByte;
5703
+ }
5704
+ }
5705
+ }
5706
+
5707
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5708
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5709
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5710
+ const tailView = getDataView(tailArray);
5711
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5712
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5713
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5714
+ return startOffset + indexByte;
5715
+ }
5716
+ }
5717
+ }
5718
+
5719
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5720
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5721
+ return UNDEFINED_VALUE;
5722
+ }
5723
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5724
+ if (localHeaderOffset != MAX_32_BITS) {
5725
+ return localHeaderOffset;
5726
+ }
5727
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5728
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5729
+ while (offsetExtraField + 4 <= endExtraField) {
5730
+ const type = getUint16$1(directoryView, offsetExtraField);
5731
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5732
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5733
+ let offsetValue = offsetExtraField + 4;
5734
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5735
+ offsetValue += 8;
5736
+ }
5737
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5738
+ offsetValue += 8;
5739
+ }
5740
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5741
+ }
5742
+ offsetExtraField += 4 + size;
5743
+ }
5744
+ return UNDEFINED_VALUE;
5745
+ }
5746
+
5629
5747
  function readDigitalSignature(signatureRecordArray) {
5630
5748
  if (signatureRecordArray.length >= 6) {
5631
5749
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5715,6 +5833,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5715
5833
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5716
5834
  });
5717
5835
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5836
+ let extraFieldZip64Missing;
5718
5837
  if (extraFieldZip64) {
5719
5838
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5720
5839
  malformedExtraField = true;
@@ -5724,7 +5843,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5724
5843
  if (localDirectory) {
5725
5844
  malformedExtraField = true;
5726
5845
  } else {
5727
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5846
+ extraFieldZip64Missing = true;
5728
5847
  }
5729
5848
  }
5730
5849
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5757,6 +5876,11 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5757
5876
  readExtraFieldUnixDates(extraFieldUnixType1, directory);
5758
5877
  directory.extraFieldUnixType1 = extraFieldUnixType1;
5759
5878
  }
5879
+ const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5880
+ if (extraFieldExtendedTimestamp) {
5881
+ readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5882
+ directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5883
+ }
5760
5884
  const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS);
5761
5885
  if (extraFieldNTFS) {
5762
5886
  readExtraFieldNTFS(extraFieldNTFS, directory);
@@ -5773,15 +5897,13 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5773
5897
  readExtraFieldUnix(extraFieldUnix, unixIdsRead ? {} : directory, false);
5774
5898
  directory.extraFieldUnix = extraFieldUnix;
5775
5899
  }
5776
- const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5777
- if (extraFieldExtendedTimestamp) {
5778
- readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5779
- directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5780
- }
5781
5900
  const extraFieldUSDZ = extraField.get(EXTRAFIELD_TYPE_USDZ);
5782
5901
  if (extraFieldUSDZ) {
5783
5902
  directory.extraFieldUSDZ = extraFieldUSDZ;
5784
5903
  }
5904
+ if (extraFieldZip64Missing) {
5905
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5906
+ }
5785
5907
  return malformedExtraField;
5786
5908
  }
5787
5909
 
@@ -5996,13 +6118,7 @@ async function detectOverlappingEntry({
5996
6118
  let dataDescriptorLength = 0;
5997
6119
  if (dataDescriptor) {
5998
6120
  const zip64 = Boolean(extraFieldZip64);
5999
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
6000
- const dataDescriptorView = getDataView(dataDescriptorArray);
6001
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6002
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6003
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6004
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6005
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6121
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
6006
6122
  if (localDataDescriptor) {
6007
6123
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6008
6124
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6072,6 +6188,25 @@ function mergeRanges(sortedRanges, pendingRanges) {
6072
6188
  return mergedRanges;
6073
6189
  }
6074
6190
 
6191
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6192
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6193
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6194
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6195
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6196
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6197
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6198
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6199
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6200
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6201
+ }
6202
+
6203
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6204
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6205
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6206
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6207
+ }
6208
+ }
6209
+
6075
6210
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6076
6211
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6077
6212
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6331,6 +6466,18 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
6331
6466
  }
6332
6467
  }
6333
6468
 
6469
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6470
+ const { dataDescriptor } = localDirectory;
6471
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6472
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6473
+ if (dataDescriptor && !maskedLocalDirectory &&
6474
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6475
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6476
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6477
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6478
+ }
6479
+ }
6480
+
6334
6481
  function reportAmbiguity(reject, warnings, reason) {
6335
6482
  if (reject) {
6336
6483
  throwAmbiguousArchive(reason);
@@ -6416,11 +6563,13 @@ var zipReader = /*#__PURE__*/Object.freeze({
6416
6563
  WARNING_COMPRESSED_PATCHED_DATA: WARNING_COMPRESSED_PATCHED_DATA,
6417
6564
  WARNING_DUPLICATE_FILENAME: WARNING_DUPLICATE_FILENAME,
6418
6565
  WARNING_MALFORMED_EXTRA_FIELD: WARNING_MALFORMED_EXTRA_FIELD,
6566
+ WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET: WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET,
6419
6567
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG: WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
6420
6568
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD: WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
6421
6569
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES: WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES,
6422
6570
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME: WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
6423
6571
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY: WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
6572
+ WARNING_MISSING_ZIP64_EXTRA_FIELD: WARNING_MISSING_ZIP64_EXTRA_FIELD,
6424
6573
  WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY: WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
6425
6574
  WARNING_PREPENDED_CENTRAL_DIRECTORY: WARNING_PREPENDED_CENTRAL_DIRECTORY,
6426
6575
  WARNING_PREPENDED_DATA: WARNING_PREPENDED_DATA,
@@ -6431,6 +6580,7 @@ var zipReader = /*#__PURE__*/Object.freeze({
6431
6580
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6432
6581
  ZipReader: ZipReader,
6433
6582
  ZipReaderStream: ZipReaderStream,
6583
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6434
6584
  isZipFile: isZipFile
6435
6585
  });
6436
6586
 
@@ -6553,130 +6703,8 @@ class ZipWriter {
6553
6703
  return watchPromiseError(this, prependZipEntries(this, reader));
6554
6704
  }
6555
6705
 
6556
- appendZip(reader) {
6557
- return watchPromiseError(this, this.appendZipEntries(reader));
6558
- }
6559
-
6560
- async appendZipEntries(reader) {
6561
- const zipWriter = this;
6562
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6563
- while (pendingAddFileCalls.size) {
6564
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6565
- }
6566
- let resolveAppendZip;
6567
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6568
- pendingAddFileCalls.add(promiseAppendZip);
6569
- const appendedFilenames = [];
6570
- let releaseLockWriter;
6571
- try {
6572
- reader = new GenericReader(reader);
6573
- await initStream(reader);
6574
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6575
- reader = new BlobReader(await streamToBlob(reader.readable));
6576
- await initStream(reader);
6577
- }
6578
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6579
- const zipReader$1 = new ZipReader(reader);
6580
- const entries = await zipReader$1.getEntries();
6581
- await zipReader$1.close();
6582
- await initStream(zipWriter.writer);
6583
- const { directoryOffset } = zipReader$1;
6584
- entries.forEach(({ filename }) => {
6585
- if (filenames.has(filename)) {
6586
- throw new Error(ERR_DUPLICATED_NAME);
6587
- }
6588
- filenames.add(filename);
6589
- appendedFilenames.push(filename);
6590
- });
6591
- zipWriter.writerLocked = true;
6592
- const { lockWriter } = zipWriter;
6593
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6594
- zipWriter.writerLocked = false;
6595
- resolve();
6596
- });
6597
- await lockWriter;
6598
- if (zipWriter.addSplitZipSignature) {
6599
- delete zipWriter.addSplitZipSignature;
6600
- if (!await startsWithSplitZipSignature(reader)) {
6601
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6602
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6603
- }
6604
- }
6605
- const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
6606
- entries.forEach(entry => {
6607
- const {
6608
- version,
6609
- rawLastModDate,
6610
- rawFilename,
6611
- bitFlag,
6612
- encrypted,
6613
- uncompressedSize,
6614
- compressedSize,
6615
- extraFieldZip64
6616
- } = entry;
6617
- let {
6618
- compressionMethod,
6619
- rawExtraField,
6620
- } = entry;
6621
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6622
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6623
- if (entry.extraFieldAES) {
6624
- compressionMethod = COMPRESSION_METHOD_AES;
6625
- }
6626
- const extraFieldLength = getLength(rawExtraField);
6627
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6628
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6629
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6630
- const {
6631
- headerArray,
6632
- headerView
6633
- } = getHeaderArrayData({
6634
- version,
6635
- bitFlag: bitFlagValue,
6636
- compressionMethod,
6637
- uncompressedSize,
6638
- compressedSize,
6639
- rawLastModDate,
6640
- rawFilename,
6641
- zip64CompressedSize,
6642
- zip64UncompressedSize,
6643
- extraFieldLength
6644
- });
6645
- const { crc32 } = entry;
6646
- if (crc32 !== UNDEFINED_VALUE) {
6647
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6648
- }
6649
- const { offset, diskNumberStart } = entryPositions.get(entry);
6650
- Object.assign(entry, {
6651
- zip64Enabled: true,
6652
- zip64UncompressedSize,
6653
- zip64CompressedSize,
6654
- offset,
6655
- diskNumberStart,
6656
- zip64DiskNumberStart: false,
6657
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6658
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6659
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6660
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6661
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6662
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6663
- rawExtraField,
6664
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6665
- headerArray,
6666
- headerView
6667
- });
6668
- fileEntries.set(entry.filename, entry);
6669
- });
6670
- } catch (error) {
6671
- appendedFilenames.forEach(filename => filenames.delete(filename));
6672
- throw error;
6673
- } finally {
6674
- resolveAppendZip();
6675
- pendingAddFileCalls.delete(promiseAppendZip);
6676
- if (releaseLockWriter) {
6677
- releaseLockWriter();
6678
- }
6679
- }
6706
+ appendZip(reader, options = {}) {
6707
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6680
6708
  }
6681
6709
 
6682
6710
  add(name = "", reader, options = {}) {
@@ -6838,11 +6866,140 @@ function watchPromiseError(zipWriter, promise) {
6838
6866
  return watchedPromise;
6839
6867
  }
6840
6868
 
6869
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6870
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6871
+ const filter = checkFunctionOption(options.filter);
6872
+ while (pendingAddFileCalls.size) {
6873
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6874
+ }
6875
+ let resolveAppendZip;
6876
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6877
+ pendingAddFileCalls.add(promiseAppendZip);
6878
+ const appendedFilenames = [];
6879
+ let releaseLockWriter;
6880
+ try {
6881
+ reader = new GenericReader(reader);
6882
+ await initStream(reader);
6883
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6884
+ reader = new BlobReader(await streamToBlob(reader.readable));
6885
+ await initStream(reader);
6886
+ }
6887
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6888
+ const zipReader$1 = new ZipReader(reader);
6889
+ const entries = await zipReader$1.getEntries();
6890
+ const keptEntries = [];
6891
+ for (const entry of entries) {
6892
+ if (!filter || await filter(entry)) {
6893
+ keptEntries.push(entry);
6894
+ }
6895
+ }
6896
+ await zipReader$1.close();
6897
+ await initStream(zipWriter.writer);
6898
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6899
+ keptEntries.forEach(entry => {
6900
+ const { filename } = entry;
6901
+ if (entriesMissingZip64ExtraField.has(entry)) {
6902
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6903
+ }
6904
+ if (filenames.has(filename)) {
6905
+ throw new Error(ERR_DUPLICATED_NAME);
6906
+ }
6907
+ filenames.add(filename);
6908
+ appendedFilenames.push(filename);
6909
+ });
6910
+ zipWriter.writerLocked = true;
6911
+ const { lockWriter } = zipWriter;
6912
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6913
+ zipWriter.writerLocked = false;
6914
+ resolve();
6915
+ });
6916
+ await lockWriter;
6917
+ if (zipWriter.addSplitZipSignature) {
6918
+ delete zipWriter.addSplitZipSignature;
6919
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6920
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6921
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6922
+ }
6923
+ }
6924
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6925
+ keptEntries.forEach(entry => {
6926
+ const {
6927
+ version,
6928
+ rawLastModDate,
6929
+ rawFilename,
6930
+ rawBitFlag,
6931
+ uncompressedSize,
6932
+ compressedSize,
6933
+ extraFieldZip64
6934
+ } = entry;
6935
+ let {
6936
+ compressionMethod,
6937
+ rawExtraField,
6938
+ } = entry;
6939
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6940
+ if (entry.extraFieldAES) {
6941
+ compressionMethod = COMPRESSION_METHOD_AES;
6942
+ }
6943
+ const extraFieldLength = getLength(rawExtraField);
6944
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6945
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6946
+ const {
6947
+ headerArray,
6948
+ headerView
6949
+ } = getHeaderArrayData({
6950
+ version,
6951
+ bitFlag: rawBitFlag,
6952
+ compressionMethod,
6953
+ uncompressedSize,
6954
+ compressedSize,
6955
+ rawLastModDate,
6956
+ rawFilename,
6957
+ zip64CompressedSize,
6958
+ zip64UncompressedSize,
6959
+ extraFieldLength
6960
+ });
6961
+ const { crc32 } = entry;
6962
+ if (crc32 !== UNDEFINED_VALUE) {
6963
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6964
+ }
6965
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6966
+ Object.assign(entry, {
6967
+ zip64Enabled: true,
6968
+ zip64UncompressedSize,
6969
+ zip64CompressedSize,
6970
+ offset,
6971
+ diskNumberStart,
6972
+ zip64DiskNumberStart: false,
6973
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6974
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6975
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6976
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6977
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6978
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6979
+ rawExtraField,
6980
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6981
+ headerArray,
6982
+ headerView
6983
+ });
6984
+ fileEntries.set(entry.filename, entry);
6985
+ });
6986
+ } catch (error) {
6987
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6988
+ throw error;
6989
+ } finally {
6990
+ resolveAppendZip();
6991
+ pendingAddFileCalls.delete(promiseAppendZip);
6992
+ if (releaseLockWriter) {
6993
+ releaseLockWriter();
6994
+ }
6995
+ }
6996
+ }
6997
+
6841
6998
  async function prependZipEntries(zipWriter, reader) {
6842
6999
  if (zipWriter.filenames.size) {
6843
7000
  throw new Error(ERR_ZIP_NOT_EMPTY);
6844
7001
  }
6845
- await zipWriter.appendZipEntries(reader);
7002
+ await appendZipEntries(zipWriter, reader);
6846
7003
  }
6847
7004
 
6848
7005
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8614,26 +8771,63 @@ function removeExtraFieldZip64(rawExtraField) {
8614
8771
  return rawExtraField;
8615
8772
  }
8616
8773
 
8617
- async function copyZipData(zipWriter, reader, entries, directoryOffset) {
8774
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8618
8775
  const { writer } = zipWriter;
8619
8776
  const entryPositions = new Map();
8620
- if (writer.closeDisk) {
8777
+ if (writer.closeDisk || compact) {
8621
8778
  const sortedEntries = Array.from(entries).sort((firstEntry, secondEntry) =>
8622
8779
  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;
8780
+ const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8781
+ const keptEntrySet = new Set(keptEntries);
8782
+ const localHeaderLengths = new Map();
8783
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8784
+ const entry = sortedEntries[indexEntry];
8785
+ if (keptEntrySet.has(entry)) {
8786
+ const sourceOffset = getSourceOffset(reader, entry);
8787
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8788
+ if (localHeaderLength === UNDEFINED_VALUE) {
8789
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8790
+ }
8791
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8792
+ if (entry.bitFlag.dataDescriptor) {
8793
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8794
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8795
+ }
8796
+ if (entryEnd > regionEnds[indexEntry]) {
8797
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8798
+ }
8799
+ regionEnds[indexEntry] = entryEnd;
8800
+ localHeaderLengths.set(entry, localHeaderLength);
8801
+ }
8802
+ }
8803
+ let copiedOffset = 0;
8804
+ let lastPosition;
8805
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8806
+ const entry = sortedEntries[indexEntry];
8807
+ if (keptEntrySet.has(entry)) {
8808
+ const sourceOffset = getSourceOffset(reader, entry);
8809
+ if (sourceOffset < copiedOffset) {
8810
+ entryPositions.set(entry, lastPosition);
8811
+ } else {
8812
+ if (!compact) {
8813
+ await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8814
+ }
8815
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8816
+ await writer.closeDisk();
8817
+ }
8818
+ lastPosition = {
8819
+ offset: getSegmentOffset(zipWriter, writer),
8820
+ diskNumberStart: getDiskNumber(writer)
8821
+ };
8822
+ entryPositions.set(entry, lastPosition);
8823
+ await copyData(zipWriter, reader, sourceOffset, regionEnds[indexEntry] - sourceOffset);
8824
+ copiedOffset = regionEnds[indexEntry];
8825
+ }
8826
+ }
8827
+ }
8828
+ if (!compact) {
8829
+ await copyData(zipWriter, reader, copiedOffset, directoryOffset - copiedOffset);
8635
8830
  }
8636
- await copyData(zipWriter, reader, copiedLength, directoryOffset - copiedLength);
8637
8831
  } else {
8638
8832
  const baseOffset = zipWriter.offset;
8639
8833
  await copyData(zipWriter, reader, 0, directoryOffset);
@@ -8645,6 +8839,20 @@ async function copyZipData(zipWriter, reader, entries, directoryOffset) {
8645
8839
  return entryPositions;
8646
8840
  }
8647
8841
 
8842
+ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8843
+ const regionEnds = new Array(sortedEntries.length);
8844
+ let regionEnd = directoryOffset;
8845
+ for (let indexEntry = sortedEntries.length - 1; indexEntry >= 0; indexEntry--) {
8846
+ const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8847
+ const nextEntry = sortedEntries[indexEntry + 1];
8848
+ if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8849
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8850
+ }
8851
+ regionEnds[indexEntry] = regionEnd;
8852
+ }
8853
+ return regionEnds;
8854
+ }
8855
+
8648
8856
  async function copyData(zipWriter, reader, offset, size) {
8649
8857
  if (size > 0) {
8650
8858
  const { writer } = zipWriter;
@@ -8652,11 +8860,13 @@ async function copyData(zipWriter, reader, offset, size) {
8652
8860
  try {
8653
8861
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8654
8862
  } catch (error) {
8655
- zipWriter.hasCorruptedEntries = true;
8656
- try {
8657
- error.corruptedEntry = true;
8658
- } catch {
8659
- // ignored
8863
+ if (copiedLength) {
8864
+ zipWriter.hasCorruptedEntries = true;
8865
+ try {
8866
+ error.corruptedEntry = true;
8867
+ } catch {
8868
+ // ignored
8869
+ }
8660
8870
  }
8661
8871
  throw error;
8662
8872
  } finally {
@@ -8667,11 +8877,16 @@ async function copyData(zipWriter, reader, offset, size) {
8667
8877
  }
8668
8878
 
8669
8879
  async function getLocalHeaderLength(reader, offset) {
8670
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8880
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8881
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8882
+ EMPTY_UINT8_ARRAY;
8671
8883
  if (getLength(headerArray) < HEADER_SIZE) {
8672
- return HEADER_SIZE;
8884
+ return UNDEFINED_VALUE;
8673
8885
  }
8674
8886
  const headerView = getDataView(headerArray);
8887
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8888
+ return UNDEFINED_VALUE;
8889
+ }
8675
8890
  return HEADER_SIZE +
8676
8891
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8677
8892
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9022,7 +9237,7 @@ function formatSupported(StreamClass, format) {
9022
9237
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9023
9238
  */
9024
9239
 
9025
- const VERSION = "2.19.0";
9240
+ const VERSION = "2.21.0";
9026
9241
 
9027
9242
  /*
9028
9243
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -11762,11 +11977,13 @@ exports.WARNING_COMPRESSED_PATCHED_DATA = WARNING_COMPRESSED_PATCHED_DATA;
11762
11977
  exports.WARNING_COMPRESSION_UNAVAILABLE = WARNING_COMPRESSION_UNAVAILABLE;
11763
11978
  exports.WARNING_DUPLICATE_FILENAME = WARNING_DUPLICATE_FILENAME;
11764
11979
  exports.WARNING_MALFORMED_EXTRA_FIELD = WARNING_MALFORMED_EXTRA_FIELD;
11980
+ exports.WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET;
11765
11981
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG;
11766
11982
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD;
11767
11983
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES;
11768
11984
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME;
11769
11985
  exports.WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY;
11986
+ exports.WARNING_MISSING_ZIP64_EXTRA_FIELD = WARNING_MISSING_ZIP64_EXTRA_FIELD;
11770
11987
  exports.WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY;
11771
11988
  exports.WARNING_PREPENDED_CENTRAL_DIRECTORY = WARNING_PREPENDED_CENTRAL_DIRECTORY;
11772
11989
  exports.WARNING_PREPENDED_DATA = WARNING_PREPENDED_DATA;