@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.
package/dist/zip-fs.js CHANGED
@@ -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",
@@ -4785,12 +4795,9 @@
4785
4795
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4786
4796
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4787
4797
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4788
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4789
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4790
4798
  const ERR_ENCRYPTED = "File contains encrypted entry";
4791
4799
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4792
4800
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4793
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4794
4801
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4795
4802
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4796
4803
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4854,8 +4861,9 @@
4854
4861
  constructor(reader, options = {}) {
4855
4862
  Object.assign(this, {
4856
4863
  reader: new GenericReader(reader),
4857
- options,
4858
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4864
+ options: options || {},
4865
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4866
+ entriesMissingZip64ExtraField: new WeakSet()
4859
4867
  });
4860
4868
  }
4861
4869
 
@@ -4926,16 +4934,22 @@
4926
4934
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4927
4935
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4928
4936
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4929
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4930
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4931
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4932
- const originalDirectoryDataOffset = directoryDataOffset;
4933
- directoryDataOffset = expectedDirectoryDataOffset;
4934
- if (directoryDataOffset > originalDirectoryDataOffset) {
4935
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4937
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4938
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4939
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4940
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4941
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4942
+ }
4943
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4944
+ directoryDataOffset != expectedDirectoryDataOffset) {
4945
+ const originalDirectoryDataOffset = directoryDataOffset;
4946
+ directoryDataOffset = expectedDirectoryDataOffset;
4947
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4948
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4949
+ }
4950
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4951
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4936
4952
  }
4937
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4938
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4939
4953
  }
4940
4954
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4941
4955
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4980,10 +4994,6 @@
4980
4994
  let declaredDirectoryDataLength = directoryDataLength;
4981
4995
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4982
4996
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4983
- if (directoryDataOffset >= reader.size) {
4984
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4985
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4986
- }
4987
4997
  if (expectedLastDiskNumber != lastDiskNumber) {
4988
4998
  throw new Error(ERR_SPLIT_ZIP_FILE);
4989
4999
  }
@@ -4991,52 +5001,64 @@
4991
5001
  throw new Error(ERR_BAD_FORMAT);
4992
5002
  }
4993
5003
  let offset = 0;
4994
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5004
+ let directoryArray = directoryDataOffset < reader.size ?
5005
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
5006
+ EMPTY_UINT8_ARRAY;
4995
5007
  let directoryView = getDataView(directoryArray);
4996
5008
  if (directoryDataLength) {
4997
- if (directoryArray.length < 4) {
4998
- throw new Error(ERR_BAD_FORMAT);
4999
- }
5000
5009
  let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
5001
5010
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
5002
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
5011
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
5012
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
5003
5013
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
5004
- detectEncryptedCentralDirectory(directoryView);
5014
+ detectEncryptedCentralDirectory(directoryView));
5015
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
5016
+ throw new Error(ERR_BAD_FORMAT);
5017
+ }
5005
5018
  let reconcile = !storedPointsAtDirectory;
5006
5019
  if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
5007
- const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5008
- if (storedPointsAtDirectory) {
5009
- reconcile = expectedPointsAtDirectory;
5010
- } else if (!expectedPointsAtDirectory) {
5020
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5021
+ if (!expectedPointsAtDirectory) {
5011
5022
  const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
5012
5023
  const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
5013
5024
  if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5014
5025
  await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5015
5026
  expectedDirectoryDataOffset = signedDirectoryDataOffset;
5027
+ expectedPointsAtDirectory = true;
5016
5028
  }
5017
5029
  }
5030
+ if (storedPointsAtDirectory) {
5031
+ reconcile = expectedPointsAtDirectory;
5032
+ }
5018
5033
  }
5019
- if (reconcile) {
5034
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
5020
5035
  const originalDirectoryDataOffset = directoryDataOffset;
5021
5036
  directoryDataOffset = expectedDirectoryDataOffset;
5022
5037
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5023
5038
  directoryView = getDataView(directoryArray);
5024
- if (directoryDataOffset > originalDirectoryDataOffset) {
5025
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5039
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5040
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5041
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5042
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5043
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5044
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5045
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5046
+ prependedDataLength += offsetDelta;
5026
5047
  prependedCentralDirectory = storedPointsAtDirectory;
5027
5048
  } else {
5028
5049
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5029
- const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5030
- if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
5031
- const localHeaderOffset = getUint32$1(directoryView, 42);
5032
- if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
5033
- await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
5034
- prependedDataLength += offsetDelta;
5035
- }
5050
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5051
+ prependedDataLength += offsetDelta;
5036
5052
  }
