@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.
@@ -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";
@@ -221,6 +225,7 @@
221
225
  const ERR_INVALID_SIGNAL = "Invalid signal (must be an AbortSignal instance)";
222
226
  const ERR_INVALID_PASSWORD_TYPE = "Invalid password (password must be a string, rawPassword must be a Uint8Array)";
223
227
  const ERR_INVALID_PASS_THROUGH_VALUE = "Invalid passThrough option (must be a boolean or 'compressed')";
228
+ const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
224
229
  const ERR_ABORTED = "The operation was aborted";
225
230
  const ABORT_ERROR_NAME$1 = "AbortError";
226
231
 
@@ -231,6 +236,13 @@
231
236
  return value;
232
237
  }
233
238
 
239
+ function checkReaderOptionsObject(readerOptions) {
240
+ if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
241
+ throw new Error(ERR_INVALID_READER_OPTIONS);
242
+ }
243
+ return readerOptions;
244
+ }
245
+
234
246
  function checkSignalOption(signal) {
235
247
  if (signal && (typeof signal.addEventListener != FUNCTION_TYPE || typeof signal.aborted != BOOLEAN_TYPE)) {
236
248
  throw new Error(ERR_INVALID_SIGNAL);
@@ -326,7 +338,6 @@
326
338
  workerStartupTimeout: 5000,
327
339
  useWebWorkers: true,
328
340
  useCompressionStream: true,
329
- transferStreams: true,
330
341
  CompressionStream: typeof CompressionStream != UNDEFINED_TYPE && CompressionStream,
331
342
  DecompressionStream: typeof DecompressionStream != UNDEFINED_TYPE && DecompressionStream
332
343
  };
@@ -340,8 +351,7 @@
340
351
  ];
341
352
  const BOOLEAN_PROPERTY_NAMES = [
342
353
  "useCompressionStream",
343
- "useWebWorkers",
344
- "transferStreams"
354
+ "useWebWorkers"
345
355
  ];
346
356
  const NUMBER_PROPERTY_NAMES = [
347
357
  "chunkSize",
@@ -4775,12 +4785,9 @@
4775
4785
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4776
4786
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4777
4787
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4778
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4779
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4780
4788
  const ERR_ENCRYPTED = "File contains encrypted entry";
4781
4789
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4782
4790
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4783
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4784
4791
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4785
4792
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4786
4793
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4844,8 +4851,9 @@
4844
4851
  constructor(reader, options = {}) {
4845
4852
  Object.assign(this, {
4846
4853
  reader: new GenericReader(reader),
4847
- options,
4848
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4854
+ options: options || {},
4855
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4856
+ entriesMissingZip64ExtraField: new WeakSet()
4849
4857
  });
4850
4858
  }
4851
4859
 
@@ -4916,16 +4924,22 @@
4916
4924
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4917
4925
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4918
4926
  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;
4927
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4928
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4929
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4930
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4931
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4932
+ }
4933
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4934
+ directoryDataOffset != expectedDirectoryDataOffset) {
4935
+ const originalDirectoryDataOffset = directoryDataOffset;
4936
+ directoryDataOffset = expectedDirectoryDataOffset;
4937
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4938
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4939
+ }
4940
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4941
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4926
4942
  }
4927
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4928
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4929
4943
  }
4930
4944
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4931
4945
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4970,10 +4984,6 @@
4970
4984
  let declaredDirectoryDataLength = directoryDataLength;
4971
4985
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4972
4986
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4973
- if (directoryDataOffset >= reader.size) {
4974
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4975
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4976
- }
4977
4987
  if (expectedLastDiskNumber != lastDiskNumber) {
4978
4988
  throw new Error(ERR_SPLIT_ZIP_FILE);
4979
4989
  }
@@ -4981,52 +4991,64 @@
4981
4991
  throw new Error(ERR_BAD_FORMAT);
4982
4992
  }
4983
4993
  let offset = 0;
4984
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4994
+ let directoryArray = directoryDataOffset < reader.size ?
4995
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
4996
+ EMPTY_UINT8_ARRAY;
4985
4997
  let directoryView = getDataView(directoryArray);
