@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-native.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";
@@ -4771,12 +4775,9 @@ const ERR_BAD_FORMAT = "File format is not recognized";
4771
4775
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4772
4776
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4773
4777
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4774
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4775
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4776
4778
  const ERR_ENCRYPTED = "File contains encrypted entry";
4777
4779
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4778
4780
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4779
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4780
4781
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4781
4782
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4782
4783
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4795,6 +4796,8 @@ const WARNING_APPENDED_DATA = "appended data";
4795
4796
  const WARNING_PREPENDED_DATA = "prepended data";
4796
4797
  const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
4797
4798
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
4799
+ const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = "mismatched central directory offset";
4800
+ const WARNING_MISSING_ZIP64_EXTRA_FIELD = "missing zip64 extra field";
4798
4801
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
4799
4802
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
4800
4803
  const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
@@ -4839,7 +4842,8 @@ class ZipReader {
4839
4842
  Object.assign(this, {
4840
4843
  reader: new GenericReader(reader),
4841
4844
  options,
4842
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4845
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4846
+ entriesMissingZip64ExtraField: new WeakSet()
4843
4847
  });
4844
4848
  }
4845
4849
 
@@ -4910,16 +4914,22 @@ class ZipReader {
4910
4914
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4911
4915
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4912
4916
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4913
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4914
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4915
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4916
- const originalDirectoryDataOffset = directoryDataOffset;
4917
- directoryDataOffset = expectedDirectoryDataOffset;
4918
- if (directoryDataOffset > originalDirectoryDataOffset) {
4919
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4917
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4918
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4919
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4920
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4921
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4922
+ }
4923
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4924
+ directoryDataOffset != expectedDirectoryDataOffset) {
4925
+ const originalDirectoryDataOffset = directoryDataOffset;
4926
+ directoryDataOffset = expectedDirectoryDataOffset;
4927
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4928
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4929
+ }
4930
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4931
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4920
4932
  }
4921
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4922
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4923
4933
  }
4924
4934
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4925
4935
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4957,15 +4967,13 @@ class ZipReader {
4957
4967
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4958
4968
  }
4959
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);
4960
4972
  }
4961
4973
  }
4962
4974
  let declaredDirectoryDataLength = directoryDataLength;
4963
4975
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4964
4976
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4965
- if (directoryDataOffset >= reader.size) {
4966
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4967
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4968
- }
4969
4977
  if (expectedLastDiskNumber != lastDiskNumber) {
4970
4978
  throw new Error(ERR_SPLIT_ZIP_FILE);
4971
4979
  }
@@ -4973,33 +4981,64 @@ class ZipReader {
4973
4981
  throw new Error(ERR_BAD_FORMAT);
4974
4982
  }
4975
4983
  let offset = 0;
4976
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4984
+ let directoryArray = directoryDataOffset < reader.size ?
4985
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
4986
+ EMPTY_UINT8_ARRAY;
4977
4987
  let directoryView = getDataView(directoryArray);
4978
4988
  if (directoryDataLength) {
4979
- if (directoryArray.length < 4) {
4980
- throw new Error(ERR_BAD_FORMAT);
4981
- }
4982
- const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4989
+ let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4983
4990
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4984
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4991
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
4992
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4985
4993
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4986
- detectEncryptedCentralDirectory(directoryView);
4994
+ detectEncryptedCentralDirectory(directoryView));
4995
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
4996
+ throw new Error(ERR_BAD_FORMAT);
4997
+ }
4987
4998
  let reconcile = !storedPointsAtDirectory;
4988
- if (!reconcile && expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4989
- const expectedSignatureArray = await readUint8Array(reader, expectedDirectoryDataOffset, 4);
4990
- reconcile = getUint32$1(getDataView(expectedSignatureArray), 0) == CENTRAL_FILE_HEADER_SIGNATURE;
4999
+ if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
5000
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5001
+ if (!expectedPointsAtDirectory) {
5002
+ const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
5003
+ const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
5004
+ if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5005
+ await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5006
+ expectedDirectoryDataOffset = signedDirectoryDataOffset;
5007
+ expectedPointsAtDirectory = true;
5008
+ }
5009
+ }
5010
+ if (storedPointsAtDirectory) {
5011
+ reconcile = expectedPointsAtDirectory;
5012
+ }
4991
5013
  }
