@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/index-native.cjs CHANGED
@@ -120,6 +120,10 @@ const MIN_DATE = new Date(1980, 0, 1);
120
120
 
121
121
  const UNDEFINED_VALUE = undefined;
122
122
  const INFINITY_VALUE = Infinity;
123
+ const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
124
+ const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
125
+ const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
126
+
123
127
  const UNDEFINED_TYPE = "undefined";
124
128
  const FUNCTION_TYPE = "function";
125
129
  const OBJECT_TYPE = "object";
@@ -215,6 +219,7 @@ const ERR_INVALID_FUNCTION_OPTION = "Invalid option (must be a function)";
215
219
  const ERR_INVALID_SIGNAL = "Invalid signal (must be an AbortSignal instance)";
216
220
  const ERR_INVALID_PASSWORD_TYPE = "Invalid password (password must be a string, rawPassword must be a Uint8Array)";
217
221
  const ERR_INVALID_PASS_THROUGH_VALUE = "Invalid passThrough option (must be a boolean or 'compressed')";
222
+ const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
218
223
  const ERR_ABORTED = "The operation was aborted";
219
224
  const ABORT_ERROR_NAME$1 = "AbortError";
220
225
 
@@ -225,6 +230,13 @@ function checkFunctionOption(value) {
225
230
  return value;
226
231
  }
227
232
 
233
+ function checkReaderOptionsObject(readerOptions) {
234
+ if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
235
+ throw new Error(ERR_INVALID_READER_OPTIONS);
236
+ }
237
+ return readerOptions;
238
+ }
239
+
228
240
  function checkSignalOption(signal) {
229
241
  if (signal && (typeof signal.addEventListener != FUNCTION_TYPE || typeof signal.aborted != BOOLEAN_TYPE)) {
230
242
  throw new Error(ERR_INVALID_SIGNAL);
@@ -320,7 +332,6 @@ const DEFAULT_CONFIGURATION = {
320
332
  workerStartupTimeout: 5000,
321
333
  useWebWorkers: true,
322
334
  useCompressionStream: true,
323
- transferStreams: true,
324
335
  CompressionStream: typeof CompressionStream != UNDEFINED_TYPE && CompressionStream,
325
336
  DecompressionStream: typeof DecompressionStream != UNDEFINED_TYPE && DecompressionStream
326
337
  };
@@ -334,8 +345,7 @@ const URI_PROPERTY_NAMES = [
334
345
  ];
335
346
  const BOOLEAN_PROPERTY_NAMES = [
336
347
  "useCompressionStream",
337
- "useWebWorkers",
338
- "transferStreams"
348
+ "useWebWorkers"
339
349
  ];
340
350
  const NUMBER_PROPERTY_NAMES = [
341
351
  "chunkSize",
@@ -4771,12 +4781,9 @@ const ERR_BAD_FORMAT = "File format is not recognized";
4771
4781
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4772
4782
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4773
4783
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4774
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4775
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4776
4784
  const ERR_ENCRYPTED = "File contains encrypted entry";
4777
4785
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4778
4786
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4779
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4780
4787
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4781
4788
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4782
4789
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4840,8 +4847,9 @@ class ZipReader {
4840
4847
  constructor(reader, options = {}) {
4841
4848
  Object.assign(this, {
4842
4849
  reader: new GenericReader(reader),
4843
- options,
4844
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4850
+ options: options || {},
4851
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4852
+ entriesMissingZip64ExtraField: new WeakSet()
4845
4853
  });
4846
4854
  }
4847
4855
 
@@ -4912,16 +4920,22 @@ class ZipReader {
4912
4920
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4913
4921
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4914
4922
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4915
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4916
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4917
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4918
- const originalDirectoryDataOffset = directoryDataOffset;
4919
- directoryDataOffset = expectedDirectoryDataOffset;
4920
- if (directoryDataOffset > originalDirectoryDataOffset) {
4921
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4923
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4924
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4925
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4926
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4927
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4928
+ }
4929
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4930
+ directoryDataOffset != expectedDirectoryDataOffset) {
4931
+ const originalDirectoryDataOffset = directoryDataOffset;
4932
+ directoryDataOffset = expectedDirectoryDataOffset;
4933
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4934
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4935
+ }
4936
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4937
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4922
4938
  }
4923
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4924
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4925
4939
  }
4926
4940
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4927
4941
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4966,10 +4980,6 @@ class ZipReader {
4966
4980
  let declaredDirectoryDataLength = directoryDataLength;
4967
4981
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4968
4982
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4969
- if (directoryDataOffset >= reader.size) {
4970
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4971
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4972
- }
4973
4983
  if (expectedLastDiskNumber != lastDiskNumber) {
4974
4984
  throw new Error(ERR_SPLIT_ZIP_FILE);
4975
4985
  }
@@ -4977,52 +4987,64 @@ class ZipReader {
4977
4987
  throw new Error(ERR_BAD_FORMAT);
4978
4988
  }
4979
4989
  let offset = 0;
4980
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4990
+ let directoryArray = directoryDataOffset < reader.size ?
4991
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
4992
+ EMPTY_UINT8_ARRAY;
4981
4993
  let directoryView = getDataView(directoryArray);
4982
4994
  if (directoryDataLength) {
4983
- if (directoryArray.length < 4) {
4984
- throw new Error(ERR_BAD_FORMAT);
4985
- }
4986
4995
  let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4987
4996
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4988
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4997
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
4998
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4989
4999
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4990
- detectEncryptedCentralDirectory(directoryView);
5000
+ detectEncryptedCentralDirectory(directoryView));
5001
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
5002
+ throw new Error(ERR_BAD_FORMAT);
5003
+ }
4991
5004
  let reconcile = !storedPointsAtDirectory;
4992
5005
  if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4993
- const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
4994
- if (storedPointsAtDirectory) {
4995
- reconcile = expectedPointsAtDirectory;
4996
- } else if (!expectedPointsAtDirectory) {
5006
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5007
+ if (!expectedPointsAtDirectory) {
4997
5008
  const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
4998
5009
  const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
4999
5010
  if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5000
5011
  await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5001
5012
  expectedDirectoryDataOffset = signedDirectoryDataOffset;
5013
+ expectedPointsAtDirectory = true;
5002
5014
  }
5003
5015
  }
5016
+ if (storedPointsAtDirectory) {
5017
+ reconcile = expectedPointsAtDirectory;
5018
+ }
5004
5019
  }
5005
- if (reconcile) {
5020
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
5006
5021
  const originalDirectoryDataOffset = directoryDataOffset;
5007
5022
  directoryDataOffset = expectedDirectoryDataOffset;
5008
5023
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5009
5024
  directoryView = getDataView(directoryArray);
5010
- if (directoryDataOffset > originalDirectoryDataOffset) {
5011
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5025
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5026
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5027
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5028
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5029
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5030
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5031
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5032
+ prependedDataLength += offsetDelta;
5012
5033
  prependedCentralDirectory = storedPointsAtDirectory;
5013
5034
  } else {
5014
5035
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5015
- const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5016
- if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
5017
- const localHeaderOffset = getUint32$1(directoryView, 42);
5018
- if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
5019
- await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
5020
- prependedDataLength += offsetDelta;
5021
- }
5036
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5037
+ prependedDataLength += offsetDelta;
5022
5038
  }
5023
5039
  }
