@zip.js/zip.js 2.19.0 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -126,6 +126,10 @@
126
126
 
127
127
  const UNDEFINED_VALUE = undefined;
128
128
  const INFINITY_VALUE = Infinity;
129
+ const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
130
+ const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
131
+ const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
132
+
129
133
  const UNDEFINED_TYPE = "undefined";
130
134
  const FUNCTION_TYPE = "function";
131
135
  const OBJECT_TYPE = "object";
@@ -4757,12 +4761,9 @@
4757
4761
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4758
4762
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4759
4763
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4760
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4761
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4762
4764
  const ERR_ENCRYPTED = "File contains encrypted entry";
4763
4765
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4764
4766
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4765
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4766
4767
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4767
4768
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4768
4769
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4781,6 +4782,8 @@
4781
4782
  const WARNING_PREPENDED_DATA = "prepended data";
4782
4783
  const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
4783
4784
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
4785
+ const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = "mismatched central directory offset";
4786
+ const WARNING_MISSING_ZIP64_EXTRA_FIELD = "missing zip64 extra field";
4784
4787
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
4785
4788
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
4786
4789
  const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
@@ -4825,7 +4828,8 @@
4825
4828
  Object.assign(this, {
4826
4829
  reader: new GenericReader(reader),
4827
4830
  options,
4828
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4831
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4832
+ entriesMissingZip64ExtraField: new WeakSet()
4829
4833
  });
4830
4834
  }
4831
4835
 
@@ -4896,16 +4900,22 @@
4896
4900
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4897
4901
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4898
4902
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4899
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4900
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4901
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4902
- const originalDirectoryDataOffset = directoryDataOffset;
4903
- directoryDataOffset = expectedDirectoryDataOffset;
4904
- if (directoryDataOffset > originalDirectoryDataOffset) {
4905
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4903
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4904
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4905
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4906
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4907
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4908
+ }
4909
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4910
+ directoryDataOffset != expectedDirectoryDataOffset) {
4911
+ const originalDirectoryDataOffset = directoryDataOffset;
4912
+ directoryDataOffset = expectedDirectoryDataOffset;
4913
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4914
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4915
+ }
4916
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4917
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4906
4918
  }
4907
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4908
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4909
4919
  }
4910
4920
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4911
4921
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4943,15 +4953,13 @@
4943
4953
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4944
4954
  }
4945
4955
  directoryDataOffset = getDiskOffset$1(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
4956
+ } else if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || diskNumber == MAX_16_BITS) {
4957
+ throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
4946
4958
  }
4947
4959
  }
4948
4960
  let declaredDirectoryDataLength = directoryDataLength;
4949
4961
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4950
4962
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4951
- if (directoryDataOffset >= reader.size) {
4952
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4953
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4954
- }
4955
4963
  if (expectedLastDiskNumber != lastDiskNumber) {
4956
4964
  throw new Error(ERR_SPLIT_ZIP_FILE);
4957
4965
  }
@@ -4959,33 +4967,64 @@
4959
4967
  throw new Error(ERR_BAD_FORMAT);
4960
4968
  }
4961
4969
  let offset = 0;
4962
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4970
+ let directoryArray = directoryDataOffset < reader.size ?
4971
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
4972
+ EMPTY_UINT8_ARRAY;
4963
4973
  let directoryView = getDataView(directoryArray);
4964
4974
  if (directoryDataLength) {
4965
- if (directoryArray.length < 4) {
4966
- throw new Error(ERR_BAD_FORMAT);
4967
- }
4968
- const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4975
+ let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4969
4976
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4970
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4977
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
4978
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4971
4979
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4972
- detectEncryptedCentralDirectory(directoryView);
4980
+ detectEncryptedCentralDirectory(directoryView));
4981
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
4982
+ throw new Error(ERR_BAD_FORMAT);
4983
+ }
4973
4984
  let reconcile = !storedPointsAtDirectory;
4974
- if (!reconcile && expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4975
- const expectedSignatureArray = await readUint8Array(reader, expectedDirectoryDataOffset, 4);
4976
- reconcile = getUint32$1(getDataView(expectedSignatureArray), 0) == CENTRAL_FILE_HEADER_SIGNATURE;
4985
+ if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4986
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
4987
+ if (!expectedPointsAtDirectory) {
4988
+ const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
4989
+ const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
4990
+ if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
4991
+ await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
4992
+ expectedDirectoryDataOffset = signedDirectoryDataOffset;
4993
+ expectedPointsAtDirectory = true;
4994
+ }
4995
+ }
4996
+ if (storedPointsAtDirectory) {
4997
+ reconcile = expectedPointsAtDirectory;
4998
+ }
4977
4999
  }