4992
- if (reconcile) {
5014
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
4993
5015
  const originalDirectoryDataOffset = directoryDataOffset;
4994
5016
  directoryDataOffset = expectedDirectoryDataOffset;
4995
- if (directoryDataOffset > originalDirectoryDataOffset) {
4996
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
4997
- prependedCentralDirectory = storedPointsAtDirectory;
4998
- }
4999
5017
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5000
5018
  directoryView = getDataView(directoryArray);
5019
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5020
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5021
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5022
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5023
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5024
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5025
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5026
+ prependedDataLength += offsetDelta;
5027
+ prependedCentralDirectory = storedPointsAtDirectory;
5028
+ } else {
5029
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5030
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5031
+ prependedDataLength += offsetDelta;
5032
+ }
5033
+ }
5001
5034
  }
5002
5035
  }
5036
+ if (directoryArray.length < 4) {
5037
+ throw new Error(ERR_BAD_FORMAT);
5038
+ }
5039
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5040
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5041
+ directoryDataOffset = centralDirectoryEndOffset;
5003
5042
  }
5004
5043
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5005
5044
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5041,8 +5080,10 @@ class ZipReader {
5041
5080
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5042
5081
  }
5043
5082
  }
5083
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5084
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5044
5085
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5045
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5086
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5046
5087
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5047
5088
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5048
5089
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5108,7 +5149,16 @@ class ZipReader {
5108
5149
  filename,
5109
5150
  comment
5110
5151
  });
5111
- const malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5152
+ let malformedExtraField;
5153
+ try {
5154
+ malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5155
+ } catch (error) {
5156
+ if (checkAmbiguity || error.message != ERR_EXTRAFIELD_ZIP64_NOT_FOUND) {
5157
+ throw error;
5158
+ }
5159
+ fileEntry.zip64ExtraFieldMissing = true;
5160
+ addWarning(warnings, WARNING_MISSING_ZIP64_EXTRA_FIELD, fileEntry.filename);
5161
+ }
5112
5162
  filename = fileEntry.filename;