4986
4998
  if (directoryDataLength) {
4987
- if (directoryArray.length < 4) {
4988
- throw new Error(ERR_BAD_FORMAT);
4989
- }
4990
4999
  let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4991
5000
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4992
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
5001
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
5002
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4993
5003
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4994
- detectEncryptedCentralDirectory(directoryView);
5004
+ detectEncryptedCentralDirectory(directoryView));
5005
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
5006
+ throw new Error(ERR_BAD_FORMAT);
5007
+ }
4995
5008
  let reconcile = !storedPointsAtDirectory;
4996
5009
  if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4997
- const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
4998
- if (storedPointsAtDirectory) {
4999
- reconcile = expectedPointsAtDirectory;
5000
- } else if (!expectedPointsAtDirectory) {
5010
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5011
+ if (!expectedPointsAtDirectory) {
5001
5012
  const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
5002
5013
  const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
5003
5014
  if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5004
5015
  await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5005
5016
  expectedDirectoryDataOffset = signedDirectoryDataOffset;
5017
+ expectedPointsAtDirectory = true;
5006
5018
  }
5007
5019
  }
5020
+ if (storedPointsAtDirectory) {
5021
+ reconcile = expectedPointsAtDirectory;
5022
+ }
5008
5023
  }
5009
- if (reconcile) {
5024
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
5010
5025
  const originalDirectoryDataOffset = directoryDataOffset;
5011
5026
  directoryDataOffset = expectedDirectoryDataOffset;
5012
5027
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5013
5028
  directoryView = getDataView(directoryArray);
5014
- if (directoryDataOffset > originalDirectoryDataOffset) {
5015
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5029
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5030
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5031
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5032
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5033
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5034
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5035
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5036
+ prependedDataLength += offsetDelta;
5016
5037
  prependedCentralDirectory = storedPointsAtDirectory;
5017
5038
  } else {
5018
5039
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5019
- const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5020
- if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
5021
- const localHeaderOffset = getUint32$1(directoryView, 42);
5022
- if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
5023
- await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
5024
- prependedDataLength += offsetDelta;
5025
- }
5040
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5041
+ prependedDataLength += offsetDelta;
5026
5042
  }
5027
5043
  }
5028
5044
  }
5029
5045
  }
5046
+ if (directoryArray.length < 4) {
5047
+ throw new Error(ERR_BAD_FORMAT);
5048
+ }
5049
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5050
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5051
+ directoryDataOffset = centralDirectoryEndOffset;
5030
5052
  }
5031
5053
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5032
5054
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5068,8 +5090,10 @@
5068
5090
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5069
5091
  }
5070
5092
  }
5093
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5094
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5071
5095
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5072
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5096
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5073
5097
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5074
5098
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5075
5099
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5160,13 +5184,17 @@
5160
5184
  if (malformedExtraField) {
5161
5185
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5162
5186
  }
5163
- fileEntry.offset += prependedDataLength;
5164
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5165
- startOffset = Math.min(entryPosition, startOffset);
5166
- if (entryPosition < previousEntryPosition) {
5167
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5187
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5188
+ startOffset = 0;
5189
+ } else {
5190
+ fileEntry.offset += prependedDataLength;
5191
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5192
+ startOffset = Math.min(entryPosition, startOffset);
5193
+ if (entryPosition < previousEntryPosition) {
5194
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5195
+ }
5196
+ previousEntryPosition = entryPosition;
5168
5197
  }
5169
- previousEntryPosition = entryPosition;
5170
5198
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5171
5199
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5172
5200
  }
@@ -5203,12 +5231,15 @@
5203
5231
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5204
5232
  });
5205
5233
  const entry = new Entry(fileEntry);
5206
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5234
+ if (fileEntry.zip64ExtraFieldMissing) {
5235
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5236
+ }
5237
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5207
5238
  entry.arrayBuffer = async options => {
5208
5239
  const writer = new TransformStream();
5209
5240
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5210
5241
  arrayBufferPromise.catch(() => { });
5211
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5242
+ await fileEntry.getData(writer, entry, zipReader,
5212
5243
  Object.assign({}, options, { preventClose: false }));
5213
5244
  return arrayBufferPromise;
5214
5245
  };
