@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.
@@ -126,6 +126,10 @@
126
126
 
127
127
  const UNDEFINED_VALUE = undefined;
128
128
  const INFINITY_VALUE = Infinity;
129
+ const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
130
+ const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
131
+ const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
132
+
129
133
  const UNDEFINED_TYPE = "undefined";
130
134
  const FUNCTION_TYPE = "function";
131
135
  const OBJECT_TYPE = "object";
@@ -4775,12 +4779,9 @@
4775
4779
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4776
4780
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4777
4781
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4778
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4779
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4780
4782
  const ERR_ENCRYPTED = "File contains encrypted entry";
4781
4783
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4782
4784
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4783
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4784
4785
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4785
4786
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4786
4787
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4799,6 +4800,8 @@
4799
4800
  const WARNING_PREPENDED_DATA = "prepended data";
4800
4801
  const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
4801
4802
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
4803
+ const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = "mismatched central directory offset";
4804
+ const WARNING_MISSING_ZIP64_EXTRA_FIELD = "missing zip64 extra field";
4802
4805
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
4803
4806
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
4804
4807
  const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
@@ -4843,7 +4846,8 @@
4843
4846
  Object.assign(this, {
4844
4847
  reader: new GenericReader(reader),
4845
4848
  options,
4846
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4849
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4850
+ entriesMissingZip64ExtraField: new WeakSet()
4847
4851
  });
4848
4852
  }
4849
4853
 
@@ -4914,16 +4918,22 @@
4914
4918
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4915
4919
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4916
4920
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4917
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4918
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4919
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4920
- const originalDirectoryDataOffset = directoryDataOffset;
4921
- directoryDataOffset = expectedDirectoryDataOffset;
4922
- if (directoryDataOffset > originalDirectoryDataOffset) {
4923
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4921
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4922
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4923
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4924
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4925
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4926
+ }
4927
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4928
+ directoryDataOffset != expectedDirectoryDataOffset) {
4929
+ const originalDirectoryDataOffset = directoryDataOffset;
4930
+ directoryDataOffset = expectedDirectoryDataOffset;
4931
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4932
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4933
+ }
4934
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4935
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4924
4936
  }
4925
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4926
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4927
4937
  }
4928
4938
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4929
4939
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4961,15 +4971,13 @@
4961
4971
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4962
4972
  }
4963
4973
  directoryDataOffset = getDiskOffset$1(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
4974
+ } else if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || diskNumber == MAX_16_BITS) {
4975
+ throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
4964
4976
  }
4965
4977
  }
4966
4978
  let declaredDirectoryDataLength = directoryDataLength;
4967
4979
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4968
4980
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4969
- if (directoryDataOffset >= reader.size) {
4970
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4971
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4972
- }
4973
4981
  if (expectedLastDiskNumber != lastDiskNumber) {
4974
4982
  throw new Error(ERR_SPLIT_ZIP_FILE);
4975
4983
  }
@@ -4977,33 +4985,64 @@
4977
4985
  throw new Error(ERR_BAD_FORMAT);
4978
4986
  }
4979
4987
  let offset = 0;
4980
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4988
+ let directoryArray = directoryDataOffset < reader.size ?
4989
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
4990
+ EMPTY_UINT8_ARRAY;
4981
4991
  let directoryView = getDataView(directoryArray);
4982
4992
  if (directoryDataLength) {
4983
- if (directoryArray.length < 4) {
4984
- throw new Error(ERR_BAD_FORMAT);
4985
- }
4986
- const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4993
+ let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4987
4994
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4988
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4995
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
4996
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4989
4997
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4990
- detectEncryptedCentralDirectory(directoryView);
4998
+ detectEncryptedCentralDirectory(directoryView));
4999
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
5000
+ throw new Error(ERR_BAD_FORMAT);
5001
+ }
4991
5002
  let reconcile = !storedPointsAtDirectory;
4992
- if (!reconcile && expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4993
- const expectedSignatureArray = await readUint8Array(reader, expectedDirectoryDataOffset, 4);
4994
- reconcile = getUint32$1(getDataView(expectedSignatureArray), 0) == CENTRAL_FILE_HEADER_SIGNATURE;
5003
+ if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
5004
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5005
+ if (!expectedPointsAtDirectory) {
5006
+ const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
5007
+ const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
5008
+ if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5009
+ await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5010
+ expectedDirectoryDataOffset = signedDirectoryDataOffset;
5011
+ expectedPointsAtDirectory = true;
5012
+ }
5013
+ }
5014
+ if (storedPointsAtDirectory) {
5015
+ reconcile = expectedPointsAtDirectory;
5016
+ }
4995
5017
  }
