@zip.js/zip.js 2.20.0 → 2.22.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.
@@ -117,6 +117,10 @@ const MIN_DATE = new Date(1980, 0, 1);
117
117
 
118
118
  const UNDEFINED_VALUE = undefined;
119
119
  const INFINITY_VALUE = Infinity;
120
+ const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
121
+ const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
122
+ const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
123
+
120
124
  const UNDEFINED_TYPE = "undefined";
121
125
  const FUNCTION_TYPE = "function";
122
126
  const OBJECT_TYPE = "object";
@@ -212,6 +216,7 @@ const ERR_INVALID_FUNCTION_OPTION = "Invalid option (must be a function)";
212
216
  const ERR_INVALID_SIGNAL = "Invalid signal (must be an AbortSignal instance)";
213
217
  const ERR_INVALID_PASSWORD_TYPE = "Invalid password (password must be a string, rawPassword must be a Uint8Array)";
214
218
  const ERR_INVALID_PASS_THROUGH_VALUE = "Invalid passThrough option (must be a boolean or 'compressed')";
219
+ const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
215
220
  const ERR_ABORTED = "The operation was aborted";
216
221
  const ABORT_ERROR_NAME$1 = "AbortError";
217
222
 
@@ -222,6 +227,13 @@ function checkFunctionOption(value) {
222
227
  return value;
223
228
  }
224
229
 