@@ -5416,8 +5447,9 @@
5416
5447
  });
5417
5448
  }
5418
5449
 
5419
- async getData(writer, fileEntry, readRanges, options = {}) {
5450
+ async getData(writer, fileEntry, zipReader, options = {}) {
5420
5451
  const zipEntry = this;
5452
+ const { readRanges, directoryOffset } = zipReader;
5421
5453
  if (zipEntry.zip64ExtraFieldMissing) {
5422
5454
  throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5423
5455
  }
@@ -5491,10 +5523,10 @@
5491
5523
  }
5492
5524
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5493
5525
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5494
- if (lastAccessDate) {
5526
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5495
5527
  fileEntry.lastAccessDate = lastAccessDate;
5496
5528
  }
5497
- if (creationDate) {
5529
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5498
5530
  fileEntry.creationDate = creationDate;
5499
5531
  }
5500
5532
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5523,7 +5555,7 @@
5523
5555
  throw new Error(ERR_ENCRYPTED);
5524
5556
  }
5525
5557
  }
5526
- if (dataOffset + compressedSize > reader.size) {
5558
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5527
5559
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5528
5560
  }
5529
5561
  const size = compressedSize;
@@ -5589,6 +5621,7 @@
5589
5621
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5590
5622
  readRanges
5591
5623
  });
5624
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5592
5625
  }
5593
5626
  let writable, abortError, aborted;
5594
5627
  try {
@@ -5608,10 +5641,7 @@
5608
5641
  writer.size += writtenSize;
5609
5642
  }
5610
5643
  } catch (error) {
5611
- const { outputSize: failedOutputSize } = workerOptions;
5612
- if (failedOutputSize !== UNDEFINED_VALUE) {
5613
- writer.size += failedOutputSize;
5614
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5644
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5615
5645
  writer.size += error.outputSize;
5616
5646
  }
5617
5647
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5639,13 +5669,7 @@
5639
5669
  };
5640
5670
 
5641
5671
  function detectEncryptedCentralDirectory(directoryView) {
5642
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5643
- for (let offset = 0; offset < maxOffset; offset++) {
5644
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5645
- return true;
5646
- }
5647
- }
5648
- return false;
5672
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5649
5673
  }
5650
5674
 
5651
5675
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5682,6 +5706,46 @@
5682
5706
  }
5683
5707
  }
5684
5708
 
5709
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5710
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5711
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5712
+ const tailView = getDataView(tailArray);
5713
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5714
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5715
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5716
+ return startOffset + indexByte;
5717
+ }
5718
+ }
5719
+ }
5720
+
5721
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5722
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5723
+ return UNDEFINED_VALUE;
5724
+ }
5725
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5726
+ if (localHeaderOffset != MAX_32_BITS) {
5727
+ return localHeaderOffset;
5728
+ }
5729
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5730
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5731
+ while (offsetExtraField + 4 <= endExtraField) {
5732
+ const type = getUint16$1(directoryView, offsetExtraField);
5733
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5734
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5735
+ let offsetValue = offsetExtraField + 4;
5736
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5737
+ offsetValue += 8;
5738
+ }
5739
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5740
+ offsetValue += 8;
5741
+ }
5742
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5743
+ }
5744
+ offsetExtraField += 4 + size;
5745
+ }
5746
+ return UNDEFINED_VALUE;
5747
+ }
5748
+
5685
5749
  function readDigitalSignature(signatureRecordArray) {
5686
5750
  if (signatureRecordArray.length >= 6) {
5687
5751
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5771,6 +5835,7 @@
5771
5835
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5772
5836
  });
5773
5837
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5838
+ let extraFieldZip64Missing;
5774
5839
  if (extraFieldZip64) {
5775
5840
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5776
5841
  malformedExtraField = true;
@@ -5780,7 +5845,7 @@
5780
5845
  if (localDirectory) {
5781
5846
  malformedExtraField = true;
5782
5847
  } else {
5783
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5848
+ extraFieldZip64Missing = true;
5784
5849
  }
5785
5850
  }