5024
5040
  }
5025
5041
  }
5042
+ if (directoryArray.length < 4) {
5043
+ throw new Error(ERR_BAD_FORMAT);
5044
+ }
5045
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5046
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5047
+ directoryDataOffset = centralDirectoryEndOffset;
5026
5048
  }
5027
5049
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5028
5050
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5064,8 +5086,10 @@ class ZipReader {
5064
5086
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5065
5087
  }
5066
5088
  }
5089
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5090
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5067
5091
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5068
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5092
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5069
5093
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5070
5094
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5071
5095
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5156,13 +5180,17 @@ class ZipReader {
5156
5180
  if (malformedExtraField) {
5157
5181
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5158
5182
  }
5159
- fileEntry.offset += prependedDataLength;
5160
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5161
- startOffset = Math.min(entryPosition, startOffset);
5162
- if (entryPosition < previousEntryPosition) {
5163
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5183
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5184
+ startOffset = 0;
5185
+ } else {
5186
+ fileEntry.offset += prependedDataLength;
5187
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5188
+ startOffset = Math.min(entryPosition, startOffset);
5189
+ if (entryPosition < previousEntryPosition) {
5190
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5191
+ }
5192
+ previousEntryPosition = entryPosition;
5164
5193
  }
5165
- previousEntryPosition = entryPosition;
5166
5194
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5167
5195
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5168
5196
  }