230
+ function checkReaderOptionsObject(readerOptions) {
231
+ if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
232
+ throw new Error(ERR_INVALID_READER_OPTIONS);
233
+ }
234
+ return readerOptions;
235
+ }
236
+
225
237
  function checkSignalOption(signal) {
226
238
  if (signal && (typeof signal.addEventListener != FUNCTION_TYPE || typeof signal.aborted != BOOLEAN_TYPE)) {
227
239
  throw new Error(ERR_INVALID_SIGNAL);
@@ -317,7 +329,6 @@ const DEFAULT_CONFIGURATION = {
317
329
  workerStartupTimeout: 5000,
318
330
  useWebWorkers: true,
319
331
  useCompressionStream: true,
320
- transferStreams: true,
321
332
  CompressionStream: typeof CompressionStream != UNDEFINED_TYPE && CompressionStream,
322
333
  DecompressionStream: typeof DecompressionStream != UNDEFINED_TYPE && DecompressionStream
323
334
  };
@@ -331,8 +342,7 @@ const URI_PROPERTY_NAMES = [
331
342
  ];
332
343
  const BOOLEAN_PROPERTY_NAMES = [
333
344
  "useCompressionStream",
334
- "useWebWorkers",
335
- "transferStreams"
345
+ "useWebWorkers"
336
346
  ];
337
347
  const NUMBER_PROPERTY_NAMES = [
338
348
  "chunkSize",
@@ -4810,12 +4820,9 @@ const ERR_BAD_FORMAT = "File format is not recognized";
4810
4820
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4811
4821
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4812
4822
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4813
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4814
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4815
4823
  const ERR_ENCRYPTED = "File contains encrypted entry";
4816
4824
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4817
4825
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4818
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4819
4826
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4820
4827
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4821
4828
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4879,8 +4886,9 @@ class ZipReader {
4879
4886
  constructor(reader, options = {}) {
4880
4887
  Object.assign(this, {
4881
4888
  reader: new GenericReader(reader),
4882
- options,
4883
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4889
+ options: options || {},
4890
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4891
+ entriesMissingZip64ExtraField: new WeakSet()
4884
4892
  });
4885
4893
  }
4886
4894
 
@@ -4951,16 +4959,22 @@ class ZipReader {
4951
4959
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4952
4960
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4953
4961
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4954
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4955
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4956
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4957
- const originalDirectoryDataOffset = directoryDataOffset;
4958
- directoryDataOffset = expectedDirectoryDataOffset;
4959
- if (directoryDataOffset > originalDirectoryDataOffset) {
4960
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4962
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4963
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4964
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4965
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4966
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4967
+ }
4968
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4969
+ directoryDataOffset != expectedDirectoryDataOffset) {
4970
+ const originalDirectoryDataOffset = directoryDataOffset;
4971
+ directoryDataOffset = expectedDirectoryDataOffset;
4972
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4973
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4974
+ }
4975
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4976
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4961
4977
  }
4962
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4963
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4964
4978
  }
4965
4979
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4966
4980
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -5005,10 +5019,6 @@ class ZipReader {
5005
5019
  let declaredDirectoryDataLength = directoryDataLength;
5006
5020
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
5007
5021
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
5008
- if (directoryDataOffset >= reader.size) {
5009
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
5010
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
5011
- }
5012
5022
  if (expectedLastDiskNumber != lastDiskNumber) {
5013
5023
  throw new Error(ERR_SPLIT_ZIP_FILE);
5014
5024
  }
@@ -5016,52 +5026,64 @@ class ZipReader {
5016
5026
  throw new Error(ERR_BAD_FORMAT);
5017
5027
  }
5018
5028
  let offset = 0;
5019
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5029
+ let directoryArray = directoryDataOffset < reader.size ?
5030
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
5031
+ EMPTY_UINT8_ARRAY;
5020
5032
  let directoryView = getDataView(directoryArray);
5021
5033
  if (directoryDataLength) {
5022
- if (directoryArray.length < 4) {
5023
- throw new Error(ERR_BAD_FORMAT);
5024
- }
5025
5034
  let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
5026
5035
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
5027
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
5036
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
5037
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
5028
5038
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
5029
- detectEncryptedCentralDirectory(directoryView);
5039
+ detectEncryptedCentralDirectory(directoryView));
5040
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
5041
+ throw new Error(ERR_BAD_FORMAT);
5042
+ }
5030
5043
  let reconcile = !storedPointsAtDirectory;
5031
5044
  if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
5032
- const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5033
- if (storedPointsAtDirectory) {
5034
- reconcile = expectedPointsAtDirectory;
5035
- } else if (!expectedPointsAtDirectory) {
5045
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5046
+ if (!expectedPointsAtDirectory) {
5036
5047
  const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
5037
5048
  const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
5038
5049
  if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5039
5050
  await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5040
5051
  expectedDirectoryDataOffset = signedDirectoryDataOffset;
5052
+ expectedPointsAtDirectory = true;
5041
5053
  }
5042
5054
  }
5055
+ if (storedPointsAtDirectory) {
5056
+ reconcile = expectedPointsAtDirectory;
5057
+ }
5043
5058
  }
5044
- if (reconcile) {
5059
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
5045
5060
  const originalDirectoryDataOffset = directoryDataOffset;
5046
5061
  directoryDataOffset = expectedDirectoryDataOffset;
5047
5062
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5048
5063
  directoryView = getDataView(directoryArray);
5049
- if (directoryDataOffset > originalDirectoryDataOffset) {
5050
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5064
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5065
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5066
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5067
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5068
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5069
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5070
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5071
+ prependedDataLength += offsetDelta;
5051
5072
  prependedCentralDirectory = storedPointsAtDirectory;
5052
5073
  } else {
5053
5074
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5054
- const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5055
- if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
5056
- const localHeaderOffset = getUint32$1(directoryView, 42);
5057
- if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
5058
- await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
5059
- prependedDataLength += offsetDelta;
5060
- }
5075
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5076
+ prependedDataLength += offsetDelta;
5061
5077
  }
5062
5078
  }
5063
5079
  }
5064
5080
  }
5081
+ if (directoryArray.length < 4) {
5082
+ throw new Error(ERR_BAD_FORMAT);
5083
+ }
5084
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5085
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5086
+ directoryDataOffset = centralDirectoryEndOffset;
5065
5087
  }
5066
5088
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5067
5089
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5103,8 +5125,10 @@ class ZipReader {
5103
5125
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5104
5126
  }
5105
5127
  }
5128
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5129
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5106
5130
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5107
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5131
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5108
5132
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5109
5133
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5110
5134
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5195,13 +5219,17 @@ class ZipReader {
5195
5219
  if (malformedExtraField) {
5196
5220
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5197
5221
  }
5198
- fileEntry.offset += prependedDataLength;
5199
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5200
- startOffset = Math.min(entryPosition, startOffset);
5201
- if (entryPosition < previousEntryPosition) {
5202
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5222
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5223
+ startOffset = 0;
5224
+ } else {
5225
+ fileEntry.offset += prependedDataLength;
5226
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5227
+ startOffset = Math.min(entryPosition, startOffset);
5228
+ if (entryPosition < previousEntryPosition) {
5229
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5230
+ }
5231
+ previousEntryPosition = entryPosition;
5203
5232
  }
5204
- previousEntryPosition = entryPosition;
5205
5233
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5206
5234
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5207
5235
  }
