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