5037
5053
  }
5038
5054
  }
5039
5055
  }
5056
+ if (directoryArray.length < 4) {
5057
+ throw new Error(ERR_BAD_FORMAT);
5058
+ }
5059
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5060
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5061
+ directoryDataOffset = centralDirectoryEndOffset;
5040
5062
  }
5041
5063
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5042
5064
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5078,8 +5100,10 @@
5078
5100
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5079
5101
  }
5080
5102
  }
5103
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5104
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5081
5105
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5082
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5106
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5083
5107
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5084
5108
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5085
5109
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5170,13 +5194,17 @@
5170
5194
  if (malformedExtraField) {
5171
5195
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5172
5196
  }
5173
- fileEntry.offset += prependedDataLength;
5174
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5175
- startOffset = Math.min(entryPosition, startOffset);
5176
- if (entryPosition < previousEntryPosition) {
5177
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5197
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5198
+ startOffset = 0;
5199
+ } else {
5200
+ fileEntry.offset += prependedDataLength;
5201
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5202
+ startOffset = Math.min(entryPosition, startOffset);
5203
+ if (entryPosition < previousEntryPosition) {
5204
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5205
+ }
5206
+ previousEntryPosition = entryPosition;
5178
5207
  }
5179
- previousEntryPosition = entryPosition;
5180
5208
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5181
5209
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5182
5210
  }
@@ -5213,12 +5241,15 @@
5213
5241
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5214
5242
  });
5215
5243
  const entry = new Entry(fileEntry);
5216
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5244
+ if (fileEntry.zip64ExtraFieldMissing) {
5245
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5246
+ }
5247
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5217
5248
  entry.arrayBuffer = async options => {
5218
5249
  const writer = new TransformStream();
5219
5250
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5220
5251
  arrayBufferPromise.catch(() => { });
5221
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5252
+ await fileEntry.getData(writer, entry, zipReader,
5222
5253
  Object.assign({}, options, { preventClose: false }));
5223
5254
  return arrayBufferPromise;
5224
5255
  };
@@ -5426,8 +5457,9 @@
5426
5457
  });
5427
5458
  }
5428
5459
 
5429
- async getData(writer, fileEntry, readRanges, options = {}) {
5460
+ async getData(writer, fileEntry, zipReader, options = {}) {
5430
5461
  const zipEntry = this;
5462
+ const { readRanges, directoryOffset } = zipReader;
5431
5463
  if (zipEntry.zip64ExtraFieldMissing) {
5432
5464
  throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5433
5465
  }
@@ -5501,10 +5533,10 @@
5501
5533
  }
5502
5534
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5503
5535
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5504
- if (lastAccessDate) {
5536
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5505
5537
  fileEntry.lastAccessDate = lastAccessDate;
5506
5538
  }
5507
- if (creationDate) {
5539
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5508
5540
  fileEntry.creationDate = creationDate;
5509
5541
  }
5510
5542
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5533,7 +5565,7 @@
5533
5565
  throw new Error(ERR_ENCRYPTED);
5534
5566
  }
5535
5567
  }
5536
- if (dataOffset + compressedSize > reader.size) {
5568
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5537
5569
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5538
5570
  }
5539
5571
  const size = compressedSize;
@@ -5599,6 +5631,7 @@
5599
5631
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5600
5632
  readRanges
5601
5633
  });
5634
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5602
5635
  }
5603
5636
  let writable, abortError, aborted;
5604
5637
  try {
@@ -5618,10 +5651,7 @@
5618
5651
  writer.size += writtenSize;
5619
5652
  }
5620
5653
  } catch (error) {
5621
- const { outputSize: failedOutputSize } = workerOptions;
5622
- if (failedOutputSize !== UNDEFINED_VALUE) {
5623
- writer.size += failedOutputSize;
5624
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5654
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5625
5655
  writer.size += error.outputSize;
5626
5656
  }
5627
5657
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5649,13 +5679,7 @@
5649
5679
  };
5650
5680
 
5651
5681
  function detectEncryptedCentralDirectory(directoryView) {
5652
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5653
- for (let offset = 0; offset < maxOffset; offset++) {
5654
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5655
- return true;
5656
- }
5657
- }
5658
- return false;
5682
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5659
5683
  }
5660
5684
 
5661
5685
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5692,6 +5716,46 @@
5692
5716
  }
5693
5717
  }
5694
5718
 