4996
- if (reconcile) {
5018
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
4997
5019
  const originalDirectoryDataOffset = directoryDataOffset;
4998
5020
  directoryDataOffset = expectedDirectoryDataOffset;
4999
- if (directoryDataOffset > originalDirectoryDataOffset) {
5000
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5001
- prependedCentralDirectory = storedPointsAtDirectory;
5002
- }
5003
5021
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5004
5022
  directoryView = getDataView(directoryArray);
5023
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5024
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5025
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5026
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5027
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5028
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5029
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5030
+ prependedDataLength += offsetDelta;
5031
+ prependedCentralDirectory = storedPointsAtDirectory;
5032
+ } else {
5033
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5034
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5035
+ prependedDataLength += offsetDelta;
5036
+ }
5037
+ }
5005
5038
  }
5006
5039
  }
5040
+ if (directoryArray.length < 4) {
5041
+ throw new Error(ERR_BAD_FORMAT);
5042
+ }
5043
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5044
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5045
+ directoryDataOffset = centralDirectoryEndOffset;
5007
5046
  }
5008
5047
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5009
5048
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5045,8 +5084,10 @@
5045
5084
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5046
5085
  }
5047
5086
  }
5087
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5088
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5048
5089
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5049
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5090
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5050
5091
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5051
5092
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5052
5093
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5112,7 +5153,16 @@
5112
5153
  filename,
5113
5154
  comment
5114
5155
  });
5115
- const malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5156
+ let malformedExtraField;
5157
+ try {
5158
+ malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5159
+ } catch (error) {
5160
+ if (checkAmbiguity || error.message != ERR_EXTRAFIELD_ZIP64_NOT_FOUND) {
5161
+ throw error;
5162
+ }
5163
+ fileEntry.zip64ExtraFieldMissing = true;
5164
+ addWarning(warnings, WARNING_MISSING_ZIP64_EXTRA_FIELD, fileEntry.filename);
5165
+ }
5116
5166
  filename = fileEntry.filename;
5117
5167
  if (normalizeFilename) {
5118
5168
  const normalizedFilename = normalizeFilename(filename);
@@ -5128,13 +5178,17 @@
5128
5178
  if (malformedExtraField) {
5129
5179
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5130
5180
  }
5131
- fileEntry.offset += prependedDataLength;
5132
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5133
- startOffset = Math.min(entryPosition, startOffset);
5134
- if (entryPosition < previousEntryPosition) {
5135
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5181
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5182
+ startOffset = 0;
5183
+ } else {
5184
+ fileEntry.offset += prependedDataLength;
5185
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5186
+ startOffset = Math.min(entryPosition, startOffset);
5187
+ if (entryPosition < previousEntryPosition) {
5188
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5189
+ }
5190
+ previousEntryPosition = entryPosition;
5136
5191
  }
5137
- previousEntryPosition = entryPosition;
5138
5192
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5139
5193
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5140
5194
  }
@@ -5154,11 +5208,12 @@
5154
5208
  const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
5155
5209
  const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
5156
5210
  const symlink = (unixType & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK;
5157
- const executable = !symlink && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5158
- ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5159
- : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5160
5211
  const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5161
5212
  const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5213
+ const directory = modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE);
5214
+ const executable = !symlink && !directory && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5215
+ ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5216
+ : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5162
5217
  Object.assign(fileEntry, {
5163
5218
  setuid,
5164
5219
  setgid,
@@ -5166,16 +5221,19 @@
5166
5221
  symlink,
5167
5222
  unixExternalUpper,
5168
5223
  executable,
5169
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
5224
+ directory,
5170
5225
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5171
5226
  });
5172
5227
  const entry = new Entry(fileEntry);
5173
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5228
+ if (fileEntry.zip64ExtraFieldMissing) {
5229
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5230
+ }
5231
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5174
5232
  entry.arrayBuffer = async options => {
5175
5233
  const writer = new TransformStream();
5176
5234
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5177
5235
  arrayBufferPromise.catch(() => { });
5178
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5236
+ await fileEntry.getData(writer, entry, zipReader,
5179
5237
  Object.assign({}, options, { preventClose: false }));
5180
5238
  return arrayBufferPromise;
5181
5239
  };