@@ -5238,12 +5266,15 @@ class ZipReader {
5238
5266
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5239
5267
  });
5240
5268
  const entry = new Entry(fileEntry);
5241
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5269
+ if (fileEntry.zip64ExtraFieldMissing) {
5270
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5271
+ }
5272
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5242
5273
  entry.arrayBuffer = async options => {
5243
5274
  const writer = new TransformStream();
5244
5275
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5245
5276
  arrayBufferPromise.catch(() => { });
5246
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5277
+ await fileEntry.getData(writer, entry, zipReader,
5247
5278
  Object.assign({}, options, { preventClose: false }));
5248
5279
  return arrayBufferPromise;
5249
5280
  };
@@ -5451,8 +5482,9 @@ let ZipEntry$1 = class ZipEntry {
5451
5482
  });
5452
5483
  }
5453
5484
 
5454
- async getData(writer, fileEntry, readRanges, options = {}) {
5485
+ async getData(writer, fileEntry, zipReader, options = {}) {
5455
5486
  const zipEntry = this;
5487
+ const { readRanges, directoryOffset } = zipReader;
5456
5488
  if (zipEntry.zip64ExtraFieldMissing) {
5457
5489
  throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5458
5490
  }
@@ -5526,10 +5558,10 @@ let ZipEntry$1 = class ZipEntry {
5526
5558
  }
5527
5559
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5528
5560
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5529
- if (lastAccessDate) {
5561
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5530
5562
  fileEntry.lastAccessDate = lastAccessDate;
5531
5563
  }
5532
- if (creationDate) {
5564
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5533
5565
  fileEntry.creationDate = creationDate;
5534
5566
  }
5535
5567
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5558,7 +5590,7 @@ let ZipEntry$1 = class ZipEntry {
5558
5590
  throw new Error(ERR_ENCRYPTED);
5559
5591
  }
5560
5592
  }
5561
- if (dataOffset + compressedSize > reader.size) {
5593
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5562
5594
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5563
5595
  }
5564
5596
  const size = compressedSize;
@@ -5624,6 +5656,7 @@ let ZipEntry$1 = class ZipEntry {
5624
5656
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5625
5657
  readRanges
5626
5658
  });
5659
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5627
5660
  }
5628
5661
  let writable, abortError, aborted;
5629
5662
  try {
@@ -5643,10 +5676,7 @@ let ZipEntry$1 = class ZipEntry {
5643
5676
  writer.size += writtenSize;
5644
5677
  }
5645
5678
  } catch (error) {
5646
- const { outputSize: failedOutputSize } = workerOptions;
5647
- if (failedOutputSize !== UNDEFINED_VALUE) {
5648
- writer.size += failedOutputSize;
5649
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5679
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5650
5680
  writer.size += error.outputSize;
5651
5681
  }
5652
5682
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5674,13 +5704,7 @@ let ZipEntry$1 = class ZipEntry {
5674
5704
  };
5675
5705
 
5676
5706
  function detectEncryptedCentralDirectory(directoryView) {
5677
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5678
- for (let offset = 0; offset < maxOffset; offset++) {
5679
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5680
- return true;
5681
- }
5682
- }
5683
- return false;
5707
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5684
5708
  }
5685
5709
 
5686
5710
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5717,6 +5741,46 @@ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5717
5741
  }
5718
5742
  }
5719
5743
 
5744
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5745
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5746
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5747
+ const tailView = getDataView(tailArray);
5748
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5749
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5750
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5751
+ return startOffset + indexByte;
5752
+ }
5753
+ }
5754
+ }
5755
+
5756
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5757
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5758
+ return UNDEFINED_VALUE;
5759
+ }
5760
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5761
+ if (localHeaderOffset != MAX_32_BITS) {
5762
+ return localHeaderOffset;
5763
+ }
5764
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5765
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5766
+ while (offsetExtraField + 4 <= endExtraField) {
5767
+ const type = getUint16$1(directoryView, offsetExtraField);
5768
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5769
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5770
+ let offsetValue = offsetExtraField + 4;
5771
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5772
+ offsetValue += 8;
5773
+ }
5774
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5775
+ offsetValue += 8;
5776
+ }
5777
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5778
+ }
5779
+ offsetExtraField += 4 + size;
5780
+ }
5781
+ return UNDEFINED_VALUE;
5782
+ }
5783
+
5720
5784
  function readDigitalSignature(signatureRecordArray) {
5721
5785
  if (signatureRecordArray.length >= 6) {
5722
5786
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5806,6 +5870,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5806
5870
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5807
5871
  });
