@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",
@@ -4777,12 +4787,9 @@
4777
4787
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4778
4788
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4779
4789
  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
4790
  const ERR_ENCRYPTED = "File contains encrypted entry";
4783
4791
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4784
4792
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4785
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4786
4793
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4787
4794
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4788
4795
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4846,8 +4853,9 @@
4846
4853
  constructor(reader, options = {}) {
4847
4854
  Object.assign(this, {
4848
4855
  reader: new GenericReader(reader),
4849
- options,
4850
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4856
+ options: options || {},
4857
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4858
+ entriesMissingZip64ExtraField: new WeakSet()
4851
4859
  });
4852
4860
  }
4853
4861
 
@@ -4918,16 +4926,22 @@
4918
4926
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4919
4927
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4920
4928
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4921
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4922
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4923
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4924
- const originalDirectoryDataOffset = directoryDataOffset;
4925
- directoryDataOffset = expectedDirectoryDataOffset;
4926
- if (directoryDataOffset > originalDirectoryDataOffset) {
4927
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4929
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4930
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4931
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4932
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4933
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4934
+ }
4935
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4936
+ directoryDataOffset != expectedDirectoryDataOffset) {
4937
+ const originalDirectoryDataOffset = directoryDataOffset;
4938
+ directoryDataOffset = expectedDirectoryDataOffset;
4939
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4940
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4941
+ }
4942
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4943
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4928
4944
  }
4929
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4930
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4931
4945
  }
4932
4946
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4933
4947
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4972,10 +4986,6 @@
4972
4986
  let declaredDirectoryDataLength = directoryDataLength;
4973
4987
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4974
4988
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4975
- if (directoryDataOffset >= reader.size) {
4976
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4977
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4978
- }
4979
4989
  if (expectedLastDiskNumber != lastDiskNumber) {
4980
4990
  throw new Error(ERR_SPLIT_ZIP_FILE);
4981
4991
  }
@@ -4983,52 +4993,64 @@
4983
4993
  throw new Error(ERR_BAD_FORMAT);
4984
4994
  }
4985
4995
  let offset = 0;
4986
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4996
+ let directoryArray = directoryDataOffset < reader.size ?
4997
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
4998
+ EMPTY_UINT8_ARRAY;
4987
4999
  let directoryView = getDataView(directoryArray);
4988
5000
  if (directoryDataLength) {
4989
- if (directoryArray.length < 4) {
4990
- throw new Error(ERR_BAD_FORMAT);
4991
- }
4992
5001
  let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4993
5002
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4994
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
5003
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
5004
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4995
5005
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4996
- detectEncryptedCentralDirectory(directoryView);
5006
+ detectEncryptedCentralDirectory(directoryView));
5007
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
5008
+ throw new Error(ERR_BAD_FORMAT);
5009
+ }
4997
5010
  let reconcile = !storedPointsAtDirectory;
4998
5011
  if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4999
- const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5000
- if (storedPointsAtDirectory) {
5001
- reconcile = expectedPointsAtDirectory;
5002
- } else if (!expectedPointsAtDirectory) {
5012
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5013
+ if (!expectedPointsAtDirectory) {
5003
5014
  const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
5004
5015
  const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
5005
5016
  if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5006
5017
  await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5007
5018
  expectedDirectoryDataOffset = signedDirectoryDataOffset;
5019
+ expectedPointsAtDirectory = true;
5008
5020
  }
5009
5021
  }
5022
+ if (storedPointsAtDirectory) {
5023
+ reconcile = expectedPointsAtDirectory;
5024
+ }
5010
5025
  }
5011
- if (reconcile) {
5026
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
5012
5027
  const originalDirectoryDataOffset = directoryDataOffset;
5013
5028
  directoryDataOffset = expectedDirectoryDataOffset;
5014
5029
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5015
5030
  directoryView = getDataView(directoryArray);
5016
- if (directoryDataOffset > originalDirectoryDataOffset) {
5017
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5031
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5032
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5033
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5034
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5035
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5036
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5037
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5038
+ prependedDataLength += offsetDelta;
5018
5039
  prependedCentralDirectory = storedPointsAtDirectory;
5019
5040
  } else {
5020
5041
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5021
- const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5022
- if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
5023
- const localHeaderOffset = getUint32$1(directoryView, 42);
5024
- if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
5025
- await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
5026
- prependedDataLength += offsetDelta;
5027
- }
5042
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5043
+ prependedDataLength += offsetDelta;
5028
5044
  }