5719
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5720
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5721
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5722
+ const tailView = getDataView(tailArray);
5723
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5724
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5725
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5726
+ return startOffset + indexByte;
5727
+ }
5728
+ }
5729
+ }
5730
+
5731
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5732
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5733
+ return UNDEFINED_VALUE;
5734
+ }
5735
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5736
+ if (localHeaderOffset != MAX_32_BITS) {
5737
+ return localHeaderOffset;
5738
+ }
5739
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5740
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5741
+ while (offsetExtraField + 4 <= endExtraField) {
5742
+ const type = getUint16$1(directoryView, offsetExtraField);
5743
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5744
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5745
+ let offsetValue = offsetExtraField + 4;
5746
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5747
+ offsetValue += 8;
5748
+ }
5749
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5750
+ offsetValue += 8;
5751
+ }
5752
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5753
+ }
5754
+ offsetExtraField += 4 + size;
5755
+ }
5756
+ return UNDEFINED_VALUE;
5757
+ }
5758
+
5695
5759
  function readDigitalSignature(signatureRecordArray) {
5696
5760
  if (signatureRecordArray.length >= 6) {
5697
5761
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5781,6 +5845,7 @@
5781
5845
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5782
5846
  });
5783
5847
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5848
+ let extraFieldZip64Missing;
5784
5849
  if (extraFieldZip64) {
5785
5850
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5786
5851
  malformedExtraField = true;
@@ -5790,7 +5855,7 @@
5790
5855
  if (localDirectory) {
5791
5856
  malformedExtraField = true;
5792
5857
  } else {
5793
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5858
+ extraFieldZip64Missing = true;
5794
5859
  }
5795
5860
  }
5796
5861
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5848,6 +5913,9 @@
5848
5913
  if (extraFieldUSDZ) {
5849
5914
  directory.extraFieldUSDZ = extraFieldUSDZ;
5850
5915
  }
5916
+ if (extraFieldZip64Missing) {
5917
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5918
+ }
5851
5919
  return malformedExtraField;
5852
5920
  }
5853
5921
 
@@ -6062,13 +6130,7 @@
6062
6130
  let dataDescriptorLength = 0;
6063
6131
  if (dataDescriptor) {
6064
6132
  const zip64 = Boolean(extraFieldZip64);
6065
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
6066
- const dataDescriptorView = getDataView(dataDescriptorArray);
6067
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6068
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6069
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6070
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6071
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6133
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
6072
6134
  if (localDataDescriptor) {
6073
6135
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6074
6136
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6138,6 +6200,25 @@
6138
6200
  return mergedRanges;
6139
6201
  }
6140
6202
 
6203
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6204
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6205
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6206
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6207
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6208
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6209
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6210
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6211
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6212
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6213
+ }
6214
+
6215
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6216
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6217
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6218
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6219
+ }
6220
+ }
6221
+
6141
6222
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6142
6223
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6143
6224
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6397,6 +6478,18 @@
6397
6478
  }
6398
6479
  }
6399
6480
 
6481
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6482
+ const { dataDescriptor } = localDirectory;
6483
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6484
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6485
+ if (dataDescriptor && !maskedLocalDirectory &&
6486
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6487
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6488
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6489
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6490
+ }
6491
+ }
6492
+
6400
6493
  function reportAmbiguity(reject, warnings, reason) {
6401
6494
  if (reject) {
6402
6495
  throwAmbiguousArchive(reason);
@@ -6499,6 +6592,7 @@
6499
6592
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6500
6593
  ZipReader: ZipReader,
6501
6594
  ZipReaderStream: ZipReaderStream,
6595
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6502
6596
  isZipFile: isZipFile
6503
6597
  });
6504
6598
 
@@ -6622,136 +6716,7 @@
6622
6716
  }
6623
6717
 