5786
5851
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5838,6 +5903,9 @@
5838
5903
  if (extraFieldUSDZ) {
5839
5904
  directory.extraFieldUSDZ = extraFieldUSDZ;
5840
5905
  }
5906
+ if (extraFieldZip64Missing) {
5907
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5908
+ }
5841
5909
  return malformedExtraField;
5842
5910
  }
5843
5911
 
@@ -6052,13 +6120,7 @@
6052
6120
  let dataDescriptorLength = 0;
6053
6121
  if (dataDescriptor) {
6054
6122
  const zip64 = Boolean(extraFieldZip64);
6055
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
6056
- const dataDescriptorView = getDataView(dataDescriptorArray);
6057
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6058
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6059
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6060
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6061
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6123
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
6062
6124
  if (localDataDescriptor) {
6063
6125
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6064
6126
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6128,6 +6190,25 @@
6128
6190
  return mergedRanges;
6129
6191
  }
6130
6192
 
6193
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6194
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6195
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6196
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6197
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6198
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6199
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6200
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6201
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6202
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6203
+ }
6204
+
6205
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6206
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6207
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6208
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6209
+ }
6210
+ }
6211
+
6131
6212
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6132
6213
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6133
6214
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6387,6 +6468,18 @@
6387
6468
  }
6388
6469
  }
6389
6470
 
6471
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6472
+ const { dataDescriptor } = localDirectory;
6473
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6474
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6475
+ if (dataDescriptor && !maskedLocalDirectory &&
6476
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6477
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6478
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6479
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6480
+ }
6481
+ }
6482
+
6390
6483
  function reportAmbiguity(reject, warnings, reason) {
6391
6484
  if (reject) {
6392
6485
  throwAmbiguousArchive(reason);
@@ -6489,6 +6582,7 @@
6489
6582
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6490
6583
  ZipReader: ZipReader,
6491
6584
  ZipReaderStream: ZipReaderStream,
6585
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6492
6586
  isZipFile: isZipFile
6493
6587
  });
6494
6588
 
@@ -6612,136 +6706,7 @@
6612
6706
  }
6613
6707
 