4978
- if (reconcile) {
5000
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
4979
5001
  const originalDirectoryDataOffset = directoryDataOffset;
4980
5002
  directoryDataOffset = expectedDirectoryDataOffset;
4981
- if (directoryDataOffset > originalDirectoryDataOffset) {
4982
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
4983
- prependedCentralDirectory = storedPointsAtDirectory;
4984
- }
4985
5003
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4986
5004
  directoryView = getDataView(directoryArray);
5005
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5006
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5007
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5008
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5009
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5010
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5011
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5012
+ prependedDataLength += offsetDelta;
5013
+ prependedCentralDirectory = storedPointsAtDirectory;
5014
+ } else {
5015
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5016
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5017
+ prependedDataLength += offsetDelta;
5018
+ }
5019
+ }
4987
5020
  }
4988
5021
  }
5022
+ if (directoryArray.length < 4) {
5023
+ throw new Error(ERR_BAD_FORMAT);
5024
+ }
5025
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5026
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5027
+ directoryDataOffset = centralDirectoryEndOffset;
4989
5028
  }
4990
5029
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
4991
5030
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5027,8 +5066,10 @@
5027
5066
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5028
5067
  }
5029
5068
  }
5069
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5070
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5030
5071
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5031
- const fileEntry = new ZipEntry(reader, zipReader.options);
5072
+ const fileEntry = new ZipEntry(reader, entryOptions);
5032
5073
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5033
5074
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5034
5075
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5094,7 +5135,16 @@
5094
5135
  filename,
5095
5136
  comment
5096
5137
  });
5097
- const malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5138
+ let malformedExtraField;
5139
+ try {
5140
+ malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5141
+ } catch (error) {
5142
+ if (checkAmbiguity || error.message != ERR_EXTRAFIELD_ZIP64_NOT_FOUND) {
5143
+ throw error;
5144
+ }
5145
+ fileEntry.zip64ExtraFieldMissing = true;
5146
+ addWarning(warnings, WARNING_MISSING_ZIP64_EXTRA_FIELD, fileEntry.filename);
5147
+ }
5098
5148
  filename = fileEntry.filename;
5099
5149
  if (normalizeFilename) {
5100
5150
  const normalizedFilename = normalizeFilename(filename);
@@ -5110,13 +5160,17 @@
5110
5160
  if (malformedExtraField) {
5111
5161
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5112
5162
  }
5113
- fileEntry.offset += prependedDataLength;
5114
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5115
- startOffset = Math.min(entryPosition, startOffset);
5116
- if (entryPosition < previousEntryPosition) {
5117
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5163
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5164
+ startOffset = 0;
5165
+ } else {
5166
+ fileEntry.offset += prependedDataLength;
5167
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5168
+ startOffset = Math.min(entryPosition, startOffset);
5169
+ if (entryPosition < previousEntryPosition) {
5170
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5171
+ }
5172
+ previousEntryPosition = entryPosition;
5118
5173
  }
5119
- previousEntryPosition = entryPosition;
5120
5174
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5121
5175
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5122
5176
  }
@@ -5136,11 +5190,12 @@
5136
5190
  const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
5137
5191
  const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
5138
5192
  const symlink = (unixType & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK;
5139
- const executable = !symlink && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5140
- ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5141
- : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5142
5193
  const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5143
5194
  const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5195
+ const directory = modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE);
5196
+ const executable = !symlink && !directory && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5197
+ ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5198
+ : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5144
5199
  Object.assign(fileEntry, {
5145
5200
  setuid,
5146
5201
  setgid,
@@ -5148,16 +5203,19 @@
5148
5203
  symlink,
5149
5204
  unixExternalUpper,
5150
5205
  executable,
5151
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
5206
+ directory,
5152
5207
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5153
5208
  });
5154
5209
  const entry = new Entry(fileEntry);