@@ -5199,12 +5227,15 @@ class ZipReader {
5199
5227
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5200
5228
  });
5201
5229
  const entry = new Entry(fileEntry);
5202
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5230
+ if (fileEntry.zip64ExtraFieldMissing) {
5231
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5232
+ }
5233
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5203
5234
  entry.arrayBuffer = async options => {
5204
5235
  const writer = new TransformStream();
5205
5236
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5206
5237
  arrayBufferPromise.catch(() => { });
5207
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5238
+ await fileEntry.getData(writer, entry, zipReader,
5208
5239
  Object.assign({}, options, { preventClose: false }));
5209
5240
  return arrayBufferPromise;
5210
5241
  };
@@ -5412,8 +5443,9 @@ let ZipEntry$1 = class ZipEntry {
5412
5443
  });
5413
5444
  }
5414
5445
 
5415
- async getData(writer, fileEntry, readRanges, options = {}) {
5446
+ async getData(writer, fileEntry, zipReader, options = {}) {
5416
5447
  const zipEntry = this;
5448
+ const { readRanges, directoryOffset } = zipReader;
5417
5449
  if (zipEntry.zip64ExtraFieldMissing) {
5418
5450
  throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5419
5451
  }
@@ -5487,10 +5519,10 @@ let ZipEntry$1 = class ZipEntry {
5487
5519
  }
5488
5520
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5489
5521
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5490
- if (lastAccessDate) {
5522
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5491
5523
  fileEntry.lastAccessDate = lastAccessDate;
5492
5524
  }
5493
- if (creationDate) {
5525
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5494
5526
  fileEntry.creationDate = creationDate;
5495
5527
  }
5496
5528
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5519,7 +5551,7 @@ let ZipEntry$1 = class ZipEntry {
5519
5551
  throw new Error(ERR_ENCRYPTED);
5520
5552
  }
5521
5553
  }
5522
- if (dataOffset + compressedSize > reader.size) {
5554
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5523
5555
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5524
5556
  }
5525
5557
  const size = compressedSize;
@@ -5585,6 +5617,7 @@ let ZipEntry$1 = class ZipEntry {
5585
5617
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5586
5618
  readRanges
5587
5619
  });
5620
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5588
5621
  }
5589
5622
  let writable, abortError, aborted;
5590
5623
  try {
@@ -5604,10 +5637,7 @@ let ZipEntry$1 = class ZipEntry {
5604
5637
  writer.size += writtenSize;
5605
5638
  }
5606
5639
  } catch (error) {
5607
- const { outputSize: failedOutputSize } = workerOptions;
5608
- if (failedOutputSize !== UNDEFINED_VALUE) {
5609
- writer.size += failedOutputSize;
5610
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5640
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5611
5641
  writer.size += error.outputSize;
5612
5642
  }
5613
5643
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5635,13 +5665,7 @@ let ZipEntry$1 = class ZipEntry {
5635
5665
  };
5636
5666
 
5637
5667
  function detectEncryptedCentralDirectory(directoryView) {
5638
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5639
- for (let offset = 0; offset < maxOffset; offset++) {
5640
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5641
- return true;
5642
- }
5643
- }
5644
- return false;
5668
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5645
5669
  }
5646
5670
 
5647
5671
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5678,6 +5702,46 @@ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5678
5702
  }
5679
5703
  }
5680
5704
 
5705
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5706
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5707
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5708
+ const tailView = getDataView(tailArray);
5709
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5710
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5711
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5712
+ return startOffset + indexByte;
5713
+ }
5714
+ }
5715
+ }
5716
+
5717
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5718
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5719
+ return UNDEFINED_VALUE;
5720
+ }
5721
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5722
+ if (localHeaderOffset != MAX_32_BITS) {
5723
+ return localHeaderOffset;
5724
+ }
5725
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5726
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5727
+ while (offsetExtraField + 4 <= endExtraField) {
5728
+ const type = getUint16$1(directoryView, offsetExtraField);
5729
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5730
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5731
+ let offsetValue = offsetExtraField + 4;
5732
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5733
+ offsetValue += 8;
5734
+ }
5735
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5736
+ offsetValue += 8;
5737
+ }
5738
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5739
+ }
5740
+ offsetExtraField += 4 + size;
5741
+ }
5742
+ return UNDEFINED_VALUE;
5743
+ }
5744
+
5681
5745
  function readDigitalSignature(signatureRecordArray) {
5682
5746
  if (signatureRecordArray.length >= 6) {
5683
5747
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5767,6 +5831,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5767
5831
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5768
5832
  });