6614
6708
  appendZip(reader, options = {}) {
6615
- return watchPromiseError(this, this.appendZipEntries(reader, options));
6616
- }
6617
-
6618
- async appendZipEntries(reader, options = {}) {
6619
- const zipWriter = this;
6620
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6621
- const filter = checkFunctionOption(options.filter);
6622
- while (pendingAddFileCalls.size) {
6623
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6624
- }
6625
- let resolveAppendZip;
6626
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6627
- pendingAddFileCalls.add(promiseAppendZip);
6628
- const appendedFilenames = [];
6629
- let releaseLockWriter;
6630
- try {
6631
- reader = new GenericReader(reader);
6632
- await initStream(reader);
6633
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6634
- reader = new BlobReader(await streamToBlob(reader.readable));
6635
- await initStream(reader);
6636
- }
6637
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6638
- const zipReader$1 = new ZipReader(reader);
6639
- const entries = await zipReader$1.getEntries();
6640
- const keptEntries = [];
6641
- for (const entry of entries) {
6642
- if (!filter || await filter(entry)) {
6643
- keptEntries.push(entry);
6644
- }
6645
- }
6646
- await zipReader$1.close();
6647
- await initStream(zipWriter.writer);
6648
- const { directoryOffset } = zipReader$1;
6649
- keptEntries.forEach(({ filename }) => {
6650
- if (filenames.has(filename)) {
6651
- throw new Error(ERR_DUPLICATED_NAME);
6652
- }
6653
- filenames.add(filename);
6654
- appendedFilenames.push(filename);
6655
- });
6656
- zipWriter.writerLocked = true;
6657
- const { lockWriter } = zipWriter;
6658
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6659
- zipWriter.writerLocked = false;
6660
- resolve();
6661
- });
6662
- await lockWriter;
6663
- if (zipWriter.addSplitZipSignature) {
6664
- delete zipWriter.addSplitZipSignature;
6665
- if (!await startsWithSplitZipSignature(reader)) {
6666
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6667
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6668
- }
6669
- }
6670
- const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
6671
- keptEntries.forEach(entry => {
6672
- const {
6673
- version,
6674
- rawLastModDate,
6675
- rawFilename,
6676
- bitFlag,
6677
- encrypted,
6678
- uncompressedSize,
6679
- compressedSize,
6680
- extraFieldZip64
6681
- } = entry;
6682
- let {
6683
- compressionMethod,
6684
- rawExtraField,
6685
- } = entry;
6686
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6687
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6688
- if (entry.extraFieldAES) {
6689
- compressionMethod = COMPRESSION_METHOD_AES;
6690
- }
6691
- const extraFieldLength = getLength(rawExtraField);
6692
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6693
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6694
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6695
- const {
6696
- headerArray,
6697
- headerView
6698
- } = getHeaderArrayData({
6699
- version,
6700
- bitFlag: bitFlagValue,
6701
- compressionMethod,
6702
- uncompressedSize,
6703
- compressedSize,
6704
- rawLastModDate,
6705
- rawFilename,
6706
- zip64CompressedSize,
6707
- zip64UncompressedSize,
6708
- extraFieldLength
6709
- });
6710
- const { crc32 } = entry;
6711
- if (crc32 !== UNDEFINED_VALUE) {
6712
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6713
- }
6714
- const { offset, diskNumberStart } = entryPositions.get(entry);
6715
- Object.assign(entry, {
6716
- zip64Enabled: true,
6717
- zip64UncompressedSize,
6718
- zip64CompressedSize,
6719
- offset,
6720
- diskNumberStart,
6721
- zip64DiskNumberStart: false,
6722
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6723
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6724
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6725
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6726
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6727
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6728
- rawExtraField,
6729
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6730
- headerArray,
6731
- headerView
6732
- });
6733
- fileEntries.set(entry.filename, entry);
6734
- });
6735
- } catch (error) {
6736
- appendedFilenames.forEach(filename => filenames.delete(filename));
6737
- throw error;
6738
- } finally {
6739
- resolveAppendZip();
6740
- pendingAddFileCalls.delete(promiseAppendZip);
6741
- if (releaseLockWriter) {
6742
- releaseLockWriter();
6743
- }
6744
- }
6709
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6745
6710
  }
6746
6711
 
6747
6712
  add(name = "", reader, options = {}) {
@@ -6903,11 +6868,149 @@
6903
6868
  return watchedPromise;
6904
6869
  }
6905
6870
 