5155
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5210
+ if (fileEntry.zip64ExtraFieldMissing) {
5211
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5212
+ }
5213
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5156
5214
  entry.arrayBuffer = async options => {
5157
5215
  const writer = new TransformStream();
5158
5216
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5159
5217
  arrayBufferPromise.catch(() => { });
5160
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5218
+ await fileEntry.getData(writer, entry, zipReader,
5161
5219
  Object.assign({}, options, { preventClose: false }));
5162
5220
  return arrayBufferPromise;
5163
5221
  };
@@ -5204,10 +5262,10 @@
5204
5262
  const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
5205
5263
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
5206
5264
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
5207
- if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
5265
+ if (checkAmbiguity && (prependedDataLength > 0 || (filesLength && startOffset > splitZipSignatureLength))) {
5208
5266
  throwAmbiguousArchive(WARNING_PREPENDED_DATA);
5209
5267
  }
5210
- if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5268
+ if (prependedDataLength > 0 || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5211
5269
  addWarning(warnings, WARNING_PREPENDED_DATA);
5212
5270
  }
5213
5271
  if (prependedCentralDirectory) {
@@ -5365,8 +5423,12 @@
5365
5423
  });
5366
5424
  }
5367
5425
 
5368
- async getData(writer, fileEntry, readRanges, options = {}) {
5426
+ async getData(writer, fileEntry, zipReader, options = {}) {
5369
5427
  const zipEntry = this;
5428
+ const { readRanges, directoryOffset } = zipReader;
5429
+ if (zipEntry.zip64ExtraFieldMissing) {
5430
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5431
+ }
5370
5432
  const config = getConfiguration();
5371
5433
  const {
5372
5434
  reader,
@@ -5437,10 +5499,10 @@
5437
5499
  }
5438
5500
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5439
5501
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5440
- if (lastAccessDate) {
5502
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5441
5503
  fileEntry.lastAccessDate = lastAccessDate;
5442
5504
  }
5443
- if (creationDate) {
5505
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5444
5506
  fileEntry.creationDate = creationDate;
5445
5507
  }
5446
5508
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5469,7 +5531,7 @@
5469
5531
  throw new Error(ERR_ENCRYPTED);
5470
5532
  }
5471
5533
  }
5472
- if (dataOffset + compressedSize > reader.size) {
5534
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5473
5535
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5474
5536
  }
5475
5537
  const size = compressedSize;
@@ -5535,6 +5597,7 @@
5535
5597
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5536
5598
  readRanges
5537
5599
  });
5600
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5538
5601
  }
5539
5602
  let writable, abortError, aborted;
5540
5603
  try {
@@ -5554,10 +5617,7 @@
5554
5617
  writer.size += writtenSize;
5555
5618
  }
5556
5619
  } catch (error) {
5557
- const { outputSize: failedOutputSize } = workerOptions;
5558
- if (failedOutputSize !== UNDEFINED_VALUE) {
5559
- writer.size += failedOutputSize;
5560
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5620
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5561
5621
  writer.size += error.outputSize;
5562
5622
  }
5563
5623
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5585,13 +5645,7 @@
5585
5645
  }
5586
5646
 
5587
5647
  function detectEncryptedCentralDirectory(directoryView) {
5588
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5589
- for (let offset = 0; offset < maxOffset; offset++) {
5590
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5591
- return true;
5592
- }
5593
- }
5594
- return false;
5648
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5595
5649
  }
5596
5650
 
5597
5651
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5604,6 +5658,70 @@
5604
5658
  return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
5605
5659
  }
5606
5660
 