5113
5163
  if (normalizeFilename) {
5114
5164
  const normalizedFilename = normalizeFilename(filename);
@@ -5124,13 +5174,17 @@ class ZipReader {
5124
5174
  if (malformedExtraField) {
5125
5175
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5126
5176
  }
5127
- fileEntry.offset += prependedDataLength;
5128
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5129
- startOffset = Math.min(entryPosition, startOffset);
5130
- if (entryPosition < previousEntryPosition) {
5131
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5177
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5178
+ startOffset = 0;
5179
+ } else {
5180
+ fileEntry.offset += prependedDataLength;
5181
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5182
+ startOffset = Math.min(entryPosition, startOffset);
5183
+ if (entryPosition < previousEntryPosition) {
5184
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5185
+ }
5186
+ previousEntryPosition = entryPosition;
5132
5187
  }
5133
- previousEntryPosition = entryPosition;
5134
5188
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5135
5189
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5136
5190
  }
@@ -5150,11 +5204,12 @@ class ZipReader {
5150
5204
  const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
5151
5205
  const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
5152
5206
  const symlink = (unixType & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK;
5153
- const executable = !symlink && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5154
- ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5155
- : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5156
5207
  const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5157
5208
  const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5209
+ const directory = modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE);
5210
+ const executable = !symlink && !directory && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5211
+ ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5212
+ : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5158
5213
  Object.assign(fileEntry, {
5159
5214
  setuid,
5160
5215
  setgid,
@@ -5162,16 +5217,19 @@ class ZipReader {
5162
5217
  symlink,
5163
5218
  unixExternalUpper,
5164
5219
  executable,
5165
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
5220
+ directory,
5166
5221
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5167
5222
  });
5168
5223
  const entry = new Entry(fileEntry);
5169
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5224
+ if (fileEntry.zip64ExtraFieldMissing) {
5225
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5226
+ }
5227
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5170
5228
  entry.arrayBuffer = async options => {
5171
5229
  const writer = new TransformStream();
5172
5230
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5173
5231
  arrayBufferPromise.catch(() => { });
5174
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5232
+ await fileEntry.getData(writer, entry, zipReader,
5175
5233
  Object.assign({}, options, { preventClose: false }));
5176
5234
  return arrayBufferPromise;
5177
5235
  };
@@ -5218,10 +5276,10 @@ class ZipReader {
5218
5276
  const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
5219
5277
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
5220
5278
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
5221
- if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
5279
+ if (checkAmbiguity && (prependedDataLength > 0 || (filesLength && startOffset > splitZipSignatureLength))) {
5222
5280
  throwAmbiguousArchive(WARNING_PREPENDED_DATA);
5223
5281
  }
5224
- if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5282
+ if (prependedDataLength > 0 || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5225
5283
  addWarning(warnings, WARNING_PREPENDED_DATA);
5226
5284
  }
5227
5285
  if (prependedCentralDirectory) {
@@ -5379,8 +5437,12 @@ let ZipEntry$1 = class ZipEntry {
5379
5437
  });
5380
5438
  }
5381
5439
 
5382
- async getData(writer, fileEntry, readRanges, options = {}) {
5440
+ async getData(writer, fileEntry, zipReader, options = {}) {
5383
5441
  const zipEntry = this;
5442
+ const { readRanges, directoryOffset } = zipReader;
5443
+ if (zipEntry.zip64ExtraFieldMissing) {
5444
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5445
+ }
5384
5446
  const config = getConfiguration();
5385
5447
  const {
5386
5448
  reader,
@@ -5451,10 +5513,10 @@ let ZipEntry$1 = class ZipEntry {
5451
5513
  }
5452
5514
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5453
5515
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5454
- if (lastAccessDate) {
5516
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5455
5517
  fileEntry.lastAccessDate = lastAccessDate;
5456
5518
  }
5457
- if (creationDate) {
5519
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5458
5520
  fileEntry.creationDate = creationDate;
5459
5521
  }
5460
5522
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5483,7 +5545,7 @@ let ZipEntry$1 = class ZipEntry {
5483
5545
  throw new Error(ERR_ENCRYPTED);
5484
5546
  }
5485
5547
  }
5486
- if (dataOffset + compressedSize > reader.size) {
5548
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5487
5549
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5488
5550
  }
5489
5551
  const size = compressedSize;
@@ -5549,6 +5611,7 @@ let ZipEntry$1 = class ZipEntry {
5549
5611
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5550
5612
  readRanges
5551
5613
  });
5614
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5552
5615
  }
5553
5616
  let writable, abortError, aborted;
5554
5617
  try {
@@ -5568,10 +5631,7 @@ let ZipEntry$1 = class ZipEntry {
5568
5631
  writer.size += writtenSize;
5569
5632
  }
5570
5633
  } catch (error) {
5571
- const { outputSize: failedOutputSize } = workerOptions;
5572
- if (failedOutputSize !== UNDEFINED_VALUE) {
5573
- writer.size += failedOutputSize;
5574
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5634
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5575
5635
  writer.size += error.outputSize;
5576
5636
  }
5577
5637
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5599,13 +5659,7 @@ let ZipEntry$1 = class ZipEntry {
5599
5659
  };
5600
5660
 
5601
5661
  function detectEncryptedCentralDirectory(directoryView) {
5602
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5603
- for (let offset = 0; offset < maxOffset; offset++) {
5604
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5605
- return true;
5606
- }
5607
- }
5608
- return false;
5662
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5609
5663
  }
5610
5664
 
5611
5665
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5618,6 +5672,70 @@ function getWrappedFilesLength(directoryView, directoryArray, offset) {
5618
5672
  return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
5619
5673
  }
5620
5674
 
5675
+ function pointsAtDirectory(reader, offset) {
5676
+ return startsWithSignature(reader, offset, CENTRAL_FILE_HEADER_SIGNATURE);
5677
+ }
5678
+
5679
+ async function startsWithSignature(reader, offset, signature) {
5680
+ if (offset < 0 || offset + 4 > reader.size) {
5681
+ return false;
5682
+ }
5683
+ const signatureArray = await readUint8Array(reader, offset, 4);
5684
+ return getUint32$1(getDataView(signatureArray), 0) == signature;
5685
+ }
5686
+
5687
+ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5688
+ const startOffset = Math.max(0, endOffset - (6 + MAX_16_BITS));
5689
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5690
+ const tailView = getDataView(tailArray);
5691
+ for (let indexByte = tailArray.length - 6; indexByte >= 0; indexByte--) {
5692
+ if (getUint32$1(tailView, indexByte) == DIGITAL_SIGNATURE_RECORD_SIGNATURE &&
5693
+ indexByte + 6 + getUint16$1(tailView, indexByte + 4) == tailArray.length) {
5694
+ return startOffset + indexByte;
5695
+ }
5696
+ }
5697
+ }
5698
+
5699
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5700
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5701
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5702
+ const tailView = getDataView(tailArray);
5703
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5704
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5705
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5706
+ return startOffset + indexByte;
5707
+ }
5708
+ }
5709
+ }
5710
+
5711
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5712
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5713
+ return UNDEFINED_VALUE;
5714
+ }
5715
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5716
+ if (localHeaderOffset != MAX_32_BITS) {
5717
+ return localHeaderOffset;
5718
+ }
5719
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5720
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5721
+ while (offsetExtraField + 4 <= endExtraField) {
5722
+ const type = getUint16$1(directoryView, offsetExtraField);
5723
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5724
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5725
+ let offsetValue = offsetExtraField + 4;
5726
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5727
+ offsetValue += 8;
5728
+ }
5729
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5730
+ offsetValue += 8;
5731
+ }
5732
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5733
+ }
5734
+ offsetExtraField += 4 + size;
5735
+ }
5736
+ return UNDEFINED_VALUE;
5737
+ }
5738
+
5621
5739
  function readDigitalSignature(signatureRecordArray) {
5622
5740
  if (signatureRecordArray.length >= 6) {
5623
5741
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5707,6 +5825,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5707
5825
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5708
5826
  });