5769
5833
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5834
+ let extraFieldZip64Missing;
5770
5835
  if (extraFieldZip64) {
5771
5836
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5772
5837
  malformedExtraField = true;
@@ -5776,7 +5841,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5776
5841
  if (localDirectory) {
5777
5842
  malformedExtraField = true;
5778
5843
  } else {
5779
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5844
+ extraFieldZip64Missing = true;
5780
5845
  }
5781
5846
  }
5782
5847
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5834,6 +5899,9 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5834
5899
  if (extraFieldUSDZ) {
5835
5900
  directory.extraFieldUSDZ = extraFieldUSDZ;
5836
5901
  }
5902
+ if (extraFieldZip64Missing) {
5903
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5904
+ }
5837
5905
  return malformedExtraField;
5838
5906
  }
5839
5907
 
@@ -6048,13 +6116,7 @@ async function detectOverlappingEntry({
6048
6116
  let dataDescriptorLength = 0;
6049
6117
  if (dataDescriptor) {
6050
6118
  const zip64 = Boolean(extraFieldZip64);
6051
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
6052
- const dataDescriptorView = getDataView(dataDescriptorArray);
6053
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6054
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6055
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6056
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6057
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6119
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
6058
6120
  if (localDataDescriptor) {
6059
6121
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6060
6122
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6124,6 +6186,25 @@ function mergeRanges(sortedRanges, pendingRanges) {
6124
6186
  return mergedRanges;
6125
6187
  }
6126
6188
 
6189
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6190
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6191
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6192
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6193
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6194
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6195
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6196
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6197
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6198
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6199
+ }
6200
+
6201
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6202
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6203
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6204
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6205
+ }
6206
+ }
6207
+
6127
6208
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6128
6209
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6129
6210
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6383,6 +6464,18 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
6383
6464
  }
6384
6465
  }
6385
6466
 