5661
+ function pointsAtDirectory(reader, offset) {
5662
+ return startsWithSignature(reader, offset, CENTRAL_FILE_HEADER_SIGNATURE);
5663
+ }
5664
+
5665
+ async function startsWithSignature(reader, offset, signature) {
5666
+ if (offset < 0 || offset + 4 > reader.size) {
5667
+ return false;
5668
+ }
5669
+ const signatureArray = await readUint8Array(reader, offset, 4);
5670
+ return getUint32$1(getDataView(signatureArray), 0) == signature;
5671
+ }
5672
+
5673
+ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5674
+ const startOffset = Math.max(0, endOffset - (6 + MAX_16_BITS));
5675
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5676
+ const tailView = getDataView(tailArray);
5677
+ for (let indexByte = tailArray.length - 6; indexByte >= 0; indexByte--) {
5678
+ if (getUint32$1(tailView, indexByte) == DIGITAL_SIGNATURE_RECORD_SIGNATURE &&
5679
+ indexByte + 6 + getUint16$1(tailView, indexByte + 4) == tailArray.length) {
5680
+ return startOffset + indexByte;
5681
+ }
5682
+ }
5683
+ }
5684
+
5685
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5686
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5687
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5688
+ const tailView = getDataView(tailArray);
5689
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5690
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5691
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5692
+ return startOffset + indexByte;
5693
+ }
5694
+ }
5695
+ }
5696
+
5697
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5698
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5699
+ return UNDEFINED_VALUE;
5700
+ }
5701
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5702
+ if (localHeaderOffset != MAX_32_BITS) {
5703
+ return localHeaderOffset;
5704
+ }
5705
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5706
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5707
+ while (offsetExtraField + 4 <= endExtraField) {
5708
+ const type = getUint16$1(directoryView, offsetExtraField);
5709
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5710
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5711
+ let offsetValue = offsetExtraField + 4;
5712
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5713
+ offsetValue += 8;
5714
+ }
5715
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5716
+ offsetValue += 8;
5717
+ }
5718
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5719
+ }
5720
+ offsetExtraField += 4 + size;
5721
+ }
5722
+ return UNDEFINED_VALUE;
5723
+ }
5724
+
5607
5725
  function readDigitalSignature(signatureRecordArray) {
5608
5726
  if (signatureRecordArray.length >= 6) {
5609
5727
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5693,6 +5811,7 @@
5693
5811
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5694
5812
  });
5695
5813
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5814
+ let extraFieldZip64Missing;
5696
5815
  if (extraFieldZip64) {
5697
5816
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5698
5817
  malformedExtraField = true;
@@ -5702,7 +5821,7 @@
5702
5821
  if (localDirectory) {
5703
5822
  malformedExtraField = true;
5704
5823
  } else {
5705
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5824
+ extraFieldZip64Missing = true;
5706
5825
  }
5707
5826
  }
5708
5827
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5735,6 +5854,11 @@
5735
5854
  readExtraFieldUnixDates(extraFieldUnixType1, directory);
5736
5855
  directory.extraFieldUnixType1 = extraFieldUnixType1;
5737
5856
  }
5857
+ const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5858
+ if (extraFieldExtendedTimestamp) {
5859
+ readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5860
+ directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5861
+ }
5738
5862
  const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS);
5739
5863
  if (extraFieldNTFS) {
5740
5864
  readExtraFieldNTFS(extraFieldNTFS, directory);
@@ -5751,15 +5875,13 @@
5751
5875
  readExtraFieldUnix(extraFieldUnix, unixIdsRead ? {} : directory, false);
5752
5876
  directory.extraFieldUnix = extraFieldUnix;
5753
5877
  }
5754
- const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5755
- if (extraFieldExtendedTimestamp) {
5756
- readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5757
- directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5758
- }
5759
5878
  const extraFieldUSDZ = extraField.get(EXTRAFIELD_TYPE_USDZ);
5760
5879
  if (extraFieldUSDZ) {
5761
5880
  directory.extraFieldUSDZ = extraFieldUSDZ;
5762
5881
  }
5882
+ if (extraFieldZip64Missing) {
5883
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5884
+ }
5763
5885
  return malformedExtraField;
5764
5886
  }
5765
5887
 
@@ -5974,13 +6096,7 @@
5974
6096
  let dataDescriptorLength = 0;
5975
6097
  if (dataDescriptor) {
5976
6098
  const zip64 = Boolean(extraFieldZip64);
5977
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
5978
- const dataDescriptorView = getDataView(dataDescriptorArray);
5979
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
5980
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
5981
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
5982
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
5983
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6099
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
5984
6100
  if (localDataDescriptor) {
5985
6101
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
5986
6102
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6050,6 +6166,25 @@
6050
6166
  return mergedRanges;
6051
6167
  }
6052
6168
 
6169
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6170
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6171
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6172
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6173
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6174
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6175
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6176
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6177
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6178
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6179
+ }
6180
+
6181
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6182
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6183
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6184
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6185
+ }
6186
+ }
6187
+
6053
6188
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6054
6189
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6055
6190
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6309,6 +6444,18 @@
6309
6444
  }
6310
6445
  }
6311
6446
 