6624
6718
  appendZip(reader, options = {}) {
6625
- return watchPromiseError(this, this.appendZipEntries(reader, options));
6626
- }
6627
-
6628
- async appendZipEntries(reader, options = {}) {
6629
- const zipWriter = this;
6630
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6631
- const filter = checkFunctionOption(options.filter);
6632
- while (pendingAddFileCalls.size) {
6633
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6634
- }
6635
- let resolveAppendZip;
6636
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6637
- pendingAddFileCalls.add(promiseAppendZip);
6638
- const appendedFilenames = [];
6639
- let releaseLockWriter;
6640
- try {
6641
- reader = new GenericReader(reader);
6642
- await initStream(reader);
6643
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6644
- reader = new BlobReader(await streamToBlob(reader.readable));
6645
- await initStream(reader);
6646
- }
6647
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6648
- const zipReader$1 = new ZipReader(reader);
6649
- const entries = await zipReader$1.getEntries();
6650
- const keptEntries = [];
6651
- for (const entry of entries) {
6652
- if (!filter || await filter(entry)) {
6653
- keptEntries.push(entry);
6654
- }
6655
- }
6656
- await zipReader$1.close();
6657
- await initStream(zipWriter.writer);
6658
- const { directoryOffset } = zipReader$1;
6659
- keptEntries.forEach(({ filename }) => {
6660
- if (filenames.has(filename)) {
6661
- throw new Error(ERR_DUPLICATED_NAME);
6662
- }
6663
- filenames.add(filename);
6664
- appendedFilenames.push(filename);
6665
- });
6666
- zipWriter.writerLocked = true;
6667
- const { lockWriter } = zipWriter;
6668
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6669
- zipWriter.writerLocked = false;
6670
- resolve();
6671
- });
6672
- await lockWriter;
6673
- if (zipWriter.addSplitZipSignature) {
6674
- delete zipWriter.addSplitZipSignature;
6675
- if (!await startsWithSplitZipSignature(reader)) {
6676
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6677
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6678
- }
6679
- }
6680
- const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
6681
- keptEntries.forEach(entry => {
6682
- const {
6683
- version,
6684
- rawLastModDate,
6685
- rawFilename,
6686
- bitFlag,
6687
- encrypted,
6688
- uncompressedSize,
6689
- compressedSize,
6690
- extraFieldZip64
6691
- } = entry;
6692
- let {
6693
- compressionMethod,
6694
- rawExtraField,
6695
- } = entry;
6696
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6697
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6698
- if (entry.extraFieldAES) {
6699
- compressionMethod = COMPRESSION_METHOD_AES;
6700
- }
6701
- const extraFieldLength = getLength(rawExtraField);
6702
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6703
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6704
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6705
- const {
6706
- headerArray,
6707
- headerView
6708
- } = getHeaderArrayData({
6709
- version,
6710
- bitFlag: bitFlagValue,
6711
- compressionMethod,
6712
- uncompressedSize,
6713
- compressedSize,
6714
- rawLastModDate,
6715
- rawFilename,
6716
- zip64CompressedSize,
6717
- zip64UncompressedSize,
6718
- extraFieldLength
6719
- });
6720
- const { crc32 } = entry;
6721
- if (crc32 !== UNDEFINED_VALUE) {
6722
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6723
- }
6724
- const { offset, diskNumberStart } = entryPositions.get(entry);
6725
- Object.assign(entry, {
6726
- zip64Enabled: true,
6727
- zip64UncompressedSize,
6728
- zip64CompressedSize,
6729
- offset,
6730
- diskNumberStart,
6731
- zip64DiskNumberStart: false,
6732
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6733
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6734
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6735
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6736
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6737
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6738
- rawExtraField,
6739
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6740
- headerArray,
6741
- headerView
6742
- });
6743
- fileEntries.set(entry.filename, entry);
6744
- });
6745
- } catch (error) {
6746
- appendedFilenames.forEach(filename => filenames.delete(filename));
6747
- throw error;
6748
- } finally {
6749
- resolveAppendZip();
6750
- pendingAddFileCalls.delete(promiseAppendZip);
6751
- if (releaseLockWriter) {
6752
- releaseLockWriter();
6753
- }
6754
- }
6719
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6755
6720
  }
6756
6721
 
6757
6722
  add(name = "", reader, options = {}) {
@@ -6913,11 +6878,149 @@
6913
6878
  return watchedPromise;
6914
6879
  }
6915
6880
 