6467
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6468
+ const { dataDescriptor } = localDirectory;
6469
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6470
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6471
+ if (dataDescriptor && !maskedLocalDirectory &&
6472
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6473
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6474
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6475
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6476
+ }
6477
+ }
6478
+
6386
6479
  function reportAmbiguity(reject, warnings, reason) {
6387
6480
  if (reject) {
6388
6481
  throwAmbiguousArchive(reason);
@@ -6485,6 +6578,7 @@ var zipReader = /*#__PURE__*/Object.freeze({
6485
6578
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6486
6579
  ZipReader: ZipReader,
6487
6580
  ZipReaderStream: ZipReaderStream,
6581
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6488
6582
  isZipFile: isZipFile
6489
6583
  });
6490
6584
 
@@ -6608,136 +6702,7 @@ class ZipWriter {
6608
6702
  }
6609
6703
 
6610
6704
  appendZip(reader, options = {}) {
6611
- return watchPromiseError(this, this.appendZipEntries(reader, options));
6612
- }
6613
-
6614
- async appendZipEntries(reader, options = {}) {
6615
- const zipWriter = this;
6616
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6617
- const filter = checkFunctionOption(options.filter);
6618
- while (pendingAddFileCalls.size) {
6619
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6620
- }
6621
- let resolveAppendZip;
6622
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6623
- pendingAddFileCalls.add(promiseAppendZip);
6624
- const appendedFilenames = [];
6625
- let releaseLockWriter;
6626
- try {
6627
- reader = new GenericReader(reader);
6628
- await initStream(reader);
6629
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6630
- reader = new BlobReader(await streamToBlob(reader.readable));
6631
- await initStream(reader);
6632
- }
6633
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6634
- const zipReader$1 = new ZipReader(reader);
6635
- const entries = await zipReader$1.getEntries();
6636
- const keptEntries = [];
6637
- for (const entry of entries) {
6638
- if (!filter || await filter(entry)) {
6639
- keptEntries.push(entry);
6640
- }
6641
- }
6642
- await zipReader$1.close();
6643
- await initStream(zipWriter.writer);
6644
- const { directoryOffset } = zipReader$1;
6645
- keptEntries.forEach(({ filename }) => {
6646
- if (filenames.has(filename)) {
6647
- throw new Error(ERR_DUPLICATED_NAME);
6648
- }
6649
- filenames.add(filename);
6650
- appendedFilenames.push(filename);
6651
- });
6652
- zipWriter.writerLocked = true;
6653
- const { lockWriter } = zipWriter;
6654
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6655
- zipWriter.writerLocked = false;
6656
- resolve();
6657
- });
6658
- await lockWriter;
6659
- if (zipWriter.addSplitZipSignature) {
6660
- delete zipWriter.addSplitZipSignature;
6661
- if (!await startsWithSplitZipSignature(reader)) {
6662
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6663
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6664
- }
6665
- }
6666
- const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
6667
- keptEntries.forEach(entry => {
6668
- const {
6669
- version,
6670
- rawLastModDate,
6671
- rawFilename,
6672
- bitFlag,
6673
- encrypted,
6674
- uncompressedSize,
6675
- compressedSize,
6676
- extraFieldZip64
6677
- } = entry;
6678
- let {
6679
- compressionMethod,
6680
- rawExtraField,
6681
- } = entry;
6682
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6683
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6684
- if (entry.extraFieldAES) {
6685
- compressionMethod = COMPRESSION_METHOD_AES;
6686
- }
6687
- const extraFieldLength = getLength(rawExtraField);
6688
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6689
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6690
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6691
- const {
6692
- headerArray,
6693
- headerView
6694
- } = getHeaderArrayData({
6695
- version,
6696
- bitFlag: bitFlagValue,
6697
- compressionMethod,
6698
- uncompressedSize,
6699
- compressedSize,
6700
- rawLastModDate,
6701
- rawFilename,
6702
- zip64CompressedSize,
6703
- zip64UncompressedSize,
6704
- extraFieldLength
6705
- });
6706
- const { crc32 } = entry;
6707
- if (crc32 !== UNDEFINED_VALUE) {
6708
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6709
- }
6710
- const { offset, diskNumberStart } = entryPositions.get(entry);
6711
- Object.assign(entry, {
6712
- zip64Enabled: true,
6713
- zip64UncompressedSize,
6714
- zip64CompressedSize,
6715
- offset,
6716
- diskNumberStart,
6717
- zip64DiskNumberStart: false,
6718
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6719
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6720
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6721
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6722
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6723
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6724
- rawExtraField,
6725
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6726
- headerArray,
6727
- headerView
6728
- });
6729
- fileEntries.set(entry.filename, entry);
6730
- });
6731
- } catch (error) {
6732
- appendedFilenames.forEach(filename => filenames.delete(filename));
6733
- throw error;
6734
- } finally {
6735
- resolveAppendZip();
6736
- pendingAddFileCalls.delete(promiseAppendZip);
6737
- if (releaseLockWriter) {
6738
- releaseLockWriter();
6739
- }
6740
- }
6705
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6741
6706
  }
6742
6707
 