6447
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6448
+ const { dataDescriptor } = localDirectory;
6449
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6450
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6451
+ if (dataDescriptor && !maskedLocalDirectory &&
6452
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6453
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6454
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6455
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6456
+ }
6457
+ }
6458
+
6312
6459
  function reportAmbiguity(reject, warnings, reason) {
6313
6460
  if (reject) {
6314
6461
  throwAmbiguousArchive(reason);
@@ -6394,11 +6541,13 @@
6394
6541
  WARNING_COMPRESSED_PATCHED_DATA: WARNING_COMPRESSED_PATCHED_DATA,
6395
6542
  WARNING_DUPLICATE_FILENAME: WARNING_DUPLICATE_FILENAME,
6396
6543
  WARNING_MALFORMED_EXTRA_FIELD: WARNING_MALFORMED_EXTRA_FIELD,
6544
+ WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET: WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET,
6397
6545
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG: WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
6398
6546
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD: WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
6399
6547
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES: WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES,
6400
6548
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME: WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
6401
6549
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY: WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
6550
+ WARNING_MISSING_ZIP64_EXTRA_FIELD: WARNING_MISSING_ZIP64_EXTRA_FIELD,
6402
6551
  WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY: WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
6403
6552
  WARNING_PREPENDED_CENTRAL_DIRECTORY: WARNING_PREPENDED_CENTRAL_DIRECTORY,
6404
6553
  WARNING_PREPENDED_DATA: WARNING_PREPENDED_DATA,
@@ -6409,6 +6558,7 @@
6409
6558
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6410
6559
  ZipReader: ZipReader,
6411
6560
  ZipReaderStream: ZipReaderStream,
6561
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6412
6562
  isZipFile: isZipFile
6413
6563
  });
6414
6564
 
@@ -6531,130 +6681,8 @@
6531
6681
  return watchPromiseError(this, prependZipEntries(this, reader));
6532
6682
  }
6533
6683
 
6534
- appendZip(reader) {
6535
- return watchPromiseError(this, this.appendZipEntries(reader));
6536
- }
6537
-
6538
- async appendZipEntries(reader) {
6539
- const zipWriter = this;
6540
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6541
- while (pendingAddFileCalls.size) {
6542
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6543
- }
6544
- let resolveAppendZip;
6545
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6546
- pendingAddFileCalls.add(promiseAppendZip);
6547
- const appendedFilenames = [];
6548
- let releaseLockWriter;
6549
- try {
6550
- reader = new GenericReader(reader);
6551
- await initStream(reader);
6552
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6553
- reader = new BlobReader(await streamToBlob(reader.readable));
6554
- await initStream(reader);
6555
- }
6556
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6557
- const zipReader$1 = new ZipReader(reader);
6558
- const entries = await zipReader$1.getEntries();
6559
- await zipReader$1.close();
6560
- await initStream(zipWriter.writer);
6561
- const { directoryOffset } = zipReader$1;
6562
- entries.forEach(({ filename }) => {
6563
- if (filenames.has(filename)) {
6564
- throw new Error(ERR_DUPLICATED_NAME);
6565
- }
6566
- filenames.add(filename);
6567
- appendedFilenames.push(filename);
6568
- });
6569
- zipWriter.writerLocked = true;
6570
- const { lockWriter } = zipWriter;
6571
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6572
- zipWriter.writerLocked = false;
6573
- resolve();
6574
- });
6575
- await lockWriter;
6576
- if (zipWriter.addSplitZipSignature) {
6577
- delete zipWriter.addSplitZipSignature;
6578
- if (!await startsWithSplitZipSignature(reader)) {
6579
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6580
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6581
- }
6582
- }
6583
- const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
6584
- entries.forEach(entry => {
6585
- const {
6586
- version,
6587
- rawLastModDate,
6588
- rawFilename,
6589
- bitFlag,
6590
- encrypted,
6591
- uncompressedSize,
6592
- compressedSize,
6593
- extraFieldZip64
6594
- } = entry;
6595
- let {
6596
- compressionMethod,
6597
- rawExtraField,
6598
- } = entry;
6599
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6600
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6601
- if (entry.extraFieldAES) {
6602
- compressionMethod = COMPRESSION_METHOD_AES;
6603
- }
6604
- const extraFieldLength = getLength(rawExtraField);
6605
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6606
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6607
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6608
- const {
6609
- headerArray,
6610
- headerView
6611
- } = getHeaderArrayData({
6612
- version,
6613
- bitFlag: bitFlagValue,
6614
- compressionMethod,
6615
- uncompressedSize,
6616
- compressedSize,
6617
- rawLastModDate,
6618
- rawFilename,
6619
- zip64CompressedSize,
6620
- zip64UncompressedSize,
6621
- extraFieldLength
6622
- });
6623
- const { crc32 } = entry;
6624
- if (crc32 !== UNDEFINED_VALUE) {
6625
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6626
- }
6627
- const { offset, diskNumberStart } = entryPositions.get(entry);
6628
- Object.assign(entry, {
6629
- zip64Enabled: true,
6630
- zip64UncompressedSize,
6631
- zip64CompressedSize,
6632
- offset,
6633
- diskNumberStart,
6634
- zip64DiskNumberStart: false,
6635
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6636
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6637
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6638
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6639
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6640
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6641
- rawExtraField,
6642
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6643
- headerArray,
6644
- headerView
6645
- });
6646
- fileEntries.set(entry.filename, entry);
6647
- });
6648
- } catch (error) {
6649
- appendedFilenames.forEach(filename => filenames.delete(filename));
6650
- throw error;
6651
- } finally {
6652
- resolveAppendZip();
6653
- pendingAddFileCalls.delete(promiseAppendZip);
6654
- if (releaseLockWriter) {
6655
- releaseLockWriter();
6656
- }
6657
- }
6684
+ appendZip(reader, options = {}) {
6685
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6658
6686
  }