@@ -5222,10 +5280,10 @@
5222
5280
  const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
5223
5281
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
5224
5282
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
5225
- if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
5283
+ if (checkAmbiguity && (prependedDataLength > 0 || (filesLength && startOffset > splitZipSignatureLength))) {
5226
5284
  throwAmbiguousArchive(WARNING_PREPENDED_DATA);
5227
5285
  }
5228
- if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5286
+ if (prependedDataLength > 0 || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5229
5287
  addWarning(warnings, WARNING_PREPENDED_DATA);
5230
5288
  }
5231
5289
  if (prependedCentralDirectory) {
@@ -5383,8 +5441,12 @@
5383
5441
  });
5384
5442
  }
5385
5443
 
5386
- async getData(writer, fileEntry, readRanges, options = {}) {
5444
+ async getData(writer, fileEntry, zipReader, options = {}) {
5387
5445
  const zipEntry = this;
5446
+ const { readRanges, directoryOffset } = zipReader;
5447
+ if (zipEntry.zip64ExtraFieldMissing) {
5448
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5449
+ }
5388
5450
  const config = getConfiguration();
5389
5451
  const {
5390
5452
  reader,
@@ -5455,10 +5517,10 @@
5455
5517
  }
5456
5518
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5457
5519
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5458
- if (lastAccessDate) {
5520
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5459
5521
  fileEntry.lastAccessDate = lastAccessDate;
5460
5522
  }
5461
- if (creationDate) {
5523
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5462
5524
  fileEntry.creationDate = creationDate;
5463
5525
  }
5464
5526
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5487,7 +5549,7 @@
5487
5549
  throw new Error(ERR_ENCRYPTED);
5488
5550
  }
5489
5551
  }
5490
- if (dataOffset + compressedSize > reader.size) {
5552
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5491
5553
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5492
5554
  }
5493
5555
  const size = compressedSize;
@@ -5553,6 +5615,7 @@
5553
5615
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5554
5616
  readRanges
5555
5617
  });
5618
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5556
5619
  }
5557
5620
  let writable, abortError, aborted;
5558
5621
  try {
@@ -5572,10 +5635,7 @@
5572
5635
  writer.size += writtenSize;
5573
5636
  }
5574
5637
  } catch (error) {
5575
- const { outputSize: failedOutputSize } = workerOptions;
5576
- if (failedOutputSize !== UNDEFINED_VALUE) {
5577
- writer.size += failedOutputSize;
5578
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5638
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5579
5639
  writer.size += error.outputSize;
5580
5640
  }
5581
5641
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5603,13 +5663,7 @@
5603
5663
  };
5604
5664
 
5605
5665
  function detectEncryptedCentralDirectory(directoryView) {
5606
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5607
- for (let offset = 0; offset < maxOffset; offset++) {
5608
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5609
- return true;
5610
- }
5611
- }
5612
- return false;
5666
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5613
5667
  }
5614
5668
 
5615
5669
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5622,6 +5676,70 @@
5622
5676
  return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
5623
5677
  }
5624
5678
 