5709
5827
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5828
+ let extraFieldZip64Missing;
5710
5829
  if (extraFieldZip64) {
5711
5830
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5712
5831
  malformedExtraField = true;
@@ -5716,7 +5835,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5716
5835
  if (localDirectory) {
5717
5836
  malformedExtraField = true;
5718
5837
  } else {
5719
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5838
+ extraFieldZip64Missing = true;
5720
5839
  }
5721
5840
  }
5722
5841
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5749,6 +5868,11 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5749
5868
  readExtraFieldUnixDates(extraFieldUnixType1, directory);
5750
5869
  directory.extraFieldUnixType1 = extraFieldUnixType1;
5751
5870
  }
5871
+ const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5872
+ if (extraFieldExtendedTimestamp) {
5873
+ readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5874
+ directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5875
+ }
5752
5876
  const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS);
5753
5877
  if (extraFieldNTFS) {
5754
5878
  readExtraFieldNTFS(extraFieldNTFS, directory);
@@ -5765,15 +5889,13 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5765
5889
  readExtraFieldUnix(extraFieldUnix, unixIdsRead ? {} : directory, false);
5766
5890
  directory.extraFieldUnix = extraFieldUnix;
5767
5891
  }
5768
- const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5769
- if (extraFieldExtendedTimestamp) {
5770
- readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5771
- directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5772
- }
5773
5892
  const extraFieldUSDZ = extraField.get(EXTRAFIELD_TYPE_USDZ);
5774
5893
  if (extraFieldUSDZ) {
5775
5894
  directory.extraFieldUSDZ = extraFieldUSDZ;
5776
5895
  }
5896
+ if (extraFieldZip64Missing) {
5897
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5898
+ }
5777
5899
  return malformedExtraField;
5778
5900
  }
5779
5901
 
@@ -5988,13 +6110,7 @@ async function detectOverlappingEntry({
5988
6110
  let dataDescriptorLength = 0;
5989
6111
  if (dataDescriptor) {
5990
6112
  const zip64 = Boolean(extraFieldZip64);
5991
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
5992
- const dataDescriptorView = getDataView(dataDescriptorArray);
5993
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
5994
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
5995
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
5996
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
5997
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6113
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
5998
6114
  if (localDataDescriptor) {
5999
6115
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6000
6116
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6064,6 +6180,25 @@ function mergeRanges(sortedRanges, pendingRanges) {
6064
6180
  return mergedRanges;
6065
6181
  }
6066
6182
 
6183
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6184
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6185
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6186
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6187
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6188
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6189
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6190
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6191
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6192
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6193
+ }
6194
+
6195
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6196
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6197
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6198
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6199
+ }
6200
+ }
6201
+
6067
6202
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6068
6203
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6069
6204
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6323,6 +6458,18 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
6323
6458
  }
6324
6459
  }
6325
6460
 