6659
6687
 
6660
6688
  add(name = "", reader, options = {}) {
@@ -6816,11 +6844,140 @@
6816
6844
  return watchedPromise;
6817
6845
  }
6818
6846
 
6847
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6848
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6849
+ const filter = checkFunctionOption(options.filter);
6850
+ while (pendingAddFileCalls.size) {
6851
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6852
+ }
6853
+ let resolveAppendZip;
6854
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6855
+ pendingAddFileCalls.add(promiseAppendZip);
6856
+ const appendedFilenames = [];
6857
+ let releaseLockWriter;
6858
+ try {
6859
+ reader = new GenericReader(reader);
6860
+ await initStream(reader);
6861
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6862
+ reader = new BlobReader(await streamToBlob(reader.readable));
6863
+ await initStream(reader);
6864
+ }
6865
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6866
+ const zipReader$1 = new ZipReader(reader);
6867
+ const entries = await zipReader$1.getEntries();
6868
+ const keptEntries = [];
6869
+ for (const entry of entries) {
6870
+ if (!filter || await filter(entry)) {
6871
+ keptEntries.push(entry);
6872
+ }
6873
+ }
6874
+ await zipReader$1.close();
6875
+ await initStream(zipWriter.writer);
6876
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6877
+ keptEntries.forEach(entry => {
6878
+ const { filename } = entry;
6879
+ if (entriesMissingZip64ExtraField.has(entry)) {
6880
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6881
+ }
6882
+ if (filenames.has(filename)) {
6883
+ throw new Error(ERR_DUPLICATED_NAME);
6884
+ }
6885
+ filenames.add(filename);
6886
+ appendedFilenames.push(filename);
6887
+ });
6888
+ zipWriter.writerLocked = true;
6889
+ const { lockWriter } = zipWriter;
6890
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6891
+ zipWriter.writerLocked = false;
6892
+ resolve();
6893
+ });
6894
+ await lockWriter;
6895
+ if (zipWriter.addSplitZipSignature) {
6896
+ delete zipWriter.addSplitZipSignature;
6897
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6898
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6899
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6900
+ }
6901
+ }
6902
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6903
+ keptEntries.forEach(entry => {
6904
+ const {
6905
+ version,
6906
+ rawLastModDate,
6907
+ rawFilename,
6908
+ rawBitFlag,
6909
+ uncompressedSize,
6910
+ compressedSize,
6911
+ extraFieldZip64
6912
+ } = entry;
6913
+ let {
6914
+ compressionMethod,
6915
+ rawExtraField,
6916
+ } = entry;
6917
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6918
+ if (entry.extraFieldAES) {
6919
+ compressionMethod = COMPRESSION_METHOD_AES;
6920
+ }
6921
+ const extraFieldLength = getLength(rawExtraField);
6922
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6923
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6924
+ const {
6925
+ headerArray,
6926
+ headerView
6927
+ } = getHeaderArrayData({
6928
+ version,
6929
+ bitFlag: rawBitFlag,
6930
+ compressionMethod,
6931
+ uncompressedSize,
6932
+ compressedSize,
6933
+ rawLastModDate,
6934
+ rawFilename,
6935
+ zip64CompressedSize,
6936
+ zip64UncompressedSize,
6937
+ extraFieldLength
6938
+ });
6939
+ const { crc32 } = entry;
6940
+ if (crc32 !== UNDEFINED_VALUE) {
6941
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6942
+ }
6943
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6944
+ Object.assign(entry, {
6945
+ zip64Enabled: true,
6946
+ zip64UncompressedSize,
6947
+ zip64CompressedSize,
6948
+ offset,
6949
+ diskNumberStart,
6950
+ zip64DiskNumberStart: false,
6951
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6952
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6953
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6954
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6955
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6956
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6957
+ rawExtraField,
6958
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6959
+ headerArray,
6960
+ headerView
6961
+ });
6962
+ fileEntries.set(entry.filename, entry);
6963
+ });
6964
+ } catch (error) {
6965
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6966
+ throw error;
6967
+ } finally {
6968
+ resolveAppendZip();
6969
+ pendingAddFileCalls.delete(promiseAppendZip);
6970
+ if (releaseLockWriter) {
6971
+ releaseLockWriter();
6972
+ }
6973
+ }
6974
+ }
6975
+
6819
6976
  async function prependZipEntries(zipWriter, reader) {
6820
6977
  if (zipWriter.filenames.size) {
6821
6978
  throw new Error(ERR_ZIP_NOT_EMPTY);
6822
6979
  }
6823
- await zipWriter.appendZipEntries(reader);
6980
+ await appendZipEntries(zipWriter, reader);
6824
6981
  }
