@zip.js/zip.js 2.8.57 → 2.8.59

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
@@ -83,6 +83,7 @@
83
83
  const BITFLAG_LEVEL_FAST_MASK = 0b100;
84
84
  const BITFLAG_LEVEL_SUPER_FAST_MASK = 0b110;
85
85
  const BITFLAG_DATA_DESCRIPTOR = 0b1000;
86
+ const BITFLAG_COMPRESSED_PATCHED_DATA = 0b100000;
86
87
  const BITFLAG_STRONG_ENCRYPTION = 0b1000000;
87
88
  const BITFLAG_LANG_ENCODING_FLAG = 0b100000000000;
88
89
  const BITFLAG_MASKED_LOCAL_HEADERS = 0b10000000000000;
@@ -101,6 +102,7 @@
101
102
  const FILE_ATTR_UNIX_SETGID_MASK = 0o2000;
102
103
  const FILE_ATTR_UNIX_STICKY_MASK = 0o1000;
103
104
 
105
+ const VERSION_STORE = 0x0A;
104
106
  const VERSION_DEFLATE = 0x14;
105
107
  const VERSION_ZIP64 = 0x2D;
106
108
  const VERSION_AES = 0x33;
@@ -199,6 +201,7 @@
199
201
  const OPTION_USDZ = "usdz";
200
202
  const OPTION_UNIX_EXTRA_FIELD_TYPE = "unixExtraFieldType";
201
203
  const OPTION_LOCAL_EXTRA_FIELD = "localExtraField";
204
+ const OPTION_CENTRAL_EXTRA_FIELD = "centralExtraField";
202
205
  const OPTION_STRICTNESS = "strictness";
203
206
  const OPTION_FILENAME_VALIDATION = "filenameValidation";
204
207
  const OPTION_NORMALIZE_FILENAME = "normalizeFilename";
@@ -4708,6 +4711,21 @@
4708
4711
  const ERR_INVALID_FILENAME_VALIDATION = "Invalid filenameValidation (must be 'strict', 'balanced' or 'tolerant')";
4709
4712
  const ERR_INVALID_MAX_APPENDED_DATA_SIZE = "Invalid maxAppendedDataSize (must be a number greater than or equal to 0)";
4710
4713
  const ERR_UNSUPPORTED_UINT64 = "64-bit value exceeds Number.MAX_SAFE_INTEGER";
4714
+ const WARNING_UNSORTED_CENTRAL_DIRECTORY = "unsorted central directory";
4715
+ const WARNING_UNKNOWN_VERSION = "unknown version needed to extract";
4716
+ const WARNING_COMPRESSED_PATCHED_DATA = "compressed patched data";
4717
+ const WARNING_MALFORMED_EXTRA_FIELD = "malformed extra field";
4718
+ const WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA = "unknown zip64 extensible data";
4719
+ const WARNING_WRAPPED_ENTRIES_COUNT = "wrapped entries count";
4720
+ const WARNING_APPENDED_DATA = "appended data";
4721
+ const WARNING_PREPENDED_DATA = "prepended data";
4722
+ const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
4723
+ const WARNING_DUPLICATE_FILENAME = "duplicate filename";
4724
+ const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
4725
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
4726
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
4727
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
4728
+ const MAX_KNOWN_VERSION = 63;
4711
4729
  const DRIVE_LETTER_REGEXP = /^[a-zA-Z]:/;
4712
4730
  const CHARSET_UTF8 = "utf-8";
4713
4731
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
@@ -4758,6 +4776,7 @@
4758
4776
  if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
4759
4777
  throw new Error(ERR_BAD_FORMAT);
4760
4778
  }
4779
+ const warnings = zipReader.warnings = [];
4761
4780
  const strictness = getStrictness(options, zipReader.options);
4762
4781
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
4763
4782
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
@@ -4781,8 +4800,12 @@
4781
4800
  const commentOffset = endOfDirectoryInfo.offset;
4782
4801
  const commentLength = getUint16$1(endOfDirectoryView, 20);
4783
4802
  const appendedDataOffset = commentOffset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
4784
- if (reader.size - appendedDataOffset > maxAppendedDataSize) {
4785
- throwAmbiguousArchive("appended data");
4803
+ const appendedDataLength = reader.size - appendedDataOffset;
4804
+ if (appendedDataLength > maxAppendedDataSize) {
4805
+ throwAmbiguousArchive(WARNING_APPENDED_DATA);
4806
+ }
4807
+ if (appendedDataLength > 0) {
4808
+ addWarning(warnings, WARNING_APPENDED_DATA);
4786
4809
  }
4787
4810
  let lastDiskNumber = getUint16$1(endOfDirectoryView, 4);
4788
4811
  const expectedLastDiskNumber = reader.lastDiskNumber || 0;
@@ -4836,23 +4859,23 @@
4836
4859
  }
4837
4860
  if (lastDiskNumber == MAX_16_BITS) {
4838
4861
  lastDiskNumber = getUint32$1(endOfDirectoryView, 16);
4839
- } else if (checkAmbiguity && lastDiskNumber != getUint32$1(endOfDirectoryView, 16)) {
4840
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
4862
+ } else if (lastDiskNumber != getUint32$1(endOfDirectoryView, 16)) {
4863
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4841
4864
  }
4842
4865
  if (diskNumber == MAX_16_BITS) {
4843
4866
  diskNumber = getUint32$1(endOfDirectoryView, 20);
4844
- } else if (checkAmbiguity && diskNumber != getUint32$1(endOfDirectoryView, 20)) {
4845
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
4867
+ } else if (diskNumber != getUint32$1(endOfDirectoryView, 20)) {
4868
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4846
4869
  }
4847
4870
  if (filesLength == MAX_16_BITS) {
4848
4871
  filesLength = getBigUint64(endOfDirectoryView, 32);
4849
- } else if (checkAmbiguity && filesLength != getBigUint64(endOfDirectoryView, 32)) {
4850
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
4872
+ } else if (filesLength != getBigUint64(endOfDirectoryView, 32)) {
4873
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4851
4874
  }
4852
4875
  if (directoryDataLength == MAX_32_BITS) {
4853
4876
  directoryDataLength = getBigUint64(endOfDirectoryView, 40);
4854
- } else if (checkAmbiguity && directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
4855
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
4877
+ } else if (directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
4878
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4856
4879
  }
4857
4880
  directoryDataOffset = getDiskOffset$1(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
4858
4881
  }
@@ -4921,11 +4944,23 @@
4921
4944
  declaredDirectoryDataLength = directoryArray.length;
4922
4945
  decryptedDirectory = true;
4923
4946
  }
4947
+ if (directoryEncryptionInfo && !decryptedDirectory &&
4948
+ (directoryArray.length < 4 || getUint32$1(directoryView, 0) == CENTRAL_FILE_HEADER_SIGNATURE)) {
4949
+ addWarning(warnings, WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA);
4950
+ }
4924
4951
  startOffset = directoryDataOffset;