6461
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6462
+ const { dataDescriptor } = localDirectory;
6463
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6464
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6465
+ if (dataDescriptor && !maskedLocalDirectory &&
6466
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6467
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6468
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6469
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6470
+ }
6471
+ }
6472
+
6326
6473
  function reportAmbiguity(reject, warnings, reason) {
6327
6474
  if (reject) {
6328
6475
  throwAmbiguousArchive(reason);
@@ -6408,11 +6555,13 @@ var zipReader = /*#__PURE__*/Object.freeze({
6408
6555
  WARNING_COMPRESSED_PATCHED_DATA: WARNING_COMPRESSED_PATCHED_DATA,
6409
6556
  WARNING_DUPLICATE_FILENAME: WARNING_DUPLICATE_FILENAME,
6410
6557
  WARNING_MALFORMED_EXTRA_FIELD: WARNING_MALFORMED_EXTRA_FIELD,
6558
+ WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET: WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET,
6411
6559
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG: WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
6412
6560
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD: WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
6413
6561
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES: WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES,
6414
6562
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME: WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
6415
6563
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY: WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
6564
+ WARNING_MISSING_ZIP64_EXTRA_FIELD: WARNING_MISSING_ZIP64_EXTRA_FIELD,
6416
6565
  WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY: WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
6417
6566
  WARNING_PREPENDED_CENTRAL_DIRECTORY: WARNING_PREPENDED_CENTRAL_DIRECTORY,
6418
6567
  WARNING_PREPENDED_DATA: WARNING_PREPENDED_DATA,
@@ -6423,6 +6572,7 @@ var zipReader = /*#__PURE__*/Object.freeze({
6423
6572
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6424
6573
  ZipReader: ZipReader,
6425
6574
  ZipReaderStream: ZipReaderStream,
6575
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6426
6576
  isZipFile: isZipFile
6427
6577
  });
6428
6578
 
@@ -6545,130 +6695,8 @@ class ZipWriter {
6545
6695
  return watchPromiseError(this, prependZipEntries(this, reader));
6546
6696
  }
6547
6697
 
6548
- appendZip(reader) {
6549
- return watchPromiseError(this, this.appendZipEntries(reader));
6550
- }
6551
-
6552
- async appendZipEntries(reader) {
6553
- const zipWriter = this;
6554
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6555
- while (pendingAddFileCalls.size) {
6556
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6557
- }
6558
- let resolveAppendZip;
6559
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6560
- pendingAddFileCalls.add(promiseAppendZip);
6561
- const appendedFilenames = [];
6562
- let releaseLockWriter;
6563
- try {
6564
- reader = new GenericReader(reader);
6565
- await initStream(reader);
6566
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6567
- reader = new BlobReader(await streamToBlob(reader.readable));
6568
- await initStream(reader);
6569
- }
6570
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6571
- const zipReader$1 = new ZipReader(reader);
6572
- const entries = await zipReader$1.getEntries();
6573
- await zipReader$1.close();
6574
- await initStream(zipWriter.writer);
6575
- const { directoryOffset } = zipReader$1;
6576
- entries.forEach(({ filename }) => {
6577
- if (filenames.has(filename)) {
6578
- throw new Error(ERR_DUPLICATED_NAME);
6579
- }
6580
- filenames.add(filename);
6581
- appendedFilenames.push(filename);
6582
- });
6583
- zipWriter.writerLocked = true;
6584
- const { lockWriter } = zipWriter;
6585
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6586
- zipWriter.writerLocked = false;
6587
- resolve();
6588
- });
6589
- await lockWriter;
6590
- if (zipWriter.addSplitZipSignature) {
6591
- delete zipWriter.addSplitZipSignature;
6592
- if (!await startsWithSplitZipSignature(reader)) {
6593
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6594
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6595
- }
6596
- }
6597
- const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
6598
- entries.forEach(entry => {
6599
- const {
6600
- version,
6601
- rawLastModDate,
6602
- rawFilename,
6603
- bitFlag,
6604
- encrypted,
6605
- uncompressedSize,
6606
- compressedSize,
6607
- extraFieldZip64
6608
- } = entry;
6609
- let {
6610
- compressionMethod,
6611
- rawExtraField,
6612
- } = entry;
6613
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6614
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6615
- if (entry.extraFieldAES) {
6616
- compressionMethod = COMPRESSION_METHOD_AES;
6617
- }
6618
- const extraFieldLength = getLength(rawExtraField);
6619
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6620
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6621
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6622
- const {
6623
- headerArray,
6624
- headerView
6625
- } = getHeaderArrayData({
6626
- version,
6627
- bitFlag: bitFlagValue,
6628
- compressionMethod,
6629
- uncompressedSize,
6630
- compressedSize,
6631
- rawLastModDate,
6632
- rawFilename,
6633
- zip64CompressedSize,
6634
- zip64UncompressedSize,
6635
- extraFieldLength
6636
- });
6637
- const { crc32 } = entry;
6638
- if (crc32 !== UNDEFINED_VALUE) {
6639
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6640
- }
6641
- const { offset, diskNumberStart } = entryPositions.get(entry);
6642
- Object.assign(entry, {
6643
- zip64Enabled: true,
6644
- zip64UncompressedSize,
6645
- zip64CompressedSize,
6646
- offset,
6647
- diskNumberStart,
6648
- zip64DiskNumberStart: false,
6649
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6650
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6651
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6652
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6653
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6654
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6655
- rawExtraField,
6656
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6657
- headerArray,
6658
- headerView
6659
- });
6660
- fileEntries.set(entry.filename, entry);
6661
- });
6662
- } catch (error) {
6663
- appendedFilenames.forEach(filename => filenames.delete(filename));
6664
- throw error;
6665
- } finally {
6666
- resolveAppendZip();
6667
- pendingAddFileCalls.delete(promiseAppendZip);
6668
- if (releaseLockWriter) {
6669
- releaseLockWriter();
6670
- }
6671
- }
6698
+ appendZip(reader, options = {}) {
6699
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6672
6700
  }
6673
6701
 
6674
6702
  add(name = "", reader, options = {}) {
@@ -6830,11 +6858,140 @@ function watchPromiseError(zipWriter, promise) {
6830
6858
  return watchedPromise;
6831
6859
  }
6832
6860
 
6861
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6862
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6863
+ const filter = checkFunctionOption(options.filter);
6864
+ while (pendingAddFileCalls.size) {
6865
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6866
+ }
6867
+ let resolveAppendZip;
6868
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6869
+ pendingAddFileCalls.add(promiseAppendZip);
6870
+ const appendedFilenames = [];
6871
+ let releaseLockWriter;
6872
+ try {
6873
+ reader = new GenericReader(reader);
6874
+ await initStream(reader);
6875
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6876
+ reader = new BlobReader(await streamToBlob(reader.readable));
6877
+ await initStream(reader);
6878
+ }
6879
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6880
+ const zipReader$1 = new ZipReader(reader);
6881
+ const entries = await zipReader$1.getEntries();
6882
+ const keptEntries = [];
6883
+ for (const entry of entries) {
6884
+ if (!filter || await filter(entry)) {
6885
+ keptEntries.push(entry);
6886
+ }
6887
+ }
6888
+ await zipReader$1.close();
6889
+ await initStream(zipWriter.writer);
6890
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6891
+ keptEntries.forEach(entry => {
6892
+ const { filename } = entry;
6893
+ if (entriesMissingZip64ExtraField.has(entry)) {
6894
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6895
+ }
6896
+ if (filenames.has(filename)) {
6897
+ throw new Error(ERR_DUPLICATED_NAME);
6898
+ }
6899
+ filenames.add(filename);
6900
+ appendedFilenames.push(filename);
6901
+ });
6902
+ zipWriter.writerLocked = true;
6903
+ const { lockWriter } = zipWriter;
6904
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6905
+ zipWriter.writerLocked = false;
6906
+ resolve();
6907
+ });
6908
+ await lockWriter;
6909
+ if (zipWriter.addSplitZipSignature) {
6910
+ delete zipWriter.addSplitZipSignature;
6911
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6912
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6913
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6914
+ }
6915
+ }
6916
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6917
+ keptEntries.forEach(entry => {
6918
+ const {
6919
+ version,
6920
+ rawLastModDate,
6921
+ rawFilename,
6922
+ rawBitFlag,
6923
+ uncompressedSize,
6924
+ compressedSize,
6925
+ extraFieldZip64
6926
+ } = entry;
6927
+ let {
6928
+ compressionMethod,
6929
+ rawExtraField,
6930
+ } = entry;
6931
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6932
+ if (entry.extraFieldAES) {
6933
+ compressionMethod = COMPRESSION_METHOD_AES;
6934
+ }
6935
+ const extraFieldLength = getLength(rawExtraField);
6936
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6937
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6938
+ const {
6939
+ headerArray,
6940
+ headerView
6941
+ } = getHeaderArrayData({
6942
+ version,
6943
+ bitFlag: rawBitFlag,
6944
+ compressionMethod,
6945
+ uncompressedSize,
6946
+ compressedSize,
6947
+ rawLastModDate,
6948
+ rawFilename,
6949
+ zip64CompressedSize,
6950
+ zip64UncompressedSize,
6951
+ extraFieldLength
6952
+ });
6953
+ const { crc32 } = entry;
6954
+ if (crc32 !== UNDEFINED_VALUE) {
6955
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6956
+ }
6957
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6958
+ Object.assign(entry, {
6959
+ zip64Enabled: true,
6960
+ zip64UncompressedSize,
6961
+ zip64CompressedSize,
6962
+ offset,
6963
+ diskNumberStart,
6964
+ zip64DiskNumberStart: false,
6965
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6966
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6967
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6968
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6969
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6970
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6971
+ rawExtraField,
6972
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6973
+ headerArray,
6974
+ headerView
6975
+ });
6976
+ fileEntries.set(entry.filename, entry);
6977
+ });
6978
+ } catch (error) {
6979
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6980
+ throw error;
6981
+ } finally {
6982
+ resolveAppendZip();
6983
+ pendingAddFileCalls.delete(promiseAppendZip);
6984
+ if (releaseLockWriter) {
6985
+ releaseLockWriter();
6986
+ }
6987
+ }
6988
+ }
6989
+
6833
6990
  async function prependZipEntries(zipWriter, reader) {
6834
6991
  if (zipWriter.filenames.size) {
6835
6992
  throw new Error(ERR_ZIP_NOT_EMPTY);
6836
6993
  }
6837
- await zipWriter.appendZipEntries(reader);
6994
+ await appendZipEntries(zipWriter, reader);
6838
6995
  }