5808
5872
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5873
+ let extraFieldZip64Missing;
5809
5874
  if (extraFieldZip64) {
5810
5875
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5811
5876
  malformedExtraField = true;
@@ -5815,7 +5880,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5815
5880
  if (localDirectory) {
5816
5881
  malformedExtraField = true;
5817
5882
  } else {
5818
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5883
+ extraFieldZip64Missing = true;
5819
5884
  }
5820
5885
  }
5821
5886
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5873,6 +5938,9 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5873
5938
  if (extraFieldUSDZ) {
5874
5939
  directory.extraFieldUSDZ = extraFieldUSDZ;
5875
5940
  }
5941
+ if (extraFieldZip64Missing) {
5942
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5943
+ }
5876
5944
  return malformedExtraField;
5877
5945
  }
5878
5946
 
@@ -6087,13 +6155,7 @@ async function detectOverlappingEntry({
6087
6155
  let dataDescriptorLength = 0;
6088
6156
  if (dataDescriptor) {
6089
6157
  const zip64 = Boolean(extraFieldZip64);
6090
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
6091
- const dataDescriptorView = getDataView(dataDescriptorArray);
6092
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6093
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6094
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6095
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6096
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6158
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
6097
6159
  if (localDataDescriptor) {
6098
6160
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6099
6161
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6163,6 +6225,25 @@ function mergeRanges(sortedRanges, pendingRanges) {
6163
6225
  return mergedRanges;
6164
6226
  }
6165
6227
 
6228
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6229
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6230
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6231
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6232
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6233
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6234
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6235
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6236
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6237
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6238
+ }
6239
+
6240
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6241
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6242
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6243
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6244
+ }
6245
+ }
6246
+
6166
6247
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6167
6248
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6168
6249
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6422,6 +6503,18 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
6422
6503
  }
6423
6504
  }
6424
6505
 