4925
4952
  const filenameEncoding = getOptionValue$1(zipReader, options, OPTION_FILENAME_ENCODING);
4926
4953
  const commentEncoding = getOptionValue$1(zipReader, options, OPTION_COMMENT_ENCODING);
4927
- const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
4954
+ const filenames = new Set();
4928
4955
  let duplicateFilename;
4956
+ let previousEntryPosition = -1;
4957
+ const recoverWrappedFilesLength = !checkAmbiguity && !zip64EndOfDirectory;
4958
+ if (!filesLength && recoverWrappedFilesLength) {
4959
+ filesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
4960
+ if (filesLength) {
4961
+ addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
4962
+ }
4963
+ }
4929
4964
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
4930
4965
  const fileEntry = new ZipEntry(reader, zipReader.options);
4931
4966
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
@@ -5003,15 +5038,26 @@
5003
5038
  filename,
5004
5039
  comment
5005
5040
  });
5006
- readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5041
+ if (readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6)) {
5042
+ addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5043
+ }
5007
5044
  fileEntry.offset += prependedDataLength;
5008
- startOffset = Math.min(getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset, startOffset);
5009
- if (checkAmbiguity) {
5010
- if (filenames.has(fileEntry.filename)) {
5011
- duplicateFilename = true;
5012
- }
5013
- filenames.add(fileEntry.filename);
5045
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5046
+ startOffset = Math.min(entryPosition, startOffset);
5047
+ if (entryPosition < previousEntryPosition) {
5048
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5049
+ }
5050
+ previousEntryPosition = entryPosition;
5051
+ if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5052
+ addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5053
+ }
5054
+ if ((fileEntry.rawBitFlag & BITFLAG_COMPRESSED_PATCHED_DATA) == BITFLAG_COMPRESSED_PATCHED_DATA) {
5055
+ addWarning(warnings, WARNING_COMPRESSED_PATCHED_DATA, filename);
5014
5056
  }
5057
+ if (filenames.has(fileEntry.filename)) {
5058
+ duplicateFilename = true;
5059
+ }
5060
+ filenames.add(fileEntry.filename);
5015
5061
  const unixExternalUpper = (fileEntry.externalFileAttributes >> 16) & MAX_16_BITS;
5016
5062
  if (fileEntry.unixMode === UNDEFINED_VALUE && (unixExternalUpper & (FILE_ATTR_UNIX_DEFAULT_MASK | FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_TYPE_DIR)) != 0) {
5017
5063
  fileEntry.unixMode = unixExternalUpper;
@@ -5035,7 +5081,7 @@
5035
5081
  internalFileAttribute: fileEntry.internalFileAttributes,
5036
5082
  externalFileAttribute: fileEntry.externalFileAttributes,
5037
5083
  executable,
5038
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || (fileEntry.filename.endsWith(DIRECTORY_SIGNATURE) && !fileEntry.uncompressedSize),
5084
+ directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
5039
5085
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5040
5086
  });
5041
5087
  const entry = new Entry(fileEntry);
@@ -5048,6 +5094,13 @@
5048
5094
  return arrayBufferPromise;
5049
5095
  };
5050
5096
  offset = endOffset;
5097
+ if (indexFile == filesLength - 1 && recoverWrappedFilesLength) {
5098
+ const wrappedFilesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
5099
+ if (wrappedFilesLength) {
5100
+ filesLength += wrappedFilesLength;
5101
+ addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5102
+ }
5103
+ }
5051
5104
  const { onprogress } = options;
5052
5105
  if (onprogress) {
5053
5106
  try {
@@ -5072,18 +5125,22 @@
5072
5125
  zipReader.digitalSignature = digitalSignature;
5073
5126
  offsetAfterSignature = offset + 6 + digitalSignature.length;
5074
5127
  }
5075
- if (checkAmbiguity && offset != declaredDirectoryDataLength && offsetAfterSignature != declaredDirectoryDataLength) {
5076
- throwAmbiguousArchive("trailing central directory data");
5128
+ if ((offset != declaredDirectoryDataLength && offsetAfterSignature != declaredDirectoryDataLength) ||
5129
+ (!decryptedDirectory && offset != directoryDataLength && offsetAfterSignature != directoryDataLength)) {
5130
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_TRAILING_CENTRAL_DIRECTORY_DATA);
5077
5131
  }
5078
5132
  if (duplicateFilename) {
5079
- throwAmbiguousArchive("duplicate filename");
5133
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_DUPLICATE_FILENAME);
5080
5134
  }
5081
5135
  const extractPrependedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
5082
5136
  const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
5083
5137
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
5084
5138
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
5085
5139
  if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
5086
- throwAmbiguousArchive("prepended data");
5140
+ throwAmbiguousArchive(WARNING_PREPENDED_DATA);
5141
+ }
5142
+ if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5143
+ addWarning(warnings, WARNING_PREPENDED_DATA);
5087
5144
  }
5088
5145
  if (extractPrependedData) {
5089
5146
  zipReader.prependedData = startOffset > splitZipSignatureLength ?
@@ -5153,6 +5210,28 @@
5153
5210
  }
5154
5211
  }
5155
5212
 
5213
+ async function isZipFile(reader, options = {}) {
5214
+ reader = new GenericReader(reader);
5215
+ await initStream(reader);
5216
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
5217
+ reader = new BlobReader(await streamToBlob(reader.readable));
5218
+ await initStream(reader);
5219
+ }
5220
+ if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
5221
+ return false;
5222
+ }
5223
+ const strictness = getStrictness(options, {});
5224
+ const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
5225
+ const maxAppendedDataSize = getMaxAppendedDataSize(options[OPTION_MAX_APPENDED_DATA_SIZE], strictness);
5226
+ const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
5227
+ if (!endOfDirectoryInfo || (strictness == STRICTNESS_STRICT && endOfDirectoryReachingEndCount > 1)) {
5228
+ return false;
5229
+ }
5230
+ const commentLength = getUint16$1(getDataView(endOfDirectoryInfo), 20);
5231
+ const appendedDataOffset = endOfDirectoryInfo.offset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
5232
+ return reader.size - appendedDataOffset <= maxAppendedDataSize;
5233
+ }
5234
+
5156
5235
  class ZipEntry {
5157
5236
 
5158
5237
  constructor(reader, options) {
@@ -5184,6 +5263,7 @@
5184
5263
  dataDescriptor
5185
5264
  } = bitFlag;
5186
5265
  const localDirectory = fileEntry.localDirectory = {};
5266
+ const warnings = fileEntry.warnings = [];
5187
5267
  const localHeaderOffset = getDiskOffset$1(reader, diskNumberStart) + offset;
5188
5268
  const dataArray = await readUint8Array(reader, localHeaderOffset, HEADER_SIZE);
5189
5269
  const dataView = getDataView(dataArray);
@@ -5206,6 +5286,7 @@
5206
5286
  extraFieldLength,
5207
5287
  filenameLength
5208
5288
  } = localDirectory;