5679
+ function pointsAtDirectory(reader, offset) {
5680
+ return startsWithSignature(reader, offset, CENTRAL_FILE_HEADER_SIGNATURE);
5681
+ }
5682
+
5683
+ async function startsWithSignature(reader, offset, signature) {
5684
+ if (offset < 0 || offset + 4 > reader.size) {
5685
+ return false;
5686
+ }
5687
+ const signatureArray = await readUint8Array(reader, offset, 4);
5688
+ return getUint32$1(getDataView(signatureArray), 0) == signature;
5689
+ }
5690
+
5691
+ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5692
+ const startOffset = Math.max(0, endOffset - (6 + MAX_16_BITS));
5693
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5694
+ const tailView = getDataView(tailArray);
5695
+ for (let indexByte = tailArray.length - 6; indexByte >= 0; indexByte--) {
5696
+ if (getUint32$1(tailView, indexByte) == DIGITAL_SIGNATURE_RECORD_SIGNATURE &&
5697
+ indexByte + 6 + getUint16$1(tailView, indexByte + 4) == tailArray.length) {
5698
+ return startOffset + indexByte;
5699
+ }
5700
+ }
5701
+ }
5702
+
5703
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5704
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5705
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5706
+ const tailView = getDataView(tailArray);
5707
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5708
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5709
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5710
+ return startOffset + indexByte;
5711
+ }
5712
+ }
5713
+ }
5714
+
5715
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5716
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5717
+ return UNDEFINED_VALUE;
5718
+ }
5719
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5720
+ if (localHeaderOffset != MAX_32_BITS) {
5721
+ return localHeaderOffset;
5722
+ }
5723
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5724
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5725
+ while (offsetExtraField + 4 <= endExtraField) {
5726
+ const type = getUint16$1(directoryView, offsetExtraField);
5727
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5728
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5729
+ let offsetValue = offsetExtraField + 4;
5730
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5731
+ offsetValue += 8;
5732
+ }
5733
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5734
+ offsetValue += 8;
5735
+ }
5736
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5737
+ }
5738
+ offsetExtraField += 4 + size;
5739
+ }
5740
+ return UNDEFINED_VALUE;
5741
+ }
5742
+
5625
5743
  function readDigitalSignature(signatureRecordArray) {
5626
5744
  if (signatureRecordArray.length >= 6) {
5627
5745
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5711,6 +5829,7 @@
5711
5829
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5712
5830
  });
5713
5831
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5832
+ let extraFieldZip64Missing;
5714
5833
  if (extraFieldZip64) {
5715
5834
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5716
5835
  malformedExtraField = true;
@@ -5720,7 +5839,7 @@
5720
5839
  if (localDirectory) {
5721
5840
  malformedExtraField = true;
5722
5841
  } else {
5723
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5842
+ extraFieldZip64Missing = true;
5724
5843
  }
5725
5844
  }
5726
5845
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5753,6 +5872,11 @@
5753
5872
  readExtraFieldUnixDates(extraFieldUnixType1, directory);
5754
5873
  directory.extraFieldUnixType1 = extraFieldUnixType1;
5755
5874
  }
5875
+ const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5876
+ if (extraFieldExtendedTimestamp) {
5877
+ readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5878
+ directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5879
+ }
5756
5880
  const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS);
5757
5881
  if (extraFieldNTFS) {
5758
5882
  readExtraFieldNTFS(extraFieldNTFS, directory);
@@ -5769,15 +5893,13 @@
5769
5893
  readExtraFieldUnix(extraFieldUnix, unixIdsRead ? {} : directory, false);
5770
5894
  directory.extraFieldUnix = extraFieldUnix;
5771
5895
  }
5772
- const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5773
- if (extraFieldExtendedTimestamp) {
5774
- readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5775
- directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5776
- }
5777
5896
  const extraFieldUSDZ = extraField.get(EXTRAFIELD_TYPE_USDZ);
5778
5897
  if (extraFieldUSDZ) {
5779
5898
  directory.extraFieldUSDZ = extraFieldUSDZ;
5780
5899
  }
5900
+ if (extraFieldZip64Missing) {
5901
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5902
+ }
5781
5903
  return malformedExtraField;
5782
5904
  }
5783
5905
 
@@ -5992,13 +6114,7 @@
5992
6114
  let dataDescriptorLength = 0;
5993
6115
  if (dataDescriptor) {
5994
6116
  const zip64 = Boolean(extraFieldZip64);
5995
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
5996
- const dataDescriptorView = getDataView(dataDescriptorArray);
5997
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
5998
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
5999
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6000
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6001
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6117
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
6002
6118
  if (localDataDescriptor) {
6003
6119
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6004
6120
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6068,6 +6184,25 @@
6068
6184
  return mergedRanges;
6069
6185
  }
6070
6186
 
6187
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6188
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6189
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6190
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6191
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6192
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6193
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6194
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6195
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6196
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6197
+ }
6198
+
6199
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6200
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6201
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6202
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6203
+ }
6204
+ }
6205
+
6071
6206
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6072
6207
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6073
6208
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6327,6 +6462,18 @@
6327
6462
  }
6328
6463
  }
6329
6464
 