6743
6708
  add(name = "", reader, options = {}) {
@@ -6899,11 +6864,149 @@ function watchPromiseError(zipWriter, promise) {
6899
6864
  return watchedPromise;
6900
6865
  }
6901
6866
 
6867
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6868
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6869
+ const filter = checkFunctionOption(options.filter);
6870
+ const readerOptions = checkReaderOptionsObject(options.readerOptions);
6871
+ while (pendingAddFileCalls.size) {
6872
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6873
+ }
6874
+ let resolveAppendZip;
6875
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6876
+ pendingAddFileCalls.add(promiseAppendZip);
6877
+ const appendedFilenames = [];
6878
+ let releaseLockWriter;
6879
+ try {
6880
+ reader = new GenericReader(reader);
6881
+ await initStream(reader);
6882
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6883
+ reader = new BlobReader(await streamToBlob(reader.readable));
6884
+ await initStream(reader);
6885
+ }
6886
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6887
+ const zipReader$1 = new ZipReader(reader, readerOptions);
6888
+ const keptEntries = [];
6889
+ let entries;
6890
+ try {
6891
+ entries = await zipReader$1.getEntries();
6892
+ for (const entry of entries) {
6893
+ if (!filter || await filter(entry, getExistingEntry(fileEntries, entry.filename))) {
6894
+ keptEntries.push(entry);
6895
+ }
6896
+ }
6897
+ } finally {
6898
+ await zipReader$1.close();
6899
+ }
6900
+ await initStream(zipWriter.writer);
6901
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6902
+ keptEntries.forEach(entry => {
6903
+ const { filename } = entry;
6904
+ if (entriesMissingZip64ExtraField.has(entry)) {
6905
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6906
+ }
6907
+ if (filenames.has(filename)) {
6908
+ throw new Error(ERR_DUPLICATED_NAME);
6909
+ }
6910
+ filenames.add(filename);
6911
+ appendedFilenames.push(filename);
6912
+ });
6913
+ zipWriter.writerLocked = true;
6914
+ const { lockWriter } = zipWriter;
6915
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6916
+ zipWriter.writerLocked = false;
6917
+ resolve();
6918
+ });
6919
+ await lockWriter;
6920
+ if (zipWriter.addSplitZipSignature) {
6921
+ delete zipWriter.addSplitZipSignature;
6922
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6923
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6924
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6925
+ }
6926
+ }
6927
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6928
+ keptEntries.forEach(entry => {
6929
+ const {
6930
+ version,
6931
+ rawLastModDate,
6932
+ rawFilename,
6933
+ rawBitFlag,
6934
+ uncompressedSize,
6935
+ compressedSize,
6936
+ extraFieldZip64
6937
+ } = entry;
6938
+ let {
6939
+ compressionMethod,
6940
+ rawExtraField,
6941
+ } = entry;
6942
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6943
+ if (entry.extraFieldAES) {
6944
+ compressionMethod = COMPRESSION_METHOD_AES;
6945
+ }
6946
+ const extraFieldLength = getLength(rawExtraField);
6947
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6948
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6949
+ const {
6950
+ headerArray,
6951
+ headerView
6952
+ } = getHeaderArrayData({
6953
+ version,
6954
+ bitFlag: rawBitFlag,
6955
+ compressionMethod,
6956
+ uncompressedSize,
6957
+ compressedSize,
6958
+ rawLastModDate,
6959
+ rawFilename,
6960
+ zip64CompressedSize,
6961
+ zip64UncompressedSize,
6962
+ extraFieldLength
6963
+ });
6964
+ const { crc32 } = entry;
6965
+ if (crc32 !== UNDEFINED_VALUE) {
6966
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6967
+ }
6968
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6969
+ fileEntries.set(entry.filename, Object.assign({}, entry, {
6970
+ zip64Enabled: true,
6971
+ zip64UncompressedSize,
6972
+ zip64CompressedSize,
6973
+ offset,
6974
+ diskNumberStart,
6975
+ zip64DiskNumberStart: false,
6976
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6977
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6978
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6979
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6980
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6981
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6982
+ rawExtraField,
6983
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6984
+ headerArray,
6985
+ headerView
6986
+ }));
6987
+ });
6988
+ } catch (error) {
6989
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6990
+ throw error;
6991
+ } finally {
6992
+ resolveAppendZip();
6993
+ pendingAddFileCalls.delete(promiseAppendZip);
6994
+ if (releaseLockWriter) {
6995
+ releaseLockWriter();
6996
+ }
6997
+ }
6998
+ }
6999
+
7000
+ function getExistingEntry(fileEntries, filename) {
7001
+ const fileEntry = fileEntries.get(filename);
7002
+ return fileEntry ? new Entry(fileEntry) : UNDEFINED_VALUE;
7003
+ }
7004
+
6902
7005
  async function prependZipEntries(zipWriter, reader) {
6903
7006
  if (zipWriter.filenames.size) {
6904
7007
  throw new Error(ERR_ZIP_NOT_EMPTY);
6905
7008
  }
6906
- await zipWriter.appendZipEntries(reader);
7009
+ await appendZipEntries(zipWriter, reader);
6907
7010
  }
6908
7011
 
6909
7012
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8675,7 +8778,7 @@ function removeExtraFieldZip64(rawExtraField) {
8675
8778
  return rawExtraField;
8676
8779
  }
8677
8780
 
8678
- async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
8781
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8679
8782
  const { writer } = zipWriter;
8680
8783
  const entryPositions = new Map();