5289
+ const dataOffset = localDirectory.dataOffset = localHeaderOffset + HEADER_SIZE + filenameLength + extraFieldLength;
5209
5290
  const checkLocalDirectoryOption = getOptionValue$1(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
5210
5291
  const entryStrictness = getStrictness(options, zipEntry.options);
5211
5292
  const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
@@ -5223,10 +5304,10 @@
5223
5304
  if (checkLocalFilename) {
5224
5305
  localDirectory.rawFilename = rawLocalFilename;
5225
5306
  }
5226
- readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
5227
- if (checkLocalDirectory) {
5228
- validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename);
5307
+ if (readCommonFooter(zipEntry, localDirectory, dataView, 4, true)) {
5308
+ addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD);
5229
5309
  }
5310
+ validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5230
5311
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5231
5312
  if (lastAccessDate) {
5232
5313
  fileEntry.lastAccessDate = lastAccessDate;
@@ -5259,7 +5340,6 @@
5259
5340
  throw new Error(ERR_ENCRYPTED);
5260
5341
  }
5261
5342
  }
5262
- const dataOffset = localHeaderOffset + HEADER_SIZE + filenameLength + extraFieldLength;
5263
5343
  if (dataOffset + compressedSize > reader.size) {
5264
5344
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5265
5345
  }
@@ -5379,6 +5459,16 @@
5379
5459
  return false;
5380
5460
  }
5381
5461
 