6871
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6872
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6873
+ const filter = checkFunctionOption(options.filter);
6874
+ const readerOptions = checkReaderOptionsObject(options.readerOptions);
6875
+ while (pendingAddFileCalls.size) {
6876
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6877
+ }
6878
+ let resolveAppendZip;
6879
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6880
+ pendingAddFileCalls.add(promiseAppendZip);
6881
+ const appendedFilenames = [];
6882
+ let releaseLockWriter;
6883
+ try {
6884
+ reader = new GenericReader(reader);
6885
+ await initStream(reader);
6886
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6887
+ reader = new BlobReader(await streamToBlob(reader.readable));
6888
+ await initStream(reader);
6889
+ }
6890
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6891
+ const zipReader$1 = new ZipReader(reader, readerOptions);
6892
+ const keptEntries = [];
6893
+ let entries;
6894
+ try {
6895
+ entries = await zipReader$1.getEntries();
6896
+ for (const entry of entries) {
6897
+ if (!filter || await filter(entry, getExistingEntry(fileEntries, entry.filename))) {
6898
+ keptEntries.push(entry);
6899
+ }
6900
+ }
6901
+ } finally {
6902
+ await zipReader$1.close();
6903
+ }
6904
+ await initStream(zipWriter.writer);
6905
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6906
+ keptEntries.forEach(entry => {
6907
+ const { filename } = entry;
6908
+ if (entriesMissingZip64ExtraField.has(entry)) {
6909
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6910
+ }
6911
+ if (filenames.has(filename)) {
6912
+ throw new Error(ERR_DUPLICATED_NAME);
6913
+ }
6914
+ filenames.add(filename);
6915
+ appendedFilenames.push(filename);
6916
+ });
6917
+ zipWriter.writerLocked = true;
6918
+ const { lockWriter } = zipWriter;
6919
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6920
+ zipWriter.writerLocked = false;
6921
+ resolve();
6922
+ });
6923
+ await lockWriter;
6924
+ if (zipWriter.addSplitZipSignature) {
6925
+ delete zipWriter.addSplitZipSignature;
6926
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6927
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6928
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6929
+ }
6930
+ }
6931
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6932
+ keptEntries.forEach(entry => {
6933
+ const {
6934
+ version,
6935
+ rawLastModDate,
6936
+ rawFilename,
6937
+ rawBitFlag,
6938
+ uncompressedSize,
6939
+ compressedSize,
6940
+ extraFieldZip64
6941
+ } = entry;
6942
+ let {
6943
+ compressionMethod,
6944
+ rawExtraField,
6945
+ } = entry;
6946
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6947
+ if (entry.extraFieldAES) {
6948
+ compressionMethod = COMPRESSION_METHOD_AES;
6949
+ }
6950
+ const extraFieldLength = getLength(rawExtraField);
6951
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6952
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6953
+ const {
6954
+ headerArray,
6955
+ headerView
6956
+ } = getHeaderArrayData({
6957
+ version,
6958
+ bitFlag: rawBitFlag,
6959
+ compressionMethod,
6960
+ uncompressedSize,
6961
+ compressedSize,
6962
+ rawLastModDate,
6963
+ rawFilename,
6964
+ zip64CompressedSize,
6965
+ zip64UncompressedSize,
6966
+ extraFieldLength
6967
+ });
6968
+ const { crc32 } = entry;
6969
+ if (crc32 !== UNDEFINED_VALUE) {
6970
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6971
+ }
6972
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6973
+ fileEntries.set(entry.filename, Object.assign({}, entry, {
6974
+ zip64Enabled: true,
6975
+ zip64UncompressedSize,
6976
+ zip64CompressedSize,
6977
+ offset,
6978
+ diskNumberStart,
6979
+ zip64DiskNumberStart: false,
6980
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6981
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6982
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6983
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6984
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6985
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6986
+ rawExtraField,
6987
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6988
+ headerArray,
6989
+ headerView
6990
+ }));
6991
+ });
6992
+ } catch (error) {
6993
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6994
+ throw error;
6995
+ } finally {
6996
+ resolveAppendZip();
6997
+ pendingAddFileCalls.delete(promiseAppendZip);
6998
+ if (releaseLockWriter) {
6999
+ releaseLockWriter();
7000
+ }
7001
+ }
7002
+ }
7003
+
7004
+ function getExistingEntry(fileEntries, filename) {
7005
+ const fileEntry = fileEntries.get(filename);
7006
+ return fileEntry ? new Entry(fileEntry) : UNDEFINED_VALUE;
7007
+ }
7008
+
6906
7009
  async function prependZipEntries(zipWriter, reader) {
6907
7010
  if (zipWriter.filenames.size) {
6908
7011
  throw new Error(ERR_ZIP_NOT_EMPTY);
6909
7012
  }
6910
- await zipWriter.appendZipEntries(reader);
7013
+ await appendZipEntries(zipWriter, reader);
6911
7014
  }
6912
7015
 
6913
7016
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8679,7 +8782,7 @@
8679
8782
  return rawExtraField;
8680
8783
  }
8681
8784
 
8682
- async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
8785
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8683
8786
  const { writer } = zipWriter;
8684
8787
  const entryPositions = new Map();