6506
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6507
+ const { dataDescriptor } = localDirectory;
6508
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6509
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6510
+ if (dataDescriptor && !maskedLocalDirectory &&
6511
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6512
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6513
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6514
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6515
+ }
6516
+ }
6517
+
6425
6518
  function reportAmbiguity(reject, warnings, reason) {
6426
6519
  if (reject) {
6427
6520
  throwAmbiguousArchive(reason);
@@ -6524,6 +6617,7 @@ var zipReader = /*#__PURE__*/Object.freeze({
6524
6617
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6525
6618
  ZipReader: ZipReader,
6526
6619
  ZipReaderStream: ZipReaderStream,
6620
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6527
6621
  isZipFile: isZipFile
6528
6622
  });
6529
6623
 
@@ -6647,136 +6741,7 @@ class ZipWriter {
6647
6741
  }
6648
6742
 
6649
6743
  appendZip(reader, options = {}) {
6650
- return watchPromiseError(this, this.appendZipEntries(reader, options));
6651
- }
6652
-
6653
- async appendZipEntries(reader, options = {}) {
6654
- const zipWriter = this;
6655
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6656
- const filter = checkFunctionOption(options.filter);
6657
- while (pendingAddFileCalls.size) {
6658
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6659
- }
6660
- let resolveAppendZip;
6661
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6662
- pendingAddFileCalls.add(promiseAppendZip);
6663
- const appendedFilenames = [];
6664
- let releaseLockWriter;
6665
- try {
6666
- reader = new GenericReader(reader);
6667
- await initStream(reader);
6668
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6669
- reader = new BlobReader(await streamToBlob(reader.readable));
6670
- await initStream(reader);
6671
- }
6672
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6673
- const zipReader$1 = new ZipReader(reader);
6674
- const entries = await zipReader$1.getEntries();
6675
- const keptEntries = [];
6676
- for (const entry of entries) {
6677
- if (!filter || await filter(entry)) {
6678
- keptEntries.push(entry);
6679
- }
6680
- }
6681
- await zipReader$1.close();
6682
- await initStream(zipWriter.writer);
6683
- const { directoryOffset } = zipReader$1;
6684
- keptEntries.forEach(({ filename }) => {
6685
- if (filenames.has(filename)) {
6686
- throw new Error(ERR_DUPLICATED_NAME);
6687
- }
6688
- filenames.add(filename);
6689
- appendedFilenames.push(filename);
6690
- });
6691
- zipWriter.writerLocked = true;
6692
- const { lockWriter } = zipWriter;
6693
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6694
- zipWriter.writerLocked = false;
6695
- resolve();
6696
- });
6697
- await lockWriter;
6698
- if (zipWriter.addSplitZipSignature) {
6699
- delete zipWriter.addSplitZipSignature;
6700
- if (!await startsWithSplitZipSignature(reader)) {
6701
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6702
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6703
- }
6704
- }
6705
- const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
6706
- keptEntries.forEach(entry => {
6707
- const {
6708
- version,
6709
- rawLastModDate,
6710
- rawFilename,
6711
- bitFlag,
6712
- encrypted,
6713
- uncompressedSize,
6714
- compressedSize,
6715
- extraFieldZip64
6716
- } = entry;
6717
- let {
6718
- compressionMethod,
6719
- rawExtraField,
6720
- } = entry;
6721
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6722
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6723
- if (entry.extraFieldAES) {
6724
- compressionMethod = COMPRESSION_METHOD_AES;
6725
- }
6726
- const extraFieldLength = getLength(rawExtraField);
6727
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6728
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6729
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6730
- const {
6731
- headerArray,
6732
- headerView
6733
- } = getHeaderArrayData({
6734
- version,
6735
- bitFlag: bitFlagValue,
6736
- compressionMethod,
6737
- uncompressedSize,
6738
- compressedSize,
6739
- rawLastModDate,
6740
- rawFilename,
6741
- zip64CompressedSize,
6742
- zip64UncompressedSize,
6743
- extraFieldLength
6744
- });
6745
- const { crc32 } = entry;
6746
- if (crc32 !== UNDEFINED_VALUE) {
6747
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6748
- }
6749
- const { offset, diskNumberStart } = entryPositions.get(entry);
6750
- Object.assign(entry, {
6751
- zip64Enabled: true,
6752
- zip64UncompressedSize,
6753
- zip64CompressedSize,
6754
- offset,
6755
- diskNumberStart,
6756
- zip64DiskNumberStart: false,
6757
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6758
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6759
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6760
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6761
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6762
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6763
- rawExtraField,
6764
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6765
- headerArray,
6766
- headerView
6767
- });
6768
- fileEntries.set(entry.filename, entry);
6769
- });
6770
- } catch (error) {
6771
- appendedFilenames.forEach(filename => filenames.delete(filename));
6772
- throw error;
6773
- } finally {
6774
- resolveAppendZip();
6775
- pendingAddFileCalls.delete(promiseAppendZip);
6776
- if (releaseLockWriter) {
6777
- releaseLockWriter();
6778
- }
6779
- }
6744
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6780
6745
  }
6781
6746
 