8681
8784
  if (writer.closeDisk || compact) {
@@ -8683,6 +8786,27 @@ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOff
8683
8786
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8684
8787
  const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8685
8788
  const keptEntrySet = new Set(keptEntries);
8789
+ const localHeaderLengths = new Map();
8790
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8791
+ const entry = sortedEntries[indexEntry];
8792
+ if (keptEntrySet.has(entry)) {
8793
+ const sourceOffset = getSourceOffset(reader, entry);
8794
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8795
+ if (localHeaderLength === UNDEFINED_VALUE) {
8796
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8797
+ }
8798
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8799
+ if (entry.bitFlag.dataDescriptor) {
8800
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8801
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8802
+ }
8803
+ if (entryEnd > regionEnds[indexEntry]) {
8804
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8805
+ }
8806
+ regionEnds[indexEntry] = entryEnd;
8807
+ localHeaderLengths.set(entry, localHeaderLength);
8808
+ }
8809
+ }
8686
8810
  let copiedOffset = 0;
8687
8811
  let lastPosition;
8688
8812
  for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
@@ -8695,7 +8819,7 @@ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOff
8695
8819
  if (!compact) {
8696
8820
  await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8697
8821
  }
8698
- if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8822
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8699
8823
  await writer.closeDisk();
8700
8824
  }
8701
8825
  lastPosition = {
@@ -8729,7 +8853,7 @@ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8729
8853
  const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8730
8854
  const nextEntry = sortedEntries[indexEntry + 1];
8731
8855
  if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8732
- regionEnd = getSourceOffset(reader, nextEntry);
8856
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8733
8857
  }
8734
8858
  regionEnds[indexEntry] = regionEnd;
8735
8859
  }
@@ -8743,11 +8867,13 @@ async function copyData(zipWriter, reader, offset, size) {
8743
8867
  try {
8744
8868
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8745
8869
  } catch (error) {
8746
- zipWriter.hasCorruptedEntries = true;
8747
- try {
8748
- error.corruptedEntry = true;
8749
- } catch {
8750
- // ignored
8870
+ if (copiedLength) {
8871
+ zipWriter.hasCorruptedEntries = true;
8872
+ try {
8873
+ error.corruptedEntry = true;
8874
+ } catch {
8875
+ // ignored
8876
+ }
8751
8877
  }
8752
8878
  throw error;
8753
8879
  } finally {
@@ -8758,11 +8884,16 @@ async function copyData(zipWriter, reader, offset, size) {
8758
8884
  }
8759
8885
 
8760
8886
  async function getLocalHeaderLength(reader, offset) {
8761
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8887
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8888
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8889
+ EMPTY_UINT8_ARRAY;
8762
8890
  if (getLength(headerArray) < HEADER_SIZE) {
8763
- return HEADER_SIZE;
8891
+ return UNDEFINED_VALUE;
8764
8892
  }
8765
8893
  const headerView = getDataView(headerArray);
8894
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8895
+ return UNDEFINED_VALUE;
8896
+ }
8766
8897
  return HEADER_SIZE +
8767
8898
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8768
8899
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9113,7 +9244,7 @@ function formatSupported(StreamClass, format) {
9113
9244
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9114
9245
  */
9115
9246
 
9116
- const VERSION = "2.20.0";
9247
+ const VERSION = "2.22.0";
9117
9248
 
9118
9249
  /*
9119
9250
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -9642,7 +9773,6 @@ const DUPLICATES_KEEP_FIRST = "keep-first";
9642
9773
  const DUPLICATES_KEEP_LAST = "keep-last";
9643
9774
  const DUPLICATES_VALUES = new Set([DUPLICATES_THROW, DUPLICATES_KEEP_FIRST, DUPLICATES_KEEP_LAST]);
9644
9775
  const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
9645
- const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
9646
9776
  const ERR_UNSUPPORTED_PASS_THROUGH_VALUE = "The 'compressed' passThrough option is only supported by Entry#getData() and ZipWriter#add()";
9647
9777
  const ERR_INVALID_PASSWORDS = "Invalid passwords option (must be an array of strings)";
9648
9778
  const ERR_INVALID_REQUEST_PASSWORD = "Invalid requestPassword option (must be a function returning a string or undefined)";
@@ -10568,9 +10698,7 @@ function getDeterminedSize(child, passThrough) {
10568
10698
  }
10569
10699
 
10570
10700
  function checkReaderOptions(readerOptions) {
10571
- if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
10572
- throw new Error(ERR_INVALID_READER_OPTIONS);
10573
- }
10701
+ checkReaderOptionsObject(readerOptions);
10574
10702
  if (readerOptions) {
10575
10703
  checkPassThroughValue(readerOptions.passThrough);
10576
10704
  checkPasswordCandidatesOptions(readerOptions);