6465
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6466
+ const { dataDescriptor } = localDirectory;
6467
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6468
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6469
+ if (dataDescriptor && !maskedLocalDirectory &&
6470
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6471
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6472
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6473
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6474
+ }
6475
+ }
6476
+
6330
6477
  function reportAmbiguity(reject, warnings, reason) {
6331
6478
  if (reject) {
6332
6479
  throwAmbiguousArchive(reason);
@@ -6412,11 +6559,13 @@
6412
6559
  WARNING_COMPRESSED_PATCHED_DATA: WARNING_COMPRESSED_PATCHED_DATA,
6413
6560
  WARNING_DUPLICATE_FILENAME: WARNING_DUPLICATE_FILENAME,
6414
6561
  WARNING_MALFORMED_EXTRA_FIELD: WARNING_MALFORMED_EXTRA_FIELD,
6562
+ WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET: WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET,
6415
6563
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG: WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
6416
6564
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD: WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
6417
6565
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES: WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES,
6418
6566
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME: WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
6419
6567
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY: WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
6568
+ WARNING_MISSING_ZIP64_EXTRA_FIELD: WARNING_MISSING_ZIP64_EXTRA_FIELD,
6420
6569
  WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY: WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
6421
6570
  WARNING_PREPENDED_CENTRAL_DIRECTORY: WARNING_PREPENDED_CENTRAL_DIRECTORY,
6422
6571
  WARNING_PREPENDED_DATA: WARNING_PREPENDED_DATA,
@@ -6427,6 +6576,7 @@
6427
6576
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6428
6577
  ZipReader: ZipReader,
6429
6578
  ZipReaderStream: ZipReaderStream,
6579
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6430
6580
  isZipFile: isZipFile
6431
6581
  });
6432
6582
 
@@ -6549,130 +6699,8 @@
6549
6699
  return watchPromiseError(this, prependZipEntries(this, reader));
6550
6700
  }
6551
6701
 
6552
- appendZip(reader) {
6553
- return watchPromiseError(this, this.appendZipEntries(reader));
6554
- }
6555
-
6556
- async appendZipEntries(reader) {
6557
- const zipWriter = this;
6558
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6559
- while (pendingAddFileCalls.size) {
6560
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6561
- }
6562
- let resolveAppendZip;
6563
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6564
- pendingAddFileCalls.add(promiseAppendZip);
6565
- const appendedFilenames = [];
6566
- let releaseLockWriter;
6567
- try {
6568
- reader = new GenericReader(reader);
6569
- await initStream(reader);
6570
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6571
- reader = new BlobReader(await streamToBlob(reader.readable));
6572
- await initStream(reader);
6573
- }
6574
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6575
- const zipReader$1 = new ZipReader(reader);
6576
- const entries = await zipReader$1.getEntries();
6577
- await zipReader$1.close();
6578
- await initStream(zipWriter.writer);
6579
- const { directoryOffset } = zipReader$1;
6580
- entries.forEach(({ filename }) => {
6581
- if (filenames.has(filename)) {
6582
- throw new Error(ERR_DUPLICATED_NAME);
6583
- }
6584
- filenames.add(filename);
6585
- appendedFilenames.push(filename);
6586
- });
6587
- zipWriter.writerLocked = true;
6588
- const { lockWriter } = zipWriter;
6589
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6590
- zipWriter.writerLocked = false;
6591
- resolve();
6592
- });
6593
- await lockWriter;
6594
- if (zipWriter.addSplitZipSignature) {
6595
- delete zipWriter.addSplitZipSignature;
6596
- if (!await startsWithSplitZipSignature(reader)) {
6597
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6598
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6599
- }
6600
- }
6601
- const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
6602
- entries.forEach(entry => {
6603
- const {
6604
- version,
6605
- rawLastModDate,
6606
- rawFilename,
6607
- bitFlag,
6608
- encrypted,
6609
- uncompressedSize,
6610
- compressedSize,
6611
- extraFieldZip64
6612
- } = entry;
6613
- let {
6614
- compressionMethod,
6615
- rawExtraField,
6616
- } = entry;
6617
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6618
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6619
- if (entry.extraFieldAES) {
6620
- compressionMethod = COMPRESSION_METHOD_AES;
6621
- }
6622
- const extraFieldLength = getLength(rawExtraField);
6623
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6624
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6625
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6626
- const {
6627
- headerArray,
6628
- headerView
6629
- } = getHeaderArrayData({
6630
- version,
6631
- bitFlag: bitFlagValue,
6632
- compressionMethod,
6633
- uncompressedSize,
6634
- compressedSize,
6635
- rawLastModDate,
6636
- rawFilename,
6637
- zip64CompressedSize,
6638
- zip64UncompressedSize,
6639
- extraFieldLength
6640
- });
6641
- const { crc32 } = entry;
6642
- if (crc32 !== UNDEFINED_VALUE) {
6643
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6644
- }
6645
- const { offset, diskNumberStart } = entryPositions.get(entry);
6646
- Object.assign(entry, {
6647
- zip64Enabled: true,
6648
- zip64UncompressedSize,
6649
- zip64CompressedSize,
6650
- offset,
6651
- diskNumberStart,
6652
- zip64DiskNumberStart: false,
6653
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6654
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6655
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6656
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6657
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6658
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6659
- rawExtraField,
6660
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6661
- headerArray,
6662
- headerView
6663
- });
6664
- fileEntries.set(entry.filename, entry);
6665
- });
6666
- } catch (error) {
6667
- appendedFilenames.forEach(filename => filenames.delete(filename));
6668
- throw error;
6669
- } finally {
6670
- resolveAppendZip();
6671
- pendingAddFileCalls.delete(promiseAppendZip);
6672
- if (releaseLockWriter) {
6673
- releaseLockWriter();
6674
- }
6675
- }
6702
+ appendZip(reader, options = {}) {
6703
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6676
6704
  }