6782
6747
  add(name = "", reader, options = {}) {
@@ -6938,11 +6903,149 @@ function watchPromiseError(zipWriter, promise) {
6938
6903
  return watchedPromise;
6939
6904
  }
6940
6905
 
6906
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6907
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6908
+ const filter = checkFunctionOption(options.filter);
6909
+ const readerOptions = checkReaderOptionsObject(options.readerOptions);
6910
+ while (pendingAddFileCalls.size) {
6911
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6912
+ }
6913
+ let resolveAppendZip;
6914
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6915
+ pendingAddFileCalls.add(promiseAppendZip);
6916
+ const appendedFilenames = [];
6917
+ let releaseLockWriter;
6918
+ try {
6919
+ reader = new GenericReader(reader);
6920
+ await initStream(reader);
6921
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6922
+ reader = new BlobReader(await streamToBlob(reader.readable));
6923
+ await initStream(reader);
6924
+ }
6925
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6926
+ const zipReader$1 = new ZipReader(reader, readerOptions);
6927
+ const keptEntries = [];
6928
+ let entries;
6929
+ try {
6930
+ entries = await zipReader$1.getEntries();
6931
+ for (const entry of entries) {
6932
+ if (!filter || await filter(entry, getExistingEntry(fileEntries, entry.filename))) {
6933
+ keptEntries.push(entry);
6934
+ }
6935
+ }
6936
+ } finally {
6937
+ await zipReader$1.close();
6938
+ }
6939
+ await initStream(zipWriter.writer);
6940
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6941
+ keptEntries.forEach(entry => {
6942
+ const { filename } = entry;
6943
+ if (entriesMissingZip64ExtraField.has(entry)) {
6944
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6945
+ }
6946
+ if (filenames.has(filename)) {
6947
+ throw new Error(ERR_DUPLICATED_NAME);
6948
+ }
6949
+ filenames.add(filename);
6950
+ appendedFilenames.push(filename);
6951
+ });
6952
+ zipWriter.writerLocked = true;
6953
+ const { lockWriter } = zipWriter;
6954
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6955
+ zipWriter.writerLocked = false;
6956
+ resolve();
6957
+ });
6958
+ await lockWriter;
6959
+ if (zipWriter.addSplitZipSignature) {
6960
+ delete zipWriter.addSplitZipSignature;
6961
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6962
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6963
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6964
+ }
6965
+ }
6966
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6967
+ keptEntries.forEach(entry => {
6968
+ const {
6969
+ version,
6970
+ rawLastModDate,
6971
+ rawFilename,
6972
+ rawBitFlag,
6973
+ uncompressedSize,
6974
+ compressedSize,
6975
+ extraFieldZip64
6976
+ } = entry;
6977
+ let {
6978
+ compressionMethod,
6979
+ rawExtraField,
6980
+ } = entry;
6981
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6982
+ if (entry.extraFieldAES) {
6983
+ compressionMethod = COMPRESSION_METHOD_AES;
6984
+ }
6985
+ const extraFieldLength = getLength(rawExtraField);
6986
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6987
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6988
+ const {
6989
+ headerArray,
6990
+ headerView
6991
+ } = getHeaderArrayData({
6992
+ version,
6993
+ bitFlag: rawBitFlag,
6994
+ compressionMethod,
6995
+ uncompressedSize,
6996
+ compressedSize,
6997
+ rawLastModDate,
6998
+ rawFilename,
6999
+ zip64CompressedSize,
7000
+ zip64UncompressedSize,
7001
+ extraFieldLength
7002
+ });
7003
+ const { crc32 } = entry;
7004
+ if (crc32 !== UNDEFINED_VALUE) {
7005
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
7006
+ }
7007
+ const { offset, diskNumberStart } = entryPositions.get(entry);
7008
+ fileEntries.set(entry.filename, Object.assign({}, entry, {
7009
+ zip64Enabled: true,
7010
+ zip64UncompressedSize,
7011
+ zip64CompressedSize,
7012
+ offset,
7013
+ diskNumberStart,
7014
+ zip64DiskNumberStart: false,
7015
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
7016
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
7017
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
7018
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
7019
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
7020
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
7021
+ rawExtraField,
7022
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
7023
+ headerArray,
7024
+ headerView
7025
+ }));
7026
+ });
7027
+ } catch (error) {
7028
+ appendedFilenames.forEach(filename => filenames.delete(filename));
7029
+ throw error;
7030
+ } finally {
7031
+ resolveAppendZip();
7032
+ pendingAddFileCalls.delete(promiseAppendZip);
7033
+ if (releaseLockWriter) {
7034
+ releaseLockWriter();
7035
+ }
7036
+ }
7037
+ }
7038
+
7039
+ function getExistingEntry(fileEntries, filename) {
7040
+ const fileEntry = fileEntries.get(filename);
7041
+ return fileEntry ? new Entry(fileEntry) : UNDEFINED_VALUE;
7042
+ }
7043
+
6941
7044
  async function prependZipEntries(zipWriter, reader) {
6942
7045
  if (zipWriter.filenames.size) {
6943
7046
  throw new Error(ERR_ZIP_NOT_EMPTY);
6944
7047
  }
6945
- await zipWriter.appendZipEntries(reader);
7048
+ await appendZipEntries(zipWriter, reader);
6946
7049
  }
6947
7050
 
6948
7051
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8714,7 +8817,7 @@ function removeExtraFieldZip64(rawExtraField) {
8714
8817
  return rawExtraField;
8715
8818
  }
8716
8819
 
8717
- async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
8820
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8718
8821
  const { writer } = zipWriter;
8719
8822
  const entryPositions = new Map();