8685
8788
  if (writer.closeDisk || compact) {
@@ -8687,6 +8790,27 @@
8687
8790
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8688
8791
  const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8689
8792
  const keptEntrySet = new Set(keptEntries);
8793
+ const localHeaderLengths = new Map();
8794
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8795
+ const entry = sortedEntries[indexEntry];
8796
+ if (keptEntrySet.has(entry)) {
8797
+ const sourceOffset = getSourceOffset(reader, entry);
8798
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8799
+ if (localHeaderLength === UNDEFINED_VALUE) {
8800
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8801
+ }
8802
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8803
+ if (entry.bitFlag.dataDescriptor) {
8804
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8805
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8806
+ }
8807
+ if (entryEnd > regionEnds[indexEntry]) {
8808
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8809
+ }
8810
+ regionEnds[indexEntry] = entryEnd;
8811
+ localHeaderLengths.set(entry, localHeaderLength);
8812
+ }
8813
+ }
8690
8814
  let copiedOffset = 0;
8691
8815
  let lastPosition;
8692
8816
  for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
@@ -8699,7 +8823,7 @@
8699
8823
  if (!compact) {
8700
8824
  await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8701
8825
  }
8702
- if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8826
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8703
8827
  await writer.closeDisk();
8704
8828
  }
8705
8829
  lastPosition = {
@@ -8733,7 +8857,7 @@
8733
8857
  const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8734
8858
  const nextEntry = sortedEntries[indexEntry + 1];
8735
8859
  if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8736
- regionEnd = getSourceOffset(reader, nextEntry);
8860
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8737
8861
  }
8738
8862
  regionEnds[indexEntry] = regionEnd;
8739
8863
  }
@@ -8747,11 +8871,13 @@
8747
8871
  try {
8748
8872
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8749
8873
  } catch (error) {
8750
- zipWriter.hasCorruptedEntries = true;
8751
- try {
8752
- error.corruptedEntry = true;
8753
- } catch {
8754
- // ignored
8874
+ if (copiedLength) {
8875
+ zipWriter.hasCorruptedEntries = true;
8876
+ try {
8877
+ error.corruptedEntry = true;
8878
+ } catch {
8879
+ // ignored
8880
+ }
8755
8881
  }
8756
8882
  throw error;
8757
8883
  } finally {
@@ -8762,11 +8888,16 @@
8762
8888
  }
8763
8889
 
8764
8890
  async function getLocalHeaderLength(reader, offset) {
8765
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8891
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8892
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8893
+ EMPTY_UINT8_ARRAY;
8766
8894
  if (getLength(headerArray) < HEADER_SIZE) {
8767
- return HEADER_SIZE;
8895
+ return UNDEFINED_VALUE;
8768
8896
  }
8769
8897
  const headerView = getDataView(headerArray);
8898
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8899
+ return UNDEFINED_VALUE;
8900
+ }
8770
8901
  return HEADER_SIZE +
8771
8902
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8772
8903
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9117,7 +9248,7 @@
9117
9248
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9118
9249
  */
9119
9250
 
9120
- const VERSION = "2.20.0";
9251
+ const VERSION = "2.22.0";
9121
9252
 
9122
9253
  /*
9123
9254
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -9608,7 +9739,6 @@
9608
9739
  const DUPLICATES_KEEP_LAST = "keep-last";
9609
9740
  const DUPLICATES_VALUES = new Set([DUPLICATES_THROW, DUPLICATES_KEEP_FIRST, DUPLICATES_KEEP_LAST]);
9610
9741
  const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
9611
- const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
9612
9742
  const ERR_UNSUPPORTED_PASS_THROUGH_VALUE = "The 'compressed' passThrough option is only supported by Entry#getData() and ZipWriter#add()";
9613
9743
  const ERR_INVALID_PASSWORDS = "Invalid passwords option (must be an array of strings)";
9614
9744
  const ERR_INVALID_REQUEST_PASSWORD = "Invalid requestPassword option (must be a function returning a string or undefined)";
@@ -10534,9 +10664,7 @@
10534
10664
  }
10535
10665
 
10536
10666
  function checkReaderOptions(readerOptions) {
10537
- if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
10538
- throw new Error(ERR_INVALID_READER_OPTIONS);
10539
- }
10667
+ checkReaderOptionsObject(readerOptions);
10540
10668
  if (readerOptions) {
10541
10669
  checkPassThroughValue(readerOptions.passThrough);
10542
10670
  checkPasswordCandidatesOptions(readerOptions);