5462
+ function getWrappedFilesLength(directoryView, directoryArray, offset) {
5463
+ let wrappedFilesLength = 0;
5464
+ while (offset + CENTRAL_FILE_HEADER_LENGTH <= directoryArray.length && getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE) {
5465
+ offset += CENTRAL_FILE_HEADER_LENGTH +
5466
+ getUint16$1(directoryView, offset + 28) + getUint16$1(directoryView, offset + 30) + getUint16$1(directoryView, offset + 32);
5467
+ wrappedFilesLength++;
5468
+ }
5469
+ return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
5470
+ }
5471
+
5382
5472
  function readDigitalSignature(signatureRecordArray) {
5383
5473
  if (signatureRecordArray.length >= 6) {
5384
5474
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5443,6 +5533,7 @@
5443
5533
  const extraField = directory.extraField = new Map();
5444
5534
  const rawExtraFieldView = getDataView(rawExtraField);
5445
5535
  let offsetExtraField = 0;
5536
+ let malformedExtraField = false;
5446
5537
  try {
5447
5538
  while (offsetExtraField < rawExtraField.length) {
5448
5539
  const type = getUint16$1(rawExtraFieldView, offsetExtraField);
@@ -5454,7 +5545,10 @@
5454
5545
  offsetExtraField += 4 + size;
5455
5546
  }
5456
5547
  } catch {
5457
- // ignored
5548
+ malformedExtraField = true;
5549
+ }
5550
+ if (offsetExtraField > rawExtraField.length) {
5551
+ malformedExtraField = true;
5458
5552
  }
5459
5553
  const compressionMethod = getUint16$1(dataView, offset + 4);
5460
5554
  Object.assign(directory, {
@@ -5522,6 +5616,7 @@
5522
5616
  if (extraFieldUSDZ) {
5523
5617
  directory.extraFieldUSDZ = extraFieldUSDZ;
5524
5618
  }
5619
+ return malformedExtraField;
5525
5620
  }
5526
5621
 
5527
5622
  function readExtraFieldZip64(extraFieldZip64, directory) {
@@ -5551,10 +5646,11 @@
5551
5646
  const computedCrc32View = getDataView(new Uint8Array(4));
5552
5647
  computedCrc32View.setUint32(0, computedCrc32.get(), true);
5553
5648
  const nameCrc32 = getUint32$1(extraFieldView, 1);
5649
+ const version = getUint8(extraFieldView, 0);
5554
5650
  Object.assign(extraFieldUnicode, {
5555
- version: getUint8(extraFieldView, 0),
5651
+ version,
5556
5652
  [propertyName]: decodeText(extraFieldUnicode.data.subarray(5)),
5557
- valid: !fileEntry.bitFlag.languageEncodingFlag && nameCrc32 == getUint32$1(computedCrc32View, 0)
5653
+ valid: version == 1 && !fileEntry.bitFlag.languageEncodingFlag && nameCrc32 == getUint32$1(computedCrc32View, 0)
5558
5654
  });
5559
5655
  if (extraFieldUnicode.valid) {
5560
5656
  directory[propertyName] = extraFieldUnicode[propertyName];
@@ -5982,27 +6078,46 @@
5982
6078
  return UNDEFINED_VALUE;
5983
6079
  }
5984
6080
 
5985
- function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename) {
6081
+ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, warnings) {
5986
6082
  const { rawFilename } = zipEntry;
6083
+ const reject = !warnings;
5987
6084
  const maskedLocalDirectory = zipEntry.decryptedDirectory &&
5988
6085
  (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
5989
6086
  if (checkLocalFilename && !maskedLocalDirectory &&
5990
6087
  (rawLocalFilename.length != rawFilename.length ||
5991
6088
  rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
5992
- throwAmbiguousArchive("mismatched local file header (filename)");
6089
+ reportAmbiguity(reject, warnings, "mismatched local file header (filename)");
5993
6090
  }
5994
6091
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
5995
- throwAmbiguousArchive("mismatched local file header (general purpose bit flag)");
6092
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
5996
6093
  }
5997
6094
  if (localDirectory.compressionMethod != zipEntry.compressionMethod) {
5998
- throwAmbiguousArchive("mismatched local file header (compression method)");
6095
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD);
5999
6096
  }
6000
6097
  if (!localDirectory.bitFlag.dataDescriptor && !maskedLocalDirectory &&
6001
6098
  (localDirectory.crc32 || localDirectory.compressedSize || localDirectory.uncompressedSize) &&
6002
6099
  (localDirectory.crc32 != zipEntry.crc32 ||
6003
6100
  localDirectory.compressedSize != zipEntry.compressedSize ||
6004
6101
  localDirectory.uncompressedSize != zipEntry.uncompressedSize)) {
6005
- throwAmbiguousArchive("mismatched local file header (crc32 or sizes)");
6102
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6103
+ }
6104
+ }
6105
+
6106
+ function reportAmbiguity(reject, warnings, reason) {
6107
+ if (reject) {
6108
+ throwAmbiguousArchive(reason);
6109
+ } else {
6110
+ addWarning(warnings, reason);
6111
+ }
6112
+ }
6113
+
6114
+ function addWarning(warnings, reason, filename) {
6115
+ if (!warnings.some(warning => warning.reason == reason)) {
6116
+ const warning = { reason };
6117
+ if (filename !== UNDEFINED_VALUE) {
6118
+ warning.filename = filename;
6119
+ }
6120
+ warnings.push(warning);
6006
6121
  }
6007
6122
  }
6008
6123
 
@@ -6023,7 +6138,8 @@
6023
6138
 
6024
6139
  function getDate(timeRaw) {
6025
6140
  const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
6026
- return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
6141
+ const result = new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
6142
+ return result < MIN_DATE ? MIN_DATE : result;
6027
6143
  }
6028
6144
 
6029
6145
  function getDateNTFS(timeRaw) {
@@ -6078,8 +6194,23 @@
6078
6194
  ERR_UNSUPPORTED_ENCRYPTION: ERR_UNSUPPORTED_ENCRYPTION,
6079
6195
  ERR_UNSUPPORTED_UINT64: ERR_UNSUPPORTED_UINT64,
6080
6196
  ERR_WORKER_STARTUP_TIMEOUT: ERR_WORKER_STARTUP_TIMEOUT,
6197
+ WARNING_APPENDED_DATA: WARNING_APPENDED_DATA,
6198
+ WARNING_COMPRESSED_PATCHED_DATA: WARNING_COMPRESSED_PATCHED_DATA,
6199
+ WARNING_DUPLICATE_FILENAME: WARNING_DUPLICATE_FILENAME,
6200
+ WARNING_MALFORMED_EXTRA_FIELD: WARNING_MALFORMED_EXTRA_FIELD,
6201
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG: WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
6202
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD: WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
6203
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES: WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES,
6204
+ WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY: WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
6205
+ WARNING_PREPENDED_DATA: WARNING_PREPENDED_DATA,
6206
+ WARNING_TRAILING_CENTRAL_DIRECTORY_DATA: WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
6207
+ WARNING_UNKNOWN_VERSION: WARNING_UNKNOWN_VERSION,
6208
+ WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA: WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA,
6209
+ WARNING_UNSORTED_CENTRAL_DIRECTORY: WARNING_UNSORTED_CENTRAL_DIRECTORY,
6210
+ WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6081
6211
  ZipReader: ZipReader,
6082
- ZipReaderStream: ZipReaderStream
6212
+ ZipReaderStream: ZipReaderStream,
6213
+ isZipFile: isZipFile
6083
6214
  });
6084
6215
 
6085
6216
  /*
@@ -6176,153 +6307,151 @@
6176
6307
  offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
6177
6308
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
6178
6309
  pendingAddFileCalls: new Set(),
6310
+ pendingErrors: [],
6179
6311
  bufferedWrites: 0,
6180
6312
  lastFileEntry: UNDEFINED_VALUE
6181
6313
  });
6182
6314
  }
6183
6315
 
6184
- async prependZip(reader) {
6185
- if (this.filenames.size) {
6186
- throw new Error(ERR_ZIP_NOT_EMPTY);
6187
- }
6188
- reader = new GenericReader(reader);
6189
- await initStream(reader);
6190
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6191
- reader = new BlobReader(await streamToBlob(reader.readable));
6192
- await initStream(reader);
6193
- }
6194
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6195
- const zipReader$1 = new ZipReader(reader);
6196
- const entries = await zipReader$1.getEntries();
6197
- await zipReader$1.close();
6198
- await initStream(this.writer);
6199
- const { directoryOffset } = zipReader$1;
6200
- if (this.addSplitZipSignature) {
6201
- delete this.addSplitZipSignature;
6202
- if (!await startsWithSplitZipSignature(reader)) {
6203
- await writeData(this.writer, getSplitZipSignatureArray());
6204
- this.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6205
- }
6206
- }
6207
- const entryPositions = await copyZipData(this, reader, entries, directoryOffset);
6208
- this.filenames = new Set(entries.map(entry => entry.filename));
6209
- this.fileEntries = new Map(entries.map(entry => {
6210
- const {
6211
- version,
6212
- rawLastModDate,
6213
- lastAccessDate,
6214
- creationDate,
6215
- rawFilename,
6216
- bitFlag,
6217
- encrypted,
6218
- uncompressedSize,
6219
- compressedSize,
6220
- extraFieldZip64
6221
- } = entry;
6222
- let {
6223
- compressionMethod,
6224
- rawExtraFieldZip64,
6225
- rawExtraFieldAES,
6226
- rawExtraFieldExtendedTimestamp,
6227
- rawExtraFieldNTFS,
6228
- rawExtraFieldUnix,
6229
- rawExtraField,
6230
- } = entry;
6231
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6232
- rawExtraFieldZip64 = rawExtraFieldZip64 || EMPTY_UINT8_ARRAY;
6233
- rawExtraFieldAES = rawExtraFieldAES || EMPTY_UINT8_ARRAY;
6234
- rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || EMPTY_UINT8_ARRAY;
6235
- rawExtraFieldNTFS = rawExtraFieldNTFS || EMPTY_UINT8_ARRAY;
6236
- rawExtraFieldUnix = rawExtraFieldUnix || EMPTY_UINT8_ARRAY;
6237
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6238
- if (entry.extraFieldAES) {
6239
- compressionMethod = COMPRESSION_METHOD_AES;
6240
- }
6241
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
6242
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6243
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6244
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6245
- const {
6246
- headerArray,
6247
- headerView
6248
- } = getHeaderArrayData({
6249
- version,
6250
- bitFlag: bitFlagValue,
6251
- compressionMethod,
6252
- uncompressedSize,
6253
- compressedSize,
6254
- rawLastModDate,
6255
- rawFilename,
6256
- zip64CompressedSize,
6257
- zip64UncompressedSize,
6258
- extraFieldLength
6259
- });
6260
- const { crc32 } = entry;
6261
- if (crc32 !== UNDEFINED_VALUE) {
6262
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6263
- }
6264
- const { offset, diskNumberStart } = entryPositions.get(entry);
6265
- Object.assign(entry, {
6266
- zip64UncompressedSize,
6267
- zip64CompressedSize,
6268
- offset,
6269
- diskNumberStart,
6270
- zip64DiskNumberStart: false,
6271
- rawExtraFieldZip64,
6272
- rawExtraFieldAES,
6273
- rawExtraFieldExtendedTimestamp,
6274
- rawExtraFieldNTFS,
6275
- rawExtraFieldUnix,
6276
- rawExtraField,
6277
- extendedTimestamp: rawExtraFieldExtendedTimestamp.length > 0 || rawExtraFieldNTFS.length > 0,
6278
- extraFieldExtendedTimestampFlag: 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0),
6279
- headerArray,
6280
- headerView
6281
- });
6282
- return [entry.filename, entry];
6283
- }));
6316
+ prependZip(reader) {
6317
+ return watchPromiseError(this, prependZipEntries(this, reader));
6318
+ }
6319
+
6320
+ appendZip(reader) {
6321
+ return watchPromiseError(this, this.appendZipEntries(reader));
6284
6322
  }
6285
6323
 
6286
- async add(name = "", reader, options = {}) {
6324
+ async appendZipEntries(reader) {
6287
6325
  const zipWriter = this;
6288
- options = Object.assign({}, options);
6289
- const { pendingAddFileCalls } = zipWriter;
6290
- if (workers < getConfiguration().maxWorkers) {
6291
- workers++;
6292
- } else {
6293
- await new Promise(resolve => pendingEntries.push(resolve));
6326
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6327
+ while (pendingAddFileCalls.size) {
6328
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6294
6329
  }
6295
- let promiseAddFile;
6296
- let nameAdded;
6330
+ let resolveAppendZip;
6331
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6332
+ pendingAddFileCalls.add(promiseAppendZip);
6333
+ const appendedFilenames = [];
6334
+ let releaseLockWriter;
6297
6335
  try {
6298
- name = name.trim();
6299
- if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
6300
- name += DIRECTORY_SIGNATURE;
6301
- }
6302
- if (zipWriter.filenames.has(name)) {
6303
- throw new Error(ERR_DUPLICATED_NAME);
6304
- }
6305
- zipWriter.filenames.add(name);
6306
- nameAdded = true;
6307
- promiseAddFile = addFile(zipWriter, name, reader, options);
6308
- pendingAddFileCalls.add(promiseAddFile);
6309
- return await promiseAddFile;
6310
- } catch (error) {
6311
- if (nameAdded) {
6312
- zipWriter.filenames.delete(name);
6336
+ reader = new GenericReader(reader);
6337
+ await initStream(reader);
6338
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6339
+ reader = new BlobReader(await streamToBlob(reader.readable));
6340
+ await initStream(reader);
6341
+ }
6342
+ const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6343
+ const zipReader$1 = new ZipReader(reader);
6344
+ const entries = await zipReader$1.getEntries();
6345
+ await zipReader$1.close();
6346
+ await initStream(zipWriter.writer);
6347
+ const { directoryOffset } = zipReader$1;
6348
+ entries.forEach(({ filename }) => {
6349
+ if (filenames.has(filename)) {
6350
+ throw new Error(ERR_DUPLICATED_NAME);
6351
+ }
6352
+ filenames.add(filename);
6353
+ appendedFilenames.push(filename);
6354
+ });
6355
+ zipWriter.writerLocked = true;
6356
+ const { lockWriter } = zipWriter;
6357
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6358
+ zipWriter.writerLocked = false;
6359
+ resolve();
6360
+ });
6361
+ await lockWriter;
6362
+ if (zipWriter.addSplitZipSignature) {
6363
+ delete zipWriter.addSplitZipSignature;
6364
+ if (!await startsWithSplitZipSignature(reader)) {
6365
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6366
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6367
+ }
6313
6368
  }
6369
+ const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
6370
+ entries.forEach(entry => {
6371
+ const {
6372
+ version,
6373
+ rawLastModDate,
6374
+ rawFilename,
6375
+ bitFlag,
6376
+ encrypted,
6377
+ uncompressedSize,
6378
+ compressedSize,
6379
+ extraFieldZip64
6380
+ } = entry;
6381
+ let {
6382
+ compressionMethod,
6383
+ rawExtraField,
6384
+ } = entry;
6385
+ const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6386
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6387
+ if (entry.extraFieldAES) {
6388
+ compressionMethod = COMPRESSION_METHOD_AES;
6389
+ }
6390
+ const extraFieldLength = getLength(rawExtraField);
6391
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6392
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6393
+ const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6394
+ const {
6395
+ headerArray,
6396
+ headerView
6397
+ } = getHeaderArrayData({
6398
+ version,
6399
+ bitFlag: bitFlagValue,
6400
+ compressionMethod,
6401
+ uncompressedSize,
6402
+ compressedSize,
6403
+ rawLastModDate,
6404
+ rawFilename,
6405
+ zip64CompressedSize,
6406
+ zip64UncompressedSize,
6407
+ extraFieldLength
6408
+ });
6409
+ const { crc32 } = entry;
6410
+ if (crc32 !== UNDEFINED_VALUE) {
6411
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6412
+ }
6413
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6414
+ Object.assign(entry, {
6415
+ zip64UncompressedSize,
6416
+ zip64CompressedSize,
6417
+ offset,
6418
+ diskNumberStart,
6419
+ zip64DiskNumberStart: false,
6420
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6421
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6422
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6423
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6424
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6425
+ rawExtraField,
6426
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6427
+ extendedTimestamp: false,
6428
+ headerArray,
6429
+ headerView
6430
+ });
6431
+ fileEntries.set(entry.filename, entry);
6432
+ });
6433
+ } catch (error) {
6434
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6314
6435
  throw error;
6315
6436
  } finally {
6316
- pendingAddFileCalls.delete(promiseAddFile);
6317
- const pendingEntry = pendingEntries.shift();
6318
- if (pendingEntry) {
6319
- pendingEntry();
6320
- } else {
6321
- workers--;
6437
+ resolveAppendZip();
6438
+ pendingAddFileCalls.delete(promiseAppendZip);
6439
+ if (releaseLockWriter) {
6440
+ releaseLockWriter();
6322
6441
  }
6323
6442
  }
6324
6443
  }
6325
6444
 
6445
+ add(name = "", reader, options = {}) {
6446
+ const zipWriter = this;
6447
+ const { pendingAddFileCalls } = zipWriter;
6448
+ const promiseAddFile = addFileEntry(zipWriter, name, reader, options);
6449
+ pendingAddFileCalls.add(promiseAddFile);
6450
+ const deletePendingAddFileCall = () => pendingAddFileCalls.delete(promiseAddFile);
6451
+ Promise.prototype.then.call(promiseAddFile, deletePendingAddFileCall, deletePendingAddFileCall);
6452
+ return watchPromiseError(zipWriter, promiseAddFile);
6453
+ }
6454
+
6326
6455
  remove(entry) {
6327
6456
  const { filenames, fileEntries } = this;
6328
6457
  // deno-lint-ignore valid-typeof
@@ -6353,6 +6482,19 @@
6353
6482
  while (pendingAddFileCalls.size) {
6354
6483
  await Promise.allSettled(Array.from(pendingAddFileCalls));
6355
6484
  }
6485
+ await Promise.allSettled(zipWriter.pendingErrors.map(watcher => watcher.recorded));
6486
+ const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.error && !watcher.observed);
6487
+ if (unobservedWatchers.length) {
6488
+ const unobservedErrors = unobservedWatchers.map(watcher => watcher.error);
6489
+ unobservedWatchers.forEach(watcher => watcher.observed = true);
6490
+ const [error] = unobservedErrors;
6491
+ try {
6492
+ error.entryErrors = unobservedErrors;
6493
+ } catch {
6494
+ // ignored
6495
+ }
6496
+ throw error;
6497
+ }
6356
6498
  await closeFile(zipWriter, comment, options);
6357
6499
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
6358
6500
  if (!preventClose) {
@@ -6387,11 +6529,7 @@
6387
6529
  try {
6388
6530
  await zipWriter.close();
6389
6531
  } catch (error) {
6390
- try {
6391
- await zipWriter.writer.writable.abort(error);
6392
- } catch {
6393
- // ignored
6394
- }
6532
+ await abortWritable(zipWriter, error);
6395
6533
  }
6396
6534
  }
6397
6535
  }
@@ -6408,8 +6546,89 @@
6408
6546
  }
6409
6547
 
6410
6548
  async close(comment = UNDEFINED_VALUE, options = {}) {
6411
- await Promise.all(Array.from(this.pendingAddFileCalls));
6412
- return this.zipWriter.close(comment, options);
6549
+ const { zipWriter } = this;
6550
+ const results = await Promise.allSettled(Array.from(this.pendingAddFileCalls));
6551
+ const entryErrors = results.filter(result => result.status == "rejected").map(result => result.reason);
6552
+ if (entryErrors.length) {
6553
+ const [error] = entryErrors;
6554
+ try {
6555
+ error.entryErrors = entryErrors;
6556
+ } catch {
6557
+ // ignored
6558
+ }
6559
+ await abortWritable(zipWriter, error);
6560
+ throw error;
6561
+ }
6562
+ try {
6563
+ return await zipWriter.close(comment, options);
6564
+ } catch (error) {
6565
+ await abortWritable(zipWriter, error);
6566
+ throw error;
6567
+ }
6568
+ }
6569
+ }
6570
+
6571
+ class WatchedPromise extends Promise {
6572
+
6573
+ then(onFulfilled, onRejected) {
6574
+ const { watcher } = this;
6575
+ if (watcher) {
6576
+ watcher.observed = true;
6577
+ }
6578
+ return super.then(onFulfilled, onRejected);
6579
+ }
6580
+ }
6581
+
6582
+ function watchPromiseError(zipWriter, promise) {
6583
+ const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
6584
+ const watcher = {};
6585
+ watchedPromise.watcher = watcher;
6586
+ watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE, error => watcher.error = error);
6587
+ zipWriter.pendingErrors.push(watcher);
6588
+ return watchedPromise;
6589
+ }
6590
+
6591
+ async function prependZipEntries(zipWriter, reader) {
6592
+ if (zipWriter.filenames.size) {
6593
+ throw new Error(ERR_ZIP_NOT_EMPTY);
6594
+ }
6595
+ await zipWriter.appendZipEntries(reader);
6596
+ }
6597
+
6598
+ async function addFileEntry(zipWriter, name, reader, options) {
6599
+ options = Object.assign({}, options);
6600
+ if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
6601
+ name += DIRECTORY_SIGNATURE;
6602
+ }
6603
+ if (zipWriter.filenames.has(name)) {
6604
+ throw new Error(ERR_DUPLICATED_NAME);
6605
+ }
6606
+ zipWriter.filenames.add(name);
6607
+ if (workers < getConfiguration().maxWorkers) {
6608
+ workers++;
6609
+ } else {
6610
+ await new Promise(resolve => pendingEntries.push(resolve));
6611
+ }
6612
+ try {
6613
+ return await addFile(zipWriter, name, reader, options);
6614
+ } catch (error) {
6615
+ zipWriter.filenames.delete(name);
6616
+ throw error;
6617
+ } finally {
6618
+ const pendingEntry = pendingEntries.shift();
6619
+ if (pendingEntry) {
6620
+ pendingEntry();
6621
+ } else {
6622
+ workers--;
6623
+ }
6624
+ }
6625
+ }
6626
+
6627
+ async function abortWritable(zipWriter, error) {
6628
+ try {
6629
+ await zipWriter.writer.writable.abort(error);
6630
+ } catch {
6631
+ // ignored
6413
6632
  }
6414
6633
  }
6415
6634
 
@@ -6468,7 +6687,6 @@
6468
6687
  }
6469
6688
 
6470
6689
  function resolveAttributes(zipWriter, name, options) {
6471
- name = name.trim();
6472
6690
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
6473
6691
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
6474
6692
  const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
@@ -6612,11 +6830,12 @@
6612
6830
  if (getLength(rawComment) > MAX_16_BITS) {
6613
6831
  throw new Error(ERR_INVALID_ENTRY_COMMENT);
6614
6832
  }
6615
- const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION, VERSION_DEFLATE);
6616
- if (version > MAX_16_BITS) {
6833
+ const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION);
6834
+ if (version !== UNDEFINED_VALUE && version > MAX_16_BITS) {
6617
6835
  throw new Error(ERR_INVALID_VERSION);
6618
6836
  }
6619
6837
  const lastModDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE, new Date());
6838
+ const rawLastModDate = getOptionValue(zipWriter, options, PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE);
6620
6839
  const lastAccessDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
6621
6840
  const creationDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
6622
6841
  const internalFileAttributes = getAliasedOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES, 0);
@@ -6660,7 +6879,7 @@
6660
6879
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
6661
6880
  dataDescriptor = false;
6662
6881
  }
6663
- if (dataDescriptor === UNDEFINED_VALUE || zipCrypto) {
6882
+ if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThrough)) {
6664
6883
  dataDescriptor = true;
6665
6884
  }
6666
6885
  if (level !== UNDEFINED_VALUE && level != 6) {
@@ -6672,6 +6891,7 @@
6672
6891
  }
6673
6892
  const rawExtraField = serializeExtraField(options[PROPERTY_NAME_EXTRA_FIELD]);
6674
6893
  const rawLocalExtraField = serializeExtraField(options[OPTION_LOCAL_EXTRA_FIELD]);
6894
+ const rawCentralExtraField = serializeExtraField(options[OPTION_CENTRAL_EXTRA_FIELD]);
6675
6895
  return {
6676
6896
  comment,
6677
6897
  resolvedOptions: {
@@ -6679,6 +6899,7 @@
6679
6899
  rawComment,
6680
6900
  version,
6681
6901
  lastModDate,
6902
+ rawLastModDate,
6682
6903
  lastAccessDate,
6683
6904
  creationDate,
6684
6905
  internalFileAttributes,
@@ -6706,7 +6927,8 @@
6706
6927
  dataDescriptor,
6707
6928
  zip64,
6708
6929
  rawExtraField,
6709
- rawLocalExtraField
6930
+ rawLocalExtraField,
6931
+ rawCentralExtraField
6710
6932
  }
6711
6933
  };
6712
6934
  }
@@ -6875,10 +7097,8 @@
6875
7097
  }
6876
7098
  await initStream(fileWriter);
6877
7099
  const diskOffset = getDiskOffset(writer);
6878
- if (zipWriter.addSplitZipSignature) {
6879
- delete zipWriter.addSplitZipSignature;
6880
- await writeData(writer, getSplitZipSignatureArray());
6881
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
7100
+ if (zipWriter.addSplitZipSignature && !bufferedWrite) {
7101
+ await writeSplitZipSignature(zipWriter, writer);
6882
7102
  }
6883
7103
  if (usdz && !bufferedWrite) {
6884
7104
  appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
@@ -6905,6 +7125,9 @@
6905
7125
  if (bufferedWrite) {
6906
7126
  await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
6907
7127
  await requestLockWriter();
7128
+ if (zipWriter.addSplitZipSignature) {
7129
+ await writeSplitZipSignature(zipWriter, writer);
7130
+ }
6908
7131
  writingBufferedEntryData = true;
6909
7132
  writerSizeBeforeEntry = writer.size;
6910
7133
  await skipDiskIfNeeded();
@@ -7024,6 +7247,7 @@
7024
7247
  versionMadeBy,
7025
7248
  rawComment,
7026
7249
  rawExtraField,
7250
+ rawCentralExtraField,
7027
7251
  useWebWorkers,
7028
7252
  transferStreams,
7029
7253
  onstart,
@@ -7067,6 +7291,7 @@
7067
7291
  rawExtraFieldUnix,
7068
7292
  rawExtraFieldAES,
7069
7293
  rawExtraField,
7294
+ rawCentralExtraField,
7070
7295
  extendedTimestamp,
7071
7296
  msDosCompatible,
7072
7297
  internalFileAttributes,
@@ -7176,6 +7401,7 @@
7176
7401
  const {
7177
7402
  rawFilename,
7178
7403
  lastModDate,
7404
+ rawLastModDate: rawLastModDateOption,
7179
7405
  lastAccessDate,
7180
7406
  creationDate,
7181
7407
  level,
@@ -7309,6 +7535,9 @@
7309
7535
  if (compressionMethod === UNDEFINED_VALUE) {
7310
7536
  compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
7311
7537
  }
7538
+ if (version === UNDEFINED_VALUE) {
7539
+ version = compressionMethod == COMPRESSION_METHOD_STORE && !directory && !encrypted ? VERSION_STORE : VERSION_DEFLATE;
7540
+ }
7312
7541
  const { codecVersionNeeded } = options;
7313
7542
  if (compressed && codecVersionNeeded !== UNDEFINED_VALUE) {
7314
7543
  version = version > codecVersionNeeded ? version : codecVersionNeeded;
@@ -7341,6 +7570,7 @@
7341
7570
  compressionMethod,
7342
7571
  uncompressedSize,
7343
7572
  lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
7573
+ rawLastModDate: rawLastModDateOption,
7344
7574
  rawFilename,
7345
7575
  zip64CompressedSize,
7346
7576
  zip64UncompressedSize,
@@ -7547,9 +7777,9 @@
7547
7777
 
7548
7778
  async function closeFile(zipWriter, comment, options) {
7549
7779
  const directoryDataLength = createDirectoryRecords(zipWriter.fileEntries);
7550
- const { directoryStart, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
7780
+ const { directoryStart, directoryEnd, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
7551
7781
  const signatureLength = await writeDigitalSignatureRecord(zipWriter, directoryArray, options);
7552
- await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryDataLength, signatureLength });
7782
+ await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength });
7553
7783
  }
7554
7784
 
7555
7785
  function createDirectoryRecords(files) {
@@ -7562,6 +7792,7 @@
7562
7792
  rawExtraFieldNTFS,
7563
7793
  rawExtraFieldUnix,
7564
7794
  rawExtraField,
7795
+ rawCentralExtraField,
7565
7796
  extendedTimestamp,
7566
7797
  extraFieldExtendedTimestampFlag,
7567
7798
  lastModDate,
@@ -7616,7 +7847,8 @@
7616
7847
  rawExtraFieldNTFS,
7617
7848
  rawExtraFieldUnix,
7618
7849
  rawExtraFieldTimestamp,
7619
- rawExtraField);
7850
+ rawExtraField,
7851
+ rawCentralExtraField);
7620
7852
  if (extraFieldLength > MAX_16_BITS) {
7621
7853
  throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
7622
7854
  }
@@ -7633,6 +7865,7 @@
7633
7865
  let directoryDiskOffset = 0;
7634
7866
  let directoryStartDiskNumber = getDiskNumber(writer);
7635
7867
  let directoryStartDiskOffset = getDiskOffset(writer);
7868
+ let directoryEndDiskEntriesLength = 0;
7636
7869
  for (const [indexFileEntry, fileEntry] of Array.from(fileEntries.values()).entries()) {
7637
7870
  const {
7638
7871
  offset: fileEntryOffset,
@@ -7643,6 +7876,7 @@
7643
7876
  rawExtraFieldNTFS,
7644
7877
  rawExtraFieldUnix,
7645
7878
  rawExtraField,
7879
+ rawCentralExtraField,
7646
7880
  rawComment,
7647
7881
  versionMadeBy,
7648
7882
  headerArray,
@@ -7657,11 +7891,12 @@
7657
7891
  uncompressedSize,
7658
7892
  compressedSize
7659
7893
  } = fileEntry;
7660
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
7894
+ const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField, rawCentralExtraField);
7661
7895
  const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
7662
7896
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
7663
7897
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
7664
7898
  directoryDiskOffset = offset;
7899
+ directoryEndDiskEntriesLength = 0;
7665
7900
  await writer.closeDisk();
7666
7901
  }
7667
7902
  if (indexFileEntry == 0) {
@@ -7694,9 +7929,11 @@
7694
7929
  directoryRecord.writeBytes(rawExtraFieldNTFS);
7695
7930
  directoryRecord.writeBytes(rawExtraFieldUnix);
7696
7931
  directoryRecord.writeBytes(rawExtraField);
7932
+ directoryRecord.writeBytes(rawCentralExtraField);
7697
7933
  directoryRecord.writeBytes(rawComment);
7698
7934
  arraySet(directoryArray, directoryRecord.array, offset);
7699
7935
  offset += directoryRecordLength;
7936
+ directoryEndDiskEntriesLength++;
7700
7937
  if (options.onprogress) {
7701
7938
  try {
7702
7939
  await options.onprogress(indexFileEntry + 1, fileEntries.size, new Entry(fileEntry));
@@ -7708,6 +7945,7 @@
7708
7945
  await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
7709
7946
  return {
7710
7947
  directoryStart: { diskNumber: directoryStartDiskNumber, diskOffset: directoryStartDiskOffset },
7948
+ directoryEnd: { diskNumber: getDiskNumber(writer), entriesLength: directoryEndDiskEntriesLength },
7711
7949
  directoryArray
7712
7950
  };
7713
7951
  }
@@ -7736,16 +7974,20 @@
7736
7974
 
7737
7975
  async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
7738
7976
  const { writer } = zipWriter;
7739
- const { directoryStart, signatureLength } = cdInfo;
7977
+ const { directoryStart, directoryEnd, signatureLength } = cdInfo;
7740
7978
  let { directoryDataLength } = cdInfo;
7741
7979
  let fileEntriesLength = zipWriter.fileEntries.size;
7742
7980
  let diskNumber = directoryStart.diskNumber;
7743
7981
  let directoryOffset = getSegmentOffset(zipWriter, directoryStart);
7982
+ const commentLength = getLength(comment);
7983
+ if (commentLength > MAX_16_BITS) {
7984
+ throw new Error(ERR_INVALID_COMMENT);
7985
+ }
7986
+ let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
7744
7987
  let lastDiskNumber = getDiskNumber(writer);
7745
- if (exceedsAvailableSize(writer, END_OF_CENTRAL_DIR_LENGTH)) {
7988
+ if (exceedsAvailableSize(writer, (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH) + commentLength)) {
7746
7989
  lastDiskNumber++;
7747
7990
  }
7748
- let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
7749
7991
  if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || fileEntriesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
7750
7992
  if (zip64 === false) {
7751
7993
  throw new Error(ERR_UNSUPPORTED_FORMAT);
@@ -7753,15 +7995,12 @@
7753
7995
  zip64 = true;
7754
7996
  }
7755
7997
  }
7756
- const commentLength = getLength(comment);
7757
- if (commentLength > MAX_16_BITS) {
7758
- throw new Error(ERR_INVALID_COMMENT);
7759
- }
7760
7998
  const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
7761
7999
  if (exceedsAvailableSize(writer, getLength(endOfdirectoryRecord.array) + commentLength)) {
7762
8000
  await writer.closeDisk();
7763
8001
  }
7764
8002
  lastDiskNumber = getDiskNumber(writer);
8003
+ let diskFileEntriesLength = lastDiskNumber == directoryEnd.diskNumber ? directoryEnd.entriesLength : 0;
7765
8004
  if (zip64) {
7766
8005
  endOfdirectoryRecord.writeUint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
7767
8006
  endOfdirectoryRecord.writeUint64(44);
@@ -7769,7 +8008,7 @@
7769
8008
  endOfdirectoryRecord.writeUint16(45);
7770
8009
  endOfdirectoryRecord.writeUint32(lastDiskNumber);
7771
8010
  endOfdirectoryRecord.writeUint32(diskNumber);
7772
- endOfdirectoryRecord.writeUint64(fileEntriesLength);
8011
+ endOfdirectoryRecord.writeUint64(diskFileEntriesLength);
7773
8012
  endOfdirectoryRecord.writeUint64(fileEntriesLength);
7774
8013
  endOfdirectoryRecord.writeUint64(directoryDataLength);
7775
8014
  endOfdirectoryRecord.writeUint64(directoryOffset);
@@ -7782,6 +8021,7 @@
7782
8021
  lastDiskNumber = MAX_16_BITS;
7783
8022
  diskNumber = MAX_16_BITS;
7784
8023
  }
8024
+ diskFileEntriesLength = MAX_16_BITS;
7785
8025
  fileEntriesLength = MAX_16_BITS;
7786
8026
  directoryOffset = MAX_32_BITS;
7787
8027
  directoryDataLength = MAX_32_BITS;
@@ -7789,7 +8029,7 @@
7789
8029
  endOfdirectoryRecord.writeUint32(END_OF_CENTRAL_DIR_SIGNATURE);
7790
8030
  endOfdirectoryRecord.writeUint16(lastDiskNumber);
7791
8031
  endOfdirectoryRecord.writeUint16(diskNumber);
7792
- endOfdirectoryRecord.writeUint16(fileEntriesLength);
8032
+ endOfdirectoryRecord.writeUint16(diskFileEntriesLength);
7793
8033
  endOfdirectoryRecord.writeUint16(fileEntriesLength);
7794
8034
  endOfdirectoryRecord.writeUint32(directoryDataLength);
7795
8035
  endOfdirectoryRecord.writeUint32(directoryOffset);
@@ -7888,9 +8128,21 @@
7888
8128
  async function copyData(zipWriter, reader, offset, size) {
7889
8129
  if (size > 0) {
7890
8130
  const { writer } = zipWriter;
7891
- await createReadable(reader, { offset, size }).pipeTo(writer.writable, { preventClose: true, preventAbort: true });
7892
- writer.size += size;
7893
- zipWriter.offset += size;
8131
+ let copiedLength = 0;
8132
+ try {
8133
+ await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8134
+ } catch (error) {
8135
+ zipWriter.hasCorruptedEntries = true;
8136
+ try {
8137
+ error.corruptedEntry = true;
8138
+ } catch {
8139
+ // ignored
8140
+ }
8141
+ throw error;
8142
+ } finally {
8143
+ writer.size += copiedLength;
8144
+ zipWriter.offset += copiedLength;
8145
+ }
7894
8146
  }
7895
8147
  }
7896
8148
 
@@ -7915,6 +8167,12 @@
7915
8167
  return signatureArray;
7916
8168
  }
7917
8169
 
8170
+ async function writeSplitZipSignature(zipWriter, writer) {
8171
+ delete zipWriter.addSplitZipSignature;
8172
+ await writeData(writer, getSplitZipSignatureArray());
8173
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
8174
+ }
8175
+
7918
8176
  async function writeData(writer, array) {
7919
8177
  const { writable } = writer;
7920
8178
  const streamWriter = writable.getWriter();
@@ -8675,6 +8933,20 @@
8675
8933
  exports.TextWriter = TextWriter;
8676
8934
  exports.Uint8ArrayReader = Uint8ArrayReader;
8677
8935
  exports.Uint8ArrayWriter = Uint8ArrayWriter;
8936
+ exports.WARNING_APPENDED_DATA = WARNING_APPENDED_DATA;
8937
+ exports.WARNING_COMPRESSED_PATCHED_DATA = WARNING_COMPRESSED_PATCHED_DATA;
8938
+ exports.WARNING_DUPLICATE_FILENAME = WARNING_DUPLICATE_FILENAME;
8939
+ exports.WARNING_MALFORMED_EXTRA_FIELD = WARNING_MALFORMED_EXTRA_FIELD;
8940
+ exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG;
8941
+ exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD;
8942
+ exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES;
8943
+ exports.WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY;
8944
+ exports.WARNING_PREPENDED_DATA = WARNING_PREPENDED_DATA;
8945
+ exports.WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = WARNING_TRAILING_CENTRAL_DIRECTORY_DATA;
8946
+ exports.WARNING_UNKNOWN_VERSION = WARNING_UNKNOWN_VERSION;
8947
+ exports.WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA = WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA;
8948
+ exports.WARNING_UNSORTED_CENTRAL_DIRECTORY = WARNING_UNSORTED_CENTRAL_DIRECTORY;
8949
+ exports.WARNING_WRAPPED_ENTRIES_COUNT = WARNING_WRAPPED_ENTRIES_COUNT;
8678
8950
  exports.Writer = Writer;
8679
8951
  exports.ZipReader = ZipReader;
8680
8952
  exports.ZipReaderStream = ZipReaderStream;
@@ -8685,6 +8957,7 @@
8685
8957
  exports.createOPFSTempStream = createOPFSTempStream;
8686
8958
  exports.createSyncAccessHandleTempStream = createSyncAccessHandleTempStream;
8687
8959
  exports.getMimeType = getMimeType;
8960
+ exports.isZipFile = isZipFile;
8688
8961
  exports.registerCodec = registerCodec;
8689
8962
  exports.resetConfiguration = resetConfiguration;
8690
8963
  exports.terminateWorkers = terminateWorkers;