5029
5045
  }
5030
5046
  }
5031
5047
  }
5048
+ if (directoryArray.length < 4) {
5049
+ throw new Error(ERR_BAD_FORMAT);
5050
+ }
5051
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5052
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5053
+ directoryDataOffset = centralDirectoryEndOffset;
5032
5054
  }
5033
5055
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5034
5056
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5070,8 +5092,10 @@
5070
5092
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5071
5093
  }
5072
5094
  }
5095
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5096
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5073
5097
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5074
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5098
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5075
5099
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5076
5100
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5077
5101
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5162,13 +5186,17 @@
5162
5186
  if (malformedExtraField) {
5163
5187
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5164
5188
  }
5165
- fileEntry.offset += prependedDataLength;
5166
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5167
- startOffset = Math.min(entryPosition, startOffset);
5168
- if (entryPosition < previousEntryPosition) {
5169
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5189
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5190
+ startOffset = 0;
5191
+ } else {
5192
+ fileEntry.offset += prependedDataLength;
5193
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5194
+ startOffset = Math.min(entryPosition, startOffset);
5195
+ if (entryPosition < previousEntryPosition) {
5196
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5197
+ }
5198
+ previousEntryPosition = entryPosition;
5170
5199
  }
5171
- previousEntryPosition = entryPosition;
5172
5200
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5173
5201
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5174
5202
  }
@@ -5205,12 +5233,15 @@
5205
5233
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5206
5234
  });
5207
5235
  const entry = new Entry(fileEntry);
5208
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5236
+ if (fileEntry.zip64ExtraFieldMissing) {
5237
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5238
+ }
5239
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5209
5240
  entry.arrayBuffer = async options => {
5210
5241
  const writer = new TransformStream();
5211
5242
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5212
5243
  arrayBufferPromise.catch(() => { });
5213
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5244
+ await fileEntry.getData(writer, entry, zipReader,
5214
5245
  Object.assign({}, options, { preventClose: false }));
5215
5246
  return arrayBufferPromise;
5216
5247
  };
@@ -5418,8 +5449,9 @@
5418
5449
  });
5419
5450
  }
5420
5451
 
5421
- async getData(writer, fileEntry, readRanges, options = {}) {
5452
+ async getData(writer, fileEntry, zipReader, options = {}) {
5422
5453
  const zipEntry = this;
5454
+ const { readRanges, directoryOffset } = zipReader;
5423
5455
  if (zipEntry.zip64ExtraFieldMissing) {
5424
5456
  throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5425
5457
  }
@@ -5493,10 +5525,10 @@
5493
5525
  }
5494
5526
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5495
5527
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5496
- if (lastAccessDate) {
5528
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5497
5529
  fileEntry.lastAccessDate = lastAccessDate;
5498
5530
  }
5499
- if (creationDate) {
5531
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5500
5532
  fileEntry.creationDate = creationDate;
5501
5533
  }
5502
5534
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5525,7 +5557,7 @@
5525
5557
  throw new Error(ERR_ENCRYPTED);
5526
5558
  }
5527
5559
  }
5528
- if (dataOffset + compressedSize > reader.size) {
5560
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5529
5561
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5530
5562
  }
5531
5563
  const size = compressedSize;
@@ -5591,6 +5623,7 @@
5591
5623
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5592
5624
  readRanges
5593
5625
  });
5626
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5594
5627
  }
5595
5628
  let writable, abortError, aborted;
5596
5629
  try {
@@ -5610,10 +5643,7 @@
5610
5643
  writer.size += writtenSize;
5611
5644
  }
5612
5645
  } catch (error) {
5613
- const { outputSize: failedOutputSize } = workerOptions;
5614
- if (failedOutputSize !== UNDEFINED_VALUE) {
5615
- writer.size += failedOutputSize;
5616
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5646
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5617
5647
  writer.size += error.outputSize;
5618
5648
  }
5619
5649
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5641,13 +5671,7 @@
5641
5671
  };
5642
5672
 
5643
5673
  function detectEncryptedCentralDirectory(directoryView) {
5644
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5645
- for (let offset = 0; offset < maxOffset; offset++) {
5646
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5647
- return true;
5648
- }
5649
- }
5650
- return false;
5674
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5651
5675
  }
5652
5676
 
5653
5677
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5684,6 +5708,46 @@
5684
5708
  }
5685
5709
  }
5686
5710
 