8720
8823
  if (writer.closeDisk || compact) {
@@ -8722,6 +8825,27 @@ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOff
8722
8825
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8723
8826
  const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8724
8827
  const keptEntrySet = new Set(keptEntries);
8828
+ const localHeaderLengths = new Map();
8829
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8830
+ const entry = sortedEntries[indexEntry];
8831
+ if (keptEntrySet.has(entry)) {
8832
+ const sourceOffset = getSourceOffset(reader, entry);
8833
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8834
+ if (localHeaderLength === UNDEFINED_VALUE) {
8835
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8836
+ }
8837
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8838
+ if (entry.bitFlag.dataDescriptor) {
8839
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8840
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8841
+ }
8842
+ if (entryEnd > regionEnds[indexEntry]) {
8843
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8844
+ }
8845
+ regionEnds[indexEntry] = entryEnd;
8846
+ localHeaderLengths.set(entry, localHeaderLength);
8847
+ }
8848
+ }
8725
8849
  let copiedOffset = 0;
8726
8850
  let lastPosition;
8727
8851
  for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
@@ -8734,7 +8858,7 @@ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOff
8734
8858
  if (!compact) {
8735
8859
  await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8736
8860
  }
8737
- if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8861
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8738
8862
  await writer.closeDisk();
8739
8863
  }
8740
8864
  lastPosition = {
@@ -8768,7 +8892,7 @@ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8768
8892
  const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8769
8893
  const nextEntry = sortedEntries[indexEntry + 1];
8770
8894
  if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8771
- regionEnd = getSourceOffset(reader, nextEntry);
8895
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8772
8896
  }
8773
8897
  regionEnds[indexEntry] = regionEnd;
8774
8898
  }
@@ -8782,11 +8906,13 @@ async function copyData(zipWriter, reader, offset, size) {
8782
8906
  try {
8783
8907
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8784
8908
  } catch (error) {
8785
- zipWriter.hasCorruptedEntries = true;
8786
- try {
8787
- error.corruptedEntry = true;
8788
- } catch {
8789
- // ignored
8909
+ if (copiedLength) {
8910
+ zipWriter.hasCorruptedEntries = true;
8911
+ try {
8912
+ error.corruptedEntry = true;
8913
+ } catch {
8914
+ // ignored
8915
+ }
8790
8916
  }
8791
8917
  throw error;
8792
8918
  } finally {
@@ -8797,11 +8923,16 @@ async function copyData(zipWriter, reader, offset, size) {
8797
8923
  }
8798
8924
 
8799
8925
  async function getLocalHeaderLength(reader, offset) {
8800
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8926
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8927
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8928
+ EMPTY_UINT8_ARRAY;
8801
8929
  if (getLength(headerArray) < HEADER_SIZE) {
8802
- return HEADER_SIZE;
8930
+ return UNDEFINED_VALUE;
8803
8931
  }
8804
8932
  const headerView = getDataView(headerArray);
8933
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8934
+ return UNDEFINED_VALUE;
8935
+ }
8805
8936
  return HEADER_SIZE +
8806
8937
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8807
8938
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9152,7 +9283,7 @@ function formatSupported(StreamClass, format) {
9152
9283
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9153
9284
  */
9154
9285
 
9155
- const VERSION = "2.20.0";
9286
+ const VERSION = "2.22.0";
9156
9287
 
9157
9288
  /*
9158
9289
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -10199,7 +10330,6 @@ const DUPLICATES_KEEP_FIRST = "keep-first";
10199
10330
  const DUPLICATES_KEEP_LAST = "keep-last";
10200
10331
  const DUPLICATES_VALUES = new Set([DUPLICATES_THROW, DUPLICATES_KEEP_FIRST, DUPLICATES_KEEP_LAST]);
10201
10332
  const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
10202
- const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
10203
10333
  const ERR_UNSUPPORTED_PASS_THROUGH_VALUE = "The 'compressed' passThrough option is only supported by Entry#getData() and ZipWriter#add()";
10204
10334
  const ERR_INVALID_PASSWORDS = "Invalid passwords option (must be an array of strings)";
10205
10335
  const ERR_INVALID_REQUEST_PASSWORD = "Invalid requestPassword option (must be a function returning a string or undefined)";
@@ -11125,9 +11255,7 @@ function getDeterminedSize(child, passThrough) {
11125
11255
  }
11126
11256
 
11127
11257
  function checkReaderOptions(readerOptions) {
11128
- if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
11129
- throw new Error(ERR_INVALID_READER_OPTIONS);
11130
- }
11258
+ checkReaderOptionsObject(readerOptions);
11131
11259
  if (readerOptions) {
11132
11260
  checkPassThroughValue(readerOptions.passThrough);
11133
11261
  checkPasswordCandidatesOptions(readerOptions);