6825
6982
 
6826
6983
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8494,26 +8651,63 @@
8494
8651
  return rawExtraField;
8495
8652
  }
8496
8653
 
8497
- async function copyZipData(zipWriter, reader, entries, directoryOffset) {
8654
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8498
8655
  const { writer } = zipWriter;
8499
8656
  const entryPositions = new Map();
8500
- if (writer.closeDisk) {
8657
+ if (writer.closeDisk || compact) {
8501
8658
  const sortedEntries = Array.from(entries).sort((firstEntry, secondEntry) =>
8502
8659
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8503
- let copiedLength = 0;
8504
- for (const entry of sortedEntries) {
8505
- const sourceOffset = getSourceOffset(reader, entry);
8506
- await copyData(zipWriter, reader, copiedLength, sourceOffset - copiedLength);
8507
- if (exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8508
- await writer.closeDisk();
8509
- }
8510
- entryPositions.set(entry, {
8511
- offset: getSegmentOffset(zipWriter, writer),
8512
- diskNumberStart: getDiskNumber(writer)
8513
- });
8514
- copiedLength = sourceOffset;
8660
+ const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8661
+ const keptEntrySet = new Set(keptEntries);
8662
+ const localHeaderLengths = new Map();
8663
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8664
+ const entry = sortedEntries[indexEntry];
8665
+ if (keptEntrySet.has(entry)) {
8666
+ const sourceOffset = getSourceOffset(reader, entry);
8667
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8668
+ if (localHeaderLength === UNDEFINED_VALUE) {
8669
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8670
+ }
8671
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8672
+ if (entry.bitFlag.dataDescriptor) {
8673
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8674
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8675
+ }
8676
+ if (entryEnd > regionEnds[indexEntry]) {
8677
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8678
+ }
8679
+ regionEnds[indexEntry] = entryEnd;
8680
+ localHeaderLengths.set(entry, localHeaderLength);
8681
+ }
8682
+ }
8683
+ let copiedOffset = 0;
8684
+ let lastPosition;
8685
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8686
+ const entry = sortedEntries[indexEntry];
8687
+ if (keptEntrySet.has(entry)) {
8688
+ const sourceOffset = getSourceOffset(reader, entry);
8689
+ if (sourceOffset < copiedOffset) {
8690
+ entryPositions.set(entry, lastPosition);
8691
+ } else {
8692
+ if (!compact) {
8693
+ await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8694
+ }
8695
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8696
+ await writer.closeDisk();
8697
+ }
8698
+ lastPosition = {
8699
+ offset: getSegmentOffset(zipWriter, writer),
8700
+ diskNumberStart: getDiskNumber(writer)
8701
+ };
8702
+ entryPositions.set(entry, lastPosition);
8703
+ await copyData(zipWriter, reader, sourceOffset, regionEnds[indexEntry] - sourceOffset);
8704
+ copiedOffset = regionEnds[indexEntry];
8705
+ }
8706
+ }
8707
+ }
8708
+ if (!compact) {
8709
+ await copyData(zipWriter, reader, copiedOffset, directoryOffset - copiedOffset);
8515
8710
  }
8516
- await copyData(zipWriter, reader, copiedLength, directoryOffset - copiedLength);
8517
8711
  } else {
8518
8712
  const baseOffset = zipWriter.offset;
8519
8713
  await copyData(zipWriter, reader, 0, directoryOffset);
@@ -8525,6 +8719,20 @@
8525
8719
  return entryPositions;
8526
8720
  }
8527
8721
 
8722
+ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8723
+ const regionEnds = new Array(sortedEntries.length);
8724
+ let regionEnd = directoryOffset;
8725
+ for (let indexEntry = sortedEntries.length - 1; indexEntry >= 0; indexEntry--) {
8726
+ const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8727
+ const nextEntry = sortedEntries[indexEntry + 1];
8728
+ if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8729
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8730
+ }
8731
+ regionEnds[indexEntry] = regionEnd;
8732
+ }
8733
+ return regionEnds;
8734
+ }
8735
+
8528
8736
  async function copyData(zipWriter, reader, offset, size) {
8529
8737
  if (size > 0) {
8530
8738
  const { writer } = zipWriter;
@@ -8532,11 +8740,13 @@
8532
8740
  try {
8533
8741
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8534
8742
  } catch (error) {
8535
- zipWriter.hasCorruptedEntries = true;
8536
- try {
8537
- error.corruptedEntry = true;
8538
- } catch {
8539
- // ignored
8743
+ if (copiedLength) {
8744
+ zipWriter.hasCorruptedEntries = true;
8745
+ try {
8746
+ error.corruptedEntry = true;
8747
+ } catch {
8748
+ // ignored
8749
+ }
8540
8750
  }
8541
8751
  throw error;
8542
8752
  } finally {
@@ -8547,11 +8757,16 @@
8547
8757
  }
8548
8758
 
8549
8759
  async function getLocalHeaderLength(reader, offset) {
8550
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8760
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8761
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8762
+ EMPTY_UINT8_ARRAY;
8551
8763
  if (getLength(headerArray) < HEADER_SIZE) {
8552
- return HEADER_SIZE;
8764
+ return UNDEFINED_VALUE;
8553
8765
  }
8554
8766
  const headerView = getDataView(headerArray);
8767
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8768
+ return UNDEFINED_VALUE;
8769
+ }
8555
8770
  return HEADER_SIZE +
8556
8771
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8557
8772
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -8902,7 +9117,7 @@
8902
9117
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8903
9118
  */
8904
9119
 
8905
- const VERSION = "2.19.0";
9120
+ const VERSION = "2.21.0";
8906
9121
 
8907
9122
  /*
8908
9123
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -9520,11 +9735,13 @@
9520
9735
  exports.WARNING_COMPRESSION_UNAVAILABLE = WARNING_COMPRESSION_UNAVAILABLE;
9521
9736
  exports.WARNING_DUPLICATE_FILENAME = WARNING_DUPLICATE_FILENAME;
9522
9737
  exports.WARNING_MALFORMED_EXTRA_FIELD = WARNING_MALFORMED_EXTRA_FIELD;
9738
+ exports.WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET;
9523
9739
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG;
9524
9740
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD;
9525
9741
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES;
9526
9742
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME;
9527
9743
  exports.WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY;
9744
+ exports.WARNING_MISSING_ZIP64_EXTRA_FIELD = WARNING_MISSING_ZIP64_EXTRA_FIELD;
9528
9745
  exports.WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY;
9529
9746
  exports.WARNING_PREPENDED_CENTRAL_DIRECTORY = WARNING_PREPENDED_CENTRAL_DIRECTORY;
9530
9747
  exports.WARNING_PREPENDED_DATA = WARNING_PREPENDED_DATA;