6677
6705
 
6678
6706
  add(name = "", reader, options = {}) {
@@ -6834,11 +6862,140 @@
6834
6862
  return watchedPromise;
6835
6863
  }
6836
6864
 
6865
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6866
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6867
+ const filter = checkFunctionOption(options.filter);
6868
+ while (pendingAddFileCalls.size) {
6869
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6870
+ }
6871
+ let resolveAppendZip;
6872
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6873
+ pendingAddFileCalls.add(promiseAppendZip);
6874
+ const appendedFilenames = [];
6875
+ let releaseLockWriter;
6876
+ try {
6877
+ reader = new GenericReader(reader);
6878
+ await initStream(reader);
6879
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6880
+ reader = new BlobReader(await streamToBlob(reader.readable));
6881
+ await initStream(reader);
6882
+ }
6883
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6884
+ const zipReader$1 = new ZipReader(reader);
6885
+ const entries = await zipReader$1.getEntries();
6886
+ const keptEntries = [];
6887
+ for (const entry of entries) {
6888
+ if (!filter || await filter(entry)) {
6889
+ keptEntries.push(entry);
6890
+ }
6891
+ }
6892
+ await zipReader$1.close();
6893
+ await initStream(zipWriter.writer);
6894
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6895
+ keptEntries.forEach(entry => {
6896
+ const { filename } = entry;
6897
+ if (entriesMissingZip64ExtraField.has(entry)) {
6898
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6899
+ }
6900
+ if (filenames.has(filename)) {
6901
+ throw new Error(ERR_DUPLICATED_NAME);
6902
+ }
6903
+ filenames.add(filename);
6904
+ appendedFilenames.push(filename);
6905
+ });
6906
+ zipWriter.writerLocked = true;
6907
+ const { lockWriter } = zipWriter;
6908
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6909
+ zipWriter.writerLocked = false;
6910
+ resolve();
6911
+ });
6912
+ await lockWriter;
6913
+ if (zipWriter.addSplitZipSignature) {
6914
+ delete zipWriter.addSplitZipSignature;
6915
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6916
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6917
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6918
+ }
6919
+ }
6920
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6921
+ keptEntries.forEach(entry => {
6922
+ const {
6923
+ version,
6924
+ rawLastModDate,
6925
+ rawFilename,
6926
+ rawBitFlag,
6927
+ uncompressedSize,
6928
+ compressedSize,
6929
+ extraFieldZip64
6930
+ } = entry;
6931
+ let {
6932
+ compressionMethod,
6933
+ rawExtraField,
6934
+ } = entry;
6935
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6936
+ if (entry.extraFieldAES) {
6937
+ compressionMethod = COMPRESSION_METHOD_AES;
6938
+ }
6939
+ const extraFieldLength = getLength(rawExtraField);
6940
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6941
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6942
+ const {
6943
+ headerArray,
6944
+ headerView
6945
+ } = getHeaderArrayData({
6946
+ version,
6947
+ bitFlag: rawBitFlag,
6948
+ compressionMethod,
6949
+ uncompressedSize,
6950
+ compressedSize,
6951
+ rawLastModDate,
6952
+ rawFilename,
6953
+ zip64CompressedSize,
6954
+ zip64UncompressedSize,
6955
+ extraFieldLength
6956
+ });
6957
+ const { crc32 } = entry;
6958
+ if (crc32 !== UNDEFINED_VALUE) {
6959
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6960
+ }
6961
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6962
+ Object.assign(entry, {
6963
+ zip64Enabled: true,
6964
+ zip64UncompressedSize,
6965
+ zip64CompressedSize,
6966
+ offset,
6967
+ diskNumberStart,
6968
+ zip64DiskNumberStart: false,
6969
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6970
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6971
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6972
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6973
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6974
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6975
+ rawExtraField,
6976
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6977
+ headerArray,
6978
+ headerView
6979
+ });
6980
+ fileEntries.set(entry.filename, entry);
6981
+ });
6982
+ } catch (error) {
6983
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6984
+ throw error;
6985
+ } finally {
6986
+ resolveAppendZip();
6987
+ pendingAddFileCalls.delete(promiseAppendZip);
6988
+ if (releaseLockWriter) {
6989
+ releaseLockWriter();
6990
+ }
6991
+ }
6992
+ }
6993
+
6837
6994
  async function prependZipEntries(zipWriter, reader) {
6838
6995
  if (zipWriter.filenames.size) {
6839
6996
  throw new Error(ERR_ZIP_NOT_EMPTY);
6840
6997
  }
6841
- await zipWriter.appendZipEntries(reader);
6998
+ await appendZipEntries(zipWriter, reader);
6842
6999
  }
