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