5711
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5712
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5713
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5714
+ const tailView = getDataView(tailArray);
5715
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5716
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5717
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5718
+ return startOffset + indexByte;
5719
+ }
5720
+ }
5721
+ }
5722
+
5723
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5724
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5725
+ return UNDEFINED_VALUE;
5726
+ }
5727
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5728
+ if (localHeaderOffset != MAX_32_BITS) {
5729
+ return localHeaderOffset;
5730
+ }
5731
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5732
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5733
+ while (offsetExtraField + 4 <= endExtraField) {
5734
+ const type = getUint16$1(directoryView, offsetExtraField);
5735
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5736
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5737
+ let offsetValue = offsetExtraField + 4;
5738
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5739
+ offsetValue += 8;
5740
+ }
5741
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5742
+ offsetValue += 8;
5743
+ }
5744
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5745
+ }
5746
+ offsetExtraField += 4 + size;
5747
+ }
5748
+ return UNDEFINED_VALUE;
5749
+ }
5750
+
5687
5751
  function readDigitalSignature(signatureRecordArray) {
5688
5752
  if (signatureRecordArray.length >= 6) {
5689
5753
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5773,6 +5837,7 @@
5773
5837
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5774
5838
  });
5775
5839
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5840
+ let extraFieldZip64Missing;
5776
5841
  if (extraFieldZip64) {
5777
5842
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5778
5843
  malformedExtraField = true;
@@ -5782,7 +5847,7 @@
5782
5847
  if (localDirectory) {
5783
5848
  malformedExtraField = true;
5784
5849
  } else {
5785
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5850
+ extraFieldZip64Missing = true;
5786
5851
  }
5787
5852
  }
5788
5853
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5840,6 +5905,9 @@
5840
5905
  if (extraFieldUSDZ) {
5841
5906
  directory.extraFieldUSDZ = extraFieldUSDZ;
5842
5907
  }
5908
+ if (extraFieldZip64Missing) {
5909
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5910
+ }
5843
5911
  return malformedExtraField;
5844
5912
  }
5845
5913
 
@@ -6054,13 +6122,7 @@
6054
6122
  let dataDescriptorLength = 0;
6055
6123
  if (dataDescriptor) {
6056
6124
  const zip64 = Boolean(extraFieldZip64);
6057
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
6058
- const dataDescriptorView = getDataView(dataDescriptorArray);
6059
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6060
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6061
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6062
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6063
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6125
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
6064
6126
  if (localDataDescriptor) {
6065
6127
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6066
6128
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6130,6 +6192,25 @@
6130
6192
  return mergedRanges;
6131
6193
  }
6132
6194
 
6195
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6196
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6197
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6198
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6199
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6200
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6201
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6202
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6203
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6204
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6205
+ }
6206
+
6207
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6208
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6209
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6210
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6211
+ }
6212
+ }
6213
+
6133
6214
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6134
6215
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6135
6216
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6389,6 +6470,18 @@
6389
6470
  }
6390
6471
  }
6391
6472
 
6473
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6474
+ const { dataDescriptor } = localDirectory;
6475
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6476
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6477
+ if (dataDescriptor && !maskedLocalDirectory &&
6478
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6479
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6480
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6481
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6482
+ }
6483
+ }
6484
+
6392
6485
  function reportAmbiguity(reject, warnings, reason) {
6393
6486
  if (reject) {
6394
6487
  throwAmbiguousArchive(reason);
@@ -6491,6 +6584,7 @@
6491
6584
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6492
6585
  ZipReader: ZipReader,
6493
6586
  ZipReaderStream: ZipReaderStream,
6587
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6494
6588
  isZipFile: isZipFile
6495
6589
  });
6496
6590
 
@@ -6614,136 +6708,7 @@
6614
6708
  }
6615
6709
 