6843
7000
 
6844
7001
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8610,26 +8767,63 @@
8610
8767
  return rawExtraField;
8611
8768
  }
8612
8769
 
8613
- async function copyZipData(zipWriter, reader, entries, directoryOffset) {
8770
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8614
8771
  const { writer } = zipWriter;
8615
8772
  const entryPositions = new Map();
8616
- if (writer.closeDisk) {
8773
+ if (writer.closeDisk || compact) {
8617
8774
  const sortedEntries = Array.from(entries).sort((firstEntry, secondEntry) =>
8618
8775
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8619
- let copiedLength = 0;
8620
- for (const entry of sortedEntries) {
8621
- const sourceOffset = getSourceOffset(reader, entry);
8622
- await copyData(zipWriter, reader, copiedLength, sourceOffset - copiedLength);
8623
- if (exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8624
- await writer.closeDisk();
8625
- }
8626
- entryPositions.set(entry, {
8627
- offset: getSegmentOffset(zipWriter, writer),
8628
- diskNumberStart: getDiskNumber(writer)
8629
- });
8630
- copiedLength = sourceOffset;
8776
+ const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8777
+ const keptEntrySet = new Set(keptEntries);
8778
+ const localHeaderLengths = new Map();
8779
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8780
+ const entry = sortedEntries[indexEntry];
8781
+ if (keptEntrySet.has(entry)) {
8782
+ const sourceOffset = getSourceOffset(reader, entry);
8783
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8784
+ if (localHeaderLength === UNDEFINED_VALUE) {
8785
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8786
+ }
8787
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8788
+ if (entry.bitFlag.dataDescriptor) {
8789
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8790
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8791
+ }
8792
+ if (entryEnd > regionEnds[indexEntry]) {
8793
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8794
+ }
8795
+ regionEnds[indexEntry] = entryEnd;
8796
+ localHeaderLengths.set(entry, localHeaderLength);
8797
+ }
8798
+ }
8799
+ let copiedOffset = 0;
8800
+ let lastPosition;
8801
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8802
+ const entry = sortedEntries[indexEntry];
8803
+ if (keptEntrySet.has(entry)) {
8804
+ const sourceOffset = getSourceOffset(reader, entry);
8805
+ if (sourceOffset < copiedOffset) {
8806
+ entryPositions.set(entry, lastPosition);
8807
+ } else {
8808
+ if (!compact) {
8809
+ await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8810
+ }
8811
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8812
+ await writer.closeDisk();
8813
+ }
8814
+ lastPosition = {
8815
+ offset: getSegmentOffset(zipWriter, writer),
8816
+ diskNumberStart: getDiskNumber(writer)
8817
+ };
8818
+ entryPositions.set(entry, lastPosition);
8819
+ await copyData(zipWriter, reader, sourceOffset, regionEnds[indexEntry] - sourceOffset);
8820
+ copiedOffset = regionEnds[indexEntry];
8821
+ }
8822
+ }
8823
+ }
8824
+ if (!compact) {
8825
+ await copyData(zipWriter, reader, copiedOffset, directoryOffset - copiedOffset);
8631
8826
  }
8632
- await copyData(zipWriter, reader, copiedLength, directoryOffset - copiedLength);
8633
8827
  } else {
8634
8828
  const baseOffset = zipWriter.offset;
8635
8829
  await copyData(zipWriter, reader, 0, directoryOffset);
@@ -8641,6 +8835,20 @@
8641
8835
  return entryPositions;
8642
8836
  }
8643
8837
 
8838
+ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8839
+ const regionEnds = new Array(sortedEntries.length);
8840
+ let regionEnd = directoryOffset;
8841
+ for (let indexEntry = sortedEntries.length - 1; indexEntry >= 0; indexEntry--) {
8842
+ const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8843
+ const nextEntry = sortedEntries[indexEntry + 1];
8844
+ if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8845
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8846
+ }
8847
+ regionEnds[indexEntry] = regionEnd;
8848
+ }
8849
+ return regionEnds;
8850
+ }
8851
+
8644
8852
  async function copyData(zipWriter, reader, offset, size) {
8645
8853
  if (size > 0) {
8646
8854
  const { writer } = zipWriter;
@@ -8648,11 +8856,13 @@
8648
8856
  try {
8649
8857
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8650
8858
  } catch (error) {
8651
- zipWriter.hasCorruptedEntries = true;
8652
- try {
8653
- error.corruptedEntry = true;
8654
- } catch {
8655
- // ignored
8859
+ if (copiedLength) {
8860
+ zipWriter.hasCorruptedEntries = true;
8861
+ try {
8862
+ error.corruptedEntry = true;
8863
+ } catch {
8864
+ // ignored
8865
+ }
8656
8866
  }
8657
8867
  throw error;
8658
8868
  } finally {
@@ -8663,11 +8873,16 @@
8663
8873
  }
8664
8874
 
8665
8875
  async function getLocalHeaderLength(reader, offset) {
8666
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8876
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8877
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8878
+ EMPTY_UINT8_ARRAY;
8667
8879
  if (getLength(headerArray) < HEADER_SIZE) {
8668
- return HEADER_SIZE;
8880
+ return UNDEFINED_VALUE;
8669
8881
  }
8670
8882
  const headerView = getDataView(headerArray);
8883
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8884
+ return UNDEFINED_VALUE;
8885
+ }
8671
8886
  return HEADER_SIZE +
8672
8887
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8673
8888
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9018,7 +9233,7 @@
9018
9233
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9019
9234
  */
9020
9235
 
9021
- const VERSION = "2.19.0";
9236
+ const VERSION = "2.21.0";
9022
9237
 
9023
9238
  /*
9024
9239
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -11079,11 +11294,13 @@
11079
11294
  exports.WARNING_COMPRESSION_UNAVAILABLE = WARNING_COMPRESSION_UNAVAILABLE;
11080
11295
  exports.WARNING_DUPLICATE_FILENAME = WARNING_DUPLICATE_FILENAME;
11081
11296
  exports.WARNING_MALFORMED_EXTRA_FIELD = WARNING_MALFORMED_EXTRA_FIELD;
11297
+ exports.WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET;
11082
11298
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG;
11083
11299
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD;
11084
11300
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES;
11085
11301
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME;
11086
11302
  exports.WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY;
11303
+ exports.WARNING_MISSING_ZIP64_EXTRA_FIELD = WARNING_MISSING_ZIP64_EXTRA_FIELD;
11087
11304
  exports.WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY;
11088
11305
  exports.WARNING_PREPENDED_CENTRAL_DIRECTORY = WARNING_PREPENDED_CENTRAL_DIRECTORY;
11089
11306
  exports.WARNING_PREPENDED_DATA = WARNING_PREPENDED_DATA;