6839
6996
 
6840
6997
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8606,26 +8763,63 @@ function removeExtraFieldZip64(rawExtraField) {
8606
8763
  return rawExtraField;
8607
8764
  }
8608
8765
 
8609
- async function copyZipData(zipWriter, reader, entries, directoryOffset) {
8766
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8610
8767
  const { writer } = zipWriter;
8611
8768
  const entryPositions = new Map();
8612
- if (writer.closeDisk) {
8769
+ if (writer.closeDisk || compact) {
8613
8770
  const sortedEntries = Array.from(entries).sort((firstEntry, secondEntry) =>
8614
8771
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8615
- let copiedLength = 0;
8616
- for (const entry of sortedEntries) {
8617
- const sourceOffset = getSourceOffset(reader, entry);
8618
- await copyData(zipWriter, reader, copiedLength, sourceOffset - copiedLength);
8619
- if (exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8620
- await writer.closeDisk();
8621
- }
8622
- entryPositions.set(entry, {
8623
- offset: getSegmentOffset(zipWriter, writer),
8624
- diskNumberStart: getDiskNumber(writer)
8625
- });
8626
- copiedLength = sourceOffset;
8772
+ const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8773
+ const keptEntrySet = new Set(keptEntries);
8774
+ const localHeaderLengths = new Map();
8775
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8776
+ const entry = sortedEntries[indexEntry];
8777
+ if (keptEntrySet.has(entry)) {
8778
+ const sourceOffset = getSourceOffset(reader, entry);
8779
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8780
+ if (localHeaderLength === UNDEFINED_VALUE) {
8781
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8782
+ }
8783
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8784
+ if (entry.bitFlag.dataDescriptor) {
8785
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8786
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8787
+ }
8788
+ if (entryEnd > regionEnds[indexEntry]) {
8789
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8790
+ }
8791
+ regionEnds[indexEntry] = entryEnd;
8792
+ localHeaderLengths.set(entry, localHeaderLength);
8793
+ }
8794
+ }
8795
+ let copiedOffset = 0;
8796
+ let lastPosition;
8797
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8798
+ const entry = sortedEntries[indexEntry];
8799
+ if (keptEntrySet.has(entry)) {
8800
+ const sourceOffset = getSourceOffset(reader, entry);
8801
+ if (sourceOffset < copiedOffset) {
8802
+ entryPositions.set(entry, lastPosition);
8803
+ } else {
8804
+ if (!compact) {
8805
+ await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8806
+ }
8807
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8808
+ await writer.closeDisk();
8809
+ }
8810
+ lastPosition = {
8811
+ offset: getSegmentOffset(zipWriter, writer),
8812
+ diskNumberStart: getDiskNumber(writer)
8813
+ };
8814
+ entryPositions.set(entry, lastPosition);
8815
+ await copyData(zipWriter, reader, sourceOffset, regionEnds[indexEntry] - sourceOffset);
8816
+ copiedOffset = regionEnds[indexEntry];
8817
+ }
8818
+ }
8819
+ }
8820
+ if (!compact) {
8821
+ await copyData(zipWriter, reader, copiedOffset, directoryOffset - copiedOffset);
8627
8822
  }
8628
- await copyData(zipWriter, reader, copiedLength, directoryOffset - copiedLength);
8629
8823
  } else {
8630
8824
  const baseOffset = zipWriter.offset;
8631
8825
  await copyData(zipWriter, reader, 0, directoryOffset);
@@ -8637,6 +8831,20 @@ async function copyZipData(zipWriter, reader, entries, directoryOffset) {
8637
8831
  return entryPositions;
8638
8832
  }
8639
8833
 
8834
+ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8835
+ const regionEnds = new Array(sortedEntries.length);
8836
+ let regionEnd = directoryOffset;
8837
+ for (let indexEntry = sortedEntries.length - 1; indexEntry >= 0; indexEntry--) {
8838
+ const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8839
+ const nextEntry = sortedEntries[indexEntry + 1];
8840
+ if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8841
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8842
+ }
8843
+ regionEnds[indexEntry] = regionEnd;
8844
+ }
8845
+ return regionEnds;
8846
+ }
8847
+
8640
8848
  async function copyData(zipWriter, reader, offset, size) {
8641
8849
  if (size > 0) {
8642
8850
  const { writer } = zipWriter;
@@ -8644,11 +8852,13 @@ async function copyData(zipWriter, reader, offset, size) {
8644
8852
  try {
8645
8853
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8646
8854
  } catch (error) {
8647
- zipWriter.hasCorruptedEntries = true;
8648
- try {
8649
- error.corruptedEntry = true;
8650
- } catch {
8651
- // ignored
8855
+ if (copiedLength) {
8856
+ zipWriter.hasCorruptedEntries = true;
8857
+ try {
8858
+ error.corruptedEntry = true;
8859
+ } catch {
8860
+ // ignored
8861
+ }
8652
8862
  }
8653
8863
  throw error;
8654
8864
  } finally {
@@ -8659,11 +8869,16 @@ async function copyData(zipWriter, reader, offset, size) {
8659
8869
  }
8660
8870
 
8661
8871
  async function getLocalHeaderLength(reader, offset) {
8662
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8872
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8873
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8874
+ EMPTY_UINT8_ARRAY;
8663
8875
  if (getLength(headerArray) < HEADER_SIZE) {
8664
- return HEADER_SIZE;
8876
+ return UNDEFINED_VALUE;
8665
8877
  }
8666
8878
  const headerView = getDataView(headerArray);
8879
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8880
+ return UNDEFINED_VALUE;
8881
+ }
8667
8882
  return HEADER_SIZE +
8668
8883
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8669
8884
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9014,7 +9229,7 @@ function formatSupported(StreamClass, format) {
9014
9229
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9015
9230
  */
9016
9231
 
9017
- const VERSION = "2.19.0";
9232
+ const VERSION = "2.21.0";
9018
9233
 
9019
9234
  /*
9020
9235
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -11208,11 +11423,13 @@ exports.WARNING_COMPRESSED_PATCHED_DATA = WARNING_COMPRESSED_PATCHED_DATA;
11208
11423
  exports.WARNING_COMPRESSION_UNAVAILABLE = WARNING_COMPRESSION_UNAVAILABLE;
11209
11424
  exports.WARNING_DUPLICATE_FILENAME = WARNING_DUPLICATE_FILENAME;
11210
11425
  exports.WARNING_MALFORMED_EXTRA_FIELD = WARNING_MALFORMED_EXTRA_FIELD;
11426
+ exports.WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET;
11211
11427
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG;
11212
11428
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD;
11213
11429
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES;
11214
11430
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME;
11215
11431
  exports.WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY;
11432
+ exports.WARNING_MISSING_ZIP64_EXTRA_FIELD = WARNING_MISSING_ZIP64_EXTRA_FIELD;
11216
11433
  exports.WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY;
11217
11434
  exports.WARNING_PREPENDED_CENTRAL_DIRECTORY = WARNING_PREPENDED_CENTRAL_DIRECTORY;
11218
11435
  exports.WARNING_PREPENDED_DATA = WARNING_PREPENDED_DATA;