6616
6710
  appendZip(reader, options = {}) {
6617
- return watchPromiseError(this, this.appendZipEntries(reader, options));
6618
- }
6619
-
6620
- async appendZipEntries(reader, options = {}) {
6621
- const zipWriter = this;
6622
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6623
- const filter = checkFunctionOption(options.filter);
6624
- while (pendingAddFileCalls.size) {
6625
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6626
- }
6627
- let resolveAppendZip;
6628
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6629
- pendingAddFileCalls.add(promiseAppendZip);
6630
- const appendedFilenames = [];
6631
- let releaseLockWriter;
6632
- try {
6633
- reader = new GenericReader(reader);
6634
- await initStream(reader);
6635
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6636
- reader = new BlobReader(await streamToBlob(reader.readable));
6637
- await initStream(reader);
6638
- }
6639
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6640
- const zipReader$1 = new ZipReader(reader);
6641
- const entries = await zipReader$1.getEntries();
6642
- const keptEntries = [];
6643
- for (const entry of entries) {
6644
- if (!filter || await filter(entry)) {
6645
- keptEntries.push(entry);
6646
- }
6647
- }
6648
- await zipReader$1.close();
6649
- await initStream(zipWriter.writer);
6650
- const { directoryOffset } = zipReader$1;
6651
- keptEntries.forEach(({ filename }) => {
6652
- if (filenames.has(filename)) {
6653
- throw new Error(ERR_DUPLICATED_NAME);
6654
- }
6655
- filenames.add(filename);
6656
- appendedFilenames.push(filename);
6657
- });
6658
- zipWriter.writerLocked = true;
6659
- const { lockWriter } = zipWriter;
6660
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6661
- zipWriter.writerLocked = false;
6662
- resolve();
6663
- });
6664
- await lockWriter;
6665
- if (zipWriter.addSplitZipSignature) {
6666
- delete zipWriter.addSplitZipSignature;
6667
- if (!await startsWithSplitZipSignature(reader)) {
6668
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6669
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6670
- }
6671
- }
6672
- const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
6673
- keptEntries.forEach(entry => {
6674
- const {
6675
- version,
6676
- rawLastModDate,
6677
- rawFilename,
6678
- bitFlag,
6679
- encrypted,
6680
- uncompressedSize,
6681
- compressedSize,
6682
- extraFieldZip64
6683
- } = entry;
6684
- let {
6685
- compressionMethod,
6686
- rawExtraField,
6687
- } = entry;
6688
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6689
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6690
- if (entry.extraFieldAES) {
6691
- compressionMethod = COMPRESSION_METHOD_AES;
6692
- }
6693
- const extraFieldLength = getLength(rawExtraField);
6694
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6695
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6696
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6697
- const {
6698
- headerArray,
6699
- headerView
6700
- } = getHeaderArrayData({
6701
- version,
6702
- bitFlag: bitFlagValue,
6703
- compressionMethod,
6704
- uncompressedSize,
6705
- compressedSize,
6706
- rawLastModDate,
6707
- rawFilename,
6708
- zip64CompressedSize,
6709
- zip64UncompressedSize,
6710
- extraFieldLength
6711
- });
6712
- const { crc32 } = entry;
6713
- if (crc32 !== UNDEFINED_VALUE) {
6714
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6715
- }
6716
- const { offset, diskNumberStart } = entryPositions.get(entry);
6717
- Object.assign(entry, {
6718
- zip64Enabled: true,
6719
- zip64UncompressedSize,
6720
- zip64CompressedSize,
6721
- offset,
6722
- diskNumberStart,
6723
- zip64DiskNumberStart: false,
6724
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6725
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6726
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6727
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6728
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6729
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6730
- rawExtraField,
6731
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6732
- headerArray,
6733
- headerView
6734
- });
6735
- fileEntries.set(entry.filename, entry);
6736
- });
6737
- } catch (error) {
6738
- appendedFilenames.forEach(filename => filenames.delete(filename));
6739
- throw error;
6740
- } finally {
6741
- resolveAppendZip();
6742
- pendingAddFileCalls.delete(promiseAppendZip);
6743
- if (releaseLockWriter) {
6744
- releaseLockWriter();
6745
- }
6746
- }
6711
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6747
6712
  }
6748
6713
 
6749
6714
  add(name = "", reader, options = {}) {
@@ -6905,11 +6870,149 @@
6905
6870
  return watchedPromise;
6906
6871
  }
6907
6872
 
