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