6881
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6882
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6883
+ const filter = checkFunctionOption(options.filter);
6884
+ const readerOptions = checkReaderOptionsObject(options.readerOptions);
6885
+ while (pendingAddFileCalls.size) {
6886
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6887
+ }
6888
+ let resolveAppendZip;
6889
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6890
+ pendingAddFileCalls.add(promiseAppendZip);
6891
+ const appendedFilenames = [];
6892
+ let releaseLockWriter;
6893
+ try {
6894
+ reader = new GenericReader(reader);
6895
+ await initStream(reader);
6896
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6897
+ reader = new BlobReader(await streamToBlob(reader.readable));
6898
+ await initStream(reader);
6899
+ }
6900
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6901
+ const zipReader$1 = new ZipReader(reader, readerOptions);
6902
+ const keptEntries = [];
6903
+ let entries;
6904
+ try {
6905
+ entries = await zipReader$1.getEntries();
6906
+ for (const entry of entries) {
6907
+ if (!filter || await filter(entry, getExistingEntry(fileEntries, entry.filename))) {
6908
+ keptEntries.push(entry);
6909
+ }
6910
+ }
6911
+ } finally {
6912
+ await zipReader$1.close();
6913
+ }
6914
+ await initStream(zipWriter.writer);
6915
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6916
+ keptEntries.forEach(entry => {
6917
+ const { filename } = entry;
6918
+ if (entriesMissingZip64ExtraField.has(entry)) {
6919
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6920
+ }
6921
+ if (filenames.has(filename)) {
6922
+ throw new Error(ERR_DUPLICATED_NAME);
6923
+ }
6924
+ filenames.add(filename);
6925
+ appendedFilenames.push(filename);
6926
+ });
6927
+ zipWriter.writerLocked = true;
6928
+ const { lockWriter } = zipWriter;
6929
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6930
+ zipWriter.writerLocked = false;
6931
+ resolve();
6932
+ });
6933
+ await lockWriter;
6934
+ if (zipWriter.addSplitZipSignature) {
6935
+ delete zipWriter.addSplitZipSignature;
6936
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6937
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6938
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6939
+ }
6940
+ }
6941
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6942
+ keptEntries.forEach(entry => {
6943
+ const {
6944
+ version,
6945
+ rawLastModDate,
6946
+ rawFilename,
6947
+ rawBitFlag,
6948
+ uncompressedSize,
6949
+ compressedSize,
6950
+ extraFieldZip64
6951
+ } = entry;
6952
+ let {
6953
+ compressionMethod,
6954
+ rawExtraField,
6955
+ } = entry;
6956
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6957
+ if (entry.extraFieldAES) {
6958
+ compressionMethod = COMPRESSION_METHOD_AES;
6959
+ }
6960
+ const extraFieldLength = getLength(rawExtraField);
6961
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6962
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6963
+ const {
6964
+ headerArray,
6965
+ headerView
6966
+ } = getHeaderArrayData({
6967
+ version,
6968
+ bitFlag: rawBitFlag,
6969
+ compressionMethod,
6970
+ uncompressedSize,
6971
+ compressedSize,
6972
+ rawLastModDate,
6973
+ rawFilename,
6974
+ zip64CompressedSize,
6975
+ zip64UncompressedSize,
6976
+ extraFieldLength
6977
+ });
6978
+ const { crc32 } = entry;
6979
+ if (crc32 !== UNDEFINED_VALUE) {
6980
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6981
+ }
6982
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6983
+ fileEntries.set(entry.filename, Object.assign({}, entry, {
6984
+ zip64Enabled: true,
6985
+ zip64UncompressedSize,
6986
+ zip64CompressedSize,
6987
+ offset,
6988
+ diskNumberStart,
6989
+ zip64DiskNumberStart: false,
6990
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6991
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6992
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6993
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6994
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6995
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6996
+ rawExtraField,
6997
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6998
+ headerArray,
6999
+ headerView
7000
+ }));
7001
+ });
7002
+ } catch (error) {
7003
+ appendedFilenames.forEach(filename => filenames.delete(filename));
7004
+ throw error;
7005
+ } finally {
7006
+ resolveAppendZip();
7007
+ pendingAddFileCalls.delete(promiseAppendZip);
7008
+ if (releaseLockWriter) {
7009
+ releaseLockWriter();
7010
+ }
7011
+ }
7012
+ }
7013
+
7014
+ function getExistingEntry(fileEntries, filename) {
7015
+ const fileEntry = fileEntries.get(filename);
7016
+ return fileEntry ? new Entry(fileEntry) : UNDEFINED_VALUE;
7017
+ }
7018
+
6916
7019
  async function prependZipEntries(zipWriter, reader) {
6917
7020
  if (zipWriter.filenames.size) {
6918
7021
  throw new Error(ERR_ZIP_NOT_EMPTY);
6919
7022
  }
6920
- await zipWriter.appendZipEntries(reader);
7023
+ await appendZipEntries(zipWriter, reader);
6921
7024
  }
6922
7025
 
6923
7026
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8689,7 +8792,7 @@
8689
8792
  return rawExtraField;
8690
8793
  }
8691
8794
 
8692
- async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
8795
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8693
8796
  const { writer } = zipWriter;
8694
8797
  const entryPositions = new Map();