6873
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6874
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6875
+ const filter = checkFunctionOption(options.filter);
6876
+ const readerOptions = checkReaderOptionsObject(options.readerOptions);
6877
+ while (pendingAddFileCalls.size) {
6878
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6879
+ }
6880
+ let resolveAppendZip;
6881
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6882
+ pendingAddFileCalls.add(promiseAppendZip);
6883
+ const appendedFilenames = [];
6884
+ let releaseLockWriter;
6885
+ try {
6886
+ reader = new GenericReader(reader);
6887
+ await initStream(reader);
6888
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6889
+ reader = new BlobReader(await streamToBlob(reader.readable));
6890
+ await initStream(reader);
6891
+ }
6892
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6893
+ const zipReader$1 = new ZipReader(reader, readerOptions);
6894
+ const keptEntries = [];
6895
+ let entries;
6896
+ try {
6897
+ entries = await zipReader$1.getEntries();
6898
+ for (const entry of entries) {
6899
+ if (!filter || await filter(entry, getExistingEntry(fileEntries, entry.filename))) {
6900
+ keptEntries.push(entry);
6901
+ }
6902
+ }
6903
+ } finally {
6904
+ await zipReader$1.close();
6905
+ }
6906
+ await initStream(zipWriter.writer);
6907
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6908
+ keptEntries.forEach(entry => {
6909
+ const { filename } = entry;
6910
+ if (entriesMissingZip64ExtraField.has(entry)) {
6911
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6912
+ }
6913
+ if (filenames.has(filename)) {
6914
+ throw new Error(ERR_DUPLICATED_NAME);
6915
+ }
6916
+ filenames.add(filename);
6917
+ appendedFilenames.push(filename);
6918
+ });
6919
+ zipWriter.writerLocked = true;
6920
+ const { lockWriter } = zipWriter;
6921
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6922
+ zipWriter.writerLocked = false;
6923
+ resolve();
6924
+ });
6925
+ await lockWriter;
6926
+ if (zipWriter.addSplitZipSignature) {
6927
+ delete zipWriter.addSplitZipSignature;
6928
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6929
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6930
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6931
+ }
6932
+ }
6933
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6934
+ keptEntries.forEach(entry => {
6935
+ const {
6936
+ version,
6937
+ rawLastModDate,
6938
+ rawFilename,
6939
+ rawBitFlag,
6940
+ uncompressedSize,
6941
+ compressedSize,
6942
+ extraFieldZip64
6943
+ } = entry;
6944
+ let {
6945
+ compressionMethod,
6946
+ rawExtraField,
6947
+ } = entry;
6948
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6949
+ if (entry.extraFieldAES) {
6950
+ compressionMethod = COMPRESSION_METHOD_AES;
6951
+ }
6952
+ const extraFieldLength = getLength(rawExtraField);
6953
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6954
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6955
+ const {
6956
+ headerArray,
6957
+ headerView
6958
+ } = getHeaderArrayData({
6959
+ version,
6960
+ bitFlag: rawBitFlag,
6961
+ compressionMethod,
6962
+ uncompressedSize,
6963
+ compressedSize,
6964
+ rawLastModDate,
6965
+ rawFilename,
6966
+ zip64CompressedSize,
6967
+ zip64UncompressedSize,
6968
+ extraFieldLength
6969
+ });
6970
+ const { crc32 } = entry;
6971
+ if (crc32 !== UNDEFINED_VALUE) {
6972
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6973
+ }
6974
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6975
+ fileEntries.set(entry.filename, Object.assign({}, entry, {
6976
+ zip64Enabled: true,
6977
+ zip64UncompressedSize,
6978
+ zip64CompressedSize,
6979
+ offset,
6980
+ diskNumberStart,
6981
+ zip64DiskNumberStart: false,
6982
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6983
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6984
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6985
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6986
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6987
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6988
+ rawExtraField,
6989
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6990
+ headerArray,
6991
+ headerView
6992
+ }));
6993
+ });
6994
+ } catch (error) {
6995
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6996
+ throw error;
6997
+ } finally {
6998
+ resolveAppendZip();
6999
+ pendingAddFileCalls.delete(promiseAppendZip);
7000
+ if (releaseLockWriter) {
7001
+ releaseLockWriter();
7002
+ }
7003
+ }
7004
+ }
7005
+
7006
+ function getExistingEntry(fileEntries, filename) {
7007
+ const fileEntry = fileEntries.get(filename);
7008
+ return fileEntry ? new Entry(fileEntry) : UNDEFINED_VALUE;
7009
+ }
7010
+
6908
7011
  async function prependZipEntries(zipWriter, reader) {
6909
7012
  if (zipWriter.filenames.size) {
6910
7013
  throw new Error(ERR_ZIP_NOT_EMPTY);
6911
7014
  }
6912
- await zipWriter.appendZipEntries(reader);
7015
+ await appendZipEntries(zipWriter, reader);
6913
7016
  }
6914
7017
 
6915
7018
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8681,7 +8784,7 @@
8681
8784
  return rawExtraField;
8682
8785
  }
8683
8786
 
8684
- async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
8787
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8685
8788
  const { writer } = zipWriter;
8686
8789
  const entryPositions = new Map();
8687
8790
  if (writer.closeDisk || compact) {
@@ -8689,6 +8792,27 @@
8689
8792
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8690
8793
  const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8691
8794
  const keptEntrySet = new Set(keptEntries);
8795
+ const localHeaderLengths = new Map();
8796
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8797
+ const entry = sortedEntries[indexEntry];
8798
+ if (keptEntrySet.has(entry)) {
8799
+ const sourceOffset = getSourceOffset(reader, entry);
8800
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8801
+ if (localHeaderLength === UNDEFINED_VALUE) {
8802
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8803
+ }
8804
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8805
+ if (entry.bitFlag.dataDescriptor) {
8806
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8807
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8808
+ }
8809
+ if (entryEnd > regionEnds[indexEntry]) {
8810
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8811
+ }
8812
+ regionEnds[indexEntry] = entryEnd;
8813
+ localHeaderLengths.set(entry, localHeaderLength);
8814
+ }
8815
+ }
8692
8816
  let copiedOffset = 0;
8693
8817
  let lastPosition;
8694
8818
  for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
@@ -8701,7 +8825,7 @@
8701
8825
  if (!compact) {
8702
8826
  await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8703
8827
  }
8704
- if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8828
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8705
8829
  await writer.closeDisk();
8706
8830
  }
8707
8831
  lastPosition = {
@@ -8735,7 +8859,7 @@
8735
8859
  const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8736
8860
  const nextEntry = sortedEntries[indexEntry + 1];
8737
8861
  if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8738
- regionEnd = getSourceOffset(reader, nextEntry);
8862
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8739
8863
  }
8740
8864
  regionEnds[indexEntry] = regionEnd;
8741
8865
  }
@@ -8749,11 +8873,13 @@
8749
8873
  try {
8750
8874
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8751
8875
  } catch (error) {
8752
- zipWriter.hasCorruptedEntries = true;
8753
- try {
8754
- error.corruptedEntry = true;
8755
- } catch {
8756
- // ignored
8876
+ if (copiedLength) {
8877
+ zipWriter.hasCorruptedEntries = true;
8878
+ try {
8879
+ error.corruptedEntry = true;
8880
+ } catch {
8881
+ // ignored
8882
+ }
8757
8883
  }
8758
8884
  throw error;
8759
8885
  } finally {
@@ -8764,11 +8890,16 @@
8764
8890
  }
8765
8891
 
8766
8892
  async function getLocalHeaderLength(reader, offset) {
8767
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8893
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8894
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8895
+ EMPTY_UINT8_ARRAY;
8768
8896
  if (getLength(headerArray) < HEADER_SIZE) {
8769
- return HEADER_SIZE;
8897
+ return UNDEFINED_VALUE;
8770
8898
  }
8771
8899
  const headerView = getDataView(headerArray);
8900
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8901
+ return UNDEFINED_VALUE;
8902
+ }
8772
8903
  return HEADER_SIZE +
8773
8904
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8774
8905
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9119,7 +9250,7 @@
9119
9250
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9120
9251
  */
9121
9252
 
9122
- const VERSION = "2.20.0";
9253
+ const VERSION = "2.22.0";
9123
9254
 
9124
9255
  /*
9125
9256
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -9648,7 +9779,6 @@
9648
9779
  const DUPLICATES_KEEP_LAST = "keep-last";
9649
9780
  const DUPLICATES_VALUES = new Set([DUPLICATES_THROW, DUPLICATES_KEEP_FIRST, DUPLICATES_KEEP_LAST]);
9650
9781
  const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
9651
- const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
9652
9782
  const ERR_UNSUPPORTED_PASS_THROUGH_VALUE = "The 'compressed' passThrough option is only supported by Entry#getData() and ZipWriter#add()";
9653
9783
  const ERR_INVALID_PASSWORDS = "Invalid passwords option (must be an array of strings)";
9654
9784
  const ERR_INVALID_REQUEST_PASSWORD = "Invalid requestPassword option (must be a function returning a string or undefined)";
@@ -10574,9 +10704,7 @@
10574
10704
  }
10575
10705
 
10576
10706
  function checkReaderOptions(readerOptions) {
10577
- if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
10578
- throw new Error(ERR_INVALID_READER_OPTIONS);
10579
- }
10707
+ checkReaderOptionsObject(readerOptions);
10580
10708
  if (readerOptions) {
10581
10709
  checkPassThroughValue(readerOptions.passThrough);
10582
10710
  checkPasswordCandidatesOptions(readerOptions);