8695
8798
  if (writer.closeDisk || compact) {
@@ -8697,6 +8800,27 @@
8697
8800
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8698
8801
  const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8699
8802
  const keptEntrySet = new Set(keptEntries);
8803
+ const localHeaderLengths = new Map();
8804
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8805
+ const entry = sortedEntries[indexEntry];
8806
+ if (keptEntrySet.has(entry)) {
8807
+ const sourceOffset = getSourceOffset(reader, entry);
8808
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8809
+ if (localHeaderLength === UNDEFINED_VALUE) {
8810
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8811
+ }
8812
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8813
+ if (entry.bitFlag.dataDescriptor) {
8814
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8815
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8816
+ }
8817
+ if (entryEnd > regionEnds[indexEntry]) {
8818
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8819
+ }
8820
+ regionEnds[indexEntry] = entryEnd;
8821
+ localHeaderLengths.set(entry, localHeaderLength);
8822
+ }
8823
+ }
8700
8824
  let copiedOffset = 0;
8701
8825
  let lastPosition;
8702
8826
  for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
@@ -8709,7 +8833,7 @@
8709
8833
  if (!compact) {
8710
8834
  await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8711
8835
  }
8712
- if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8836
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8713
8837
  await writer.closeDisk();
8714
8838
  }
8715
8839
  lastPosition = {
@@ -8743,7 +8867,7 @@
8743
8867
  const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8744
8868
  const nextEntry = sortedEntries[indexEntry + 1];
8745
8869
  if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8746
- regionEnd = getSourceOffset(reader, nextEntry);
8870
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8747
8871
  }
8748
8872
  regionEnds[indexEntry] = regionEnd;
8749
8873
  }
@@ -8757,11 +8881,13 @@
8757
8881
  try {
8758
8882
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8759
8883
  } catch (error) {
8760
- zipWriter.hasCorruptedEntries = true;
8761
- try {
8762
- error.corruptedEntry = true;
8763
- } catch {
8764
- // ignored
8884
+ if (copiedLength) {
8885
+ zipWriter.hasCorruptedEntries = true;
8886
+ try {
8887
+ error.corruptedEntry = true;
8888
+ } catch {
8889
+ // ignored
8890
+ }
8765
8891
  }
8766
8892
  throw error;
8767
8893
  } finally {
@@ -8772,11 +8898,16 @@
8772
8898
  }
8773
8899
 
8774
8900
  async function getLocalHeaderLength(reader, offset) {
8775
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8901
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8902
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8903
+ EMPTY_UINT8_ARRAY;
8776
8904
  if (getLength(headerArray) < HEADER_SIZE) {
8777
- return HEADER_SIZE;
8905
+ return UNDEFINED_VALUE;
8778
8906
  }
8779
8907
  const headerView = getDataView(headerArray);
8908
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8909
+ return UNDEFINED_VALUE;
8910
+ }
8780
8911
  return HEADER_SIZE +
8781
8912
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8782
8913
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9127,7 +9258,7 @@
9127
9258
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9128
9259
  */
9129
9260
 
9130
- const VERSION = "2.20.0";
9261
+ const VERSION = "2.22.0";
9131
9262
 
9132
9263
  /*
9133
9264
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -10202,7 +10333,6 @@
10202
10333
  const DUPLICATES_KEEP_LAST = "keep-last";
10203
10334
  const DUPLICATES_VALUES = new Set([DUPLICATES_THROW, DUPLICATES_KEEP_FIRST, DUPLICATES_KEEP_LAST]);
10204
10335
  const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
10205
- const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
10206
10336
  const ERR_UNSUPPORTED_PASS_THROUGH_VALUE = "The 'compressed' passThrough option is only supported by Entry#getData() and ZipWriter#add()";
10207
10337
  const ERR_INVALID_PASSWORDS = "Invalid passwords option (must be an array of strings)";
10208
10338
  const ERR_INVALID_REQUEST_PASSWORD = "Invalid requestPassword option (must be a function returning a string or undefined)";
@@ -11128,9 +11258,7 @@
11128
11258
  }
11129
11259
 
11130
11260
  function checkReaderOptions(readerOptions) {
11131
- if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
11132
- throw new Error(ERR_INVALID_READER_OPTIONS);
11133
- }
11261
+ checkReaderOptionsObject(readerOptions);
11134
11262
  if (readerOptions) {
11135
11263
  checkPassThroughValue(readerOptions.passThrough);
11136
11264
  checkPasswordCandidatesOptions(readerOptions);