@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.
@@ -82,6 +82,7 @@
82
82
  const BITFLAG_LEVEL_FAST_MASK = 0b100;
83
83
  const BITFLAG_LEVEL_SUPER_FAST_MASK = 0b110;
84
84
  const BITFLAG_DATA_DESCRIPTOR = 0b1000;
85
+ const BITFLAG_COMPRESSED_PATCHED_DATA = 0b100000;
85
86
  const BITFLAG_STRONG_ENCRYPTION = 0b1000000;
86
87
  const BITFLAG_LANG_ENCODING_FLAG = 0b100000000000;
87
88
  const BITFLAG_MASKED_LOCAL_HEADERS = 0b10000000000000;
@@ -100,6 +101,7 @@
100
101
  const FILE_ATTR_UNIX_SETGID_MASK = 0o2000;
101
102
  const FILE_ATTR_UNIX_STICKY_MASK = 0o1000;
102
103
 
104
+ const VERSION_STORE = 0x0A;
103
105
  const VERSION_DEFLATE = 0x14;
104
106
  const VERSION_ZIP64 = 0x2D;
105
107
  const VERSION_AES = 0x33;
@@ -198,6 +200,7 @@
198
200
  const OPTION_USDZ = "usdz";
199
201
  const OPTION_UNIX_EXTRA_FIELD_TYPE = "unixExtraFieldType";
200
202
  const OPTION_LOCAL_EXTRA_FIELD = "localExtraField";
203
+ const OPTION_CENTRAL_EXTRA_FIELD = "centralExtraField";
201
204
  const OPTION_STRICTNESS = "strictness";
202
205
  const OPTION_FILENAME_VALIDATION = "filenameValidation";
203
206
  const OPTION_NORMALIZE_FILENAME = "normalizeFilename";
@@ -3608,6 +3611,8 @@
3608
3611
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3609
3612
  */
3610
3613
 
3614
+
3615
+ const ERR_WORKER_STARTUP_TIMEOUT = "Worker startup timeout";
3611
3616
  let initModule = () => { };
3612
3617
 
3613
3618
  async function supportsDeflate(config) {
@@ -4123,6 +4128,21 @@
4123
4128
  const ERR_INVALID_FILENAME_VALIDATION = "Invalid filenameValidation (must be 'strict', 'balanced' or 'tolerant')";
4124
4129
  const ERR_INVALID_MAX_APPENDED_DATA_SIZE = "Invalid maxAppendedDataSize (must be a number greater than or equal to 0)";
4125
4130
  const ERR_UNSUPPORTED_UINT64 = "64-bit value exceeds Number.MAX_SAFE_INTEGER";
4131
+ const WARNING_UNSORTED_CENTRAL_DIRECTORY = "unsorted central directory";
4132
+ const WARNING_UNKNOWN_VERSION = "unknown version needed to extract";
4133
+ const WARNING_COMPRESSED_PATCHED_DATA = "compressed patched data";
4134
+ const WARNING_MALFORMED_EXTRA_FIELD = "malformed extra field";
4135
+ const WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA = "unknown zip64 extensible data";
4136
+ const WARNING_WRAPPED_ENTRIES_COUNT = "wrapped entries count";
4137
+ const WARNING_APPENDED_DATA = "appended data";
4138
+ const WARNING_PREPENDED_DATA = "prepended data";
4139
+ const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
4140
+ const WARNING_DUPLICATE_FILENAME = "duplicate filename";
4141
+ const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
4142
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
4143
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
4144
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
4145
+ const MAX_KNOWN_VERSION = 63;
4126
4146
  const DRIVE_LETTER_REGEXP = /^[a-zA-Z]:/;
4127
4147
  const CHARSET_UTF8 = "utf-8";
4128
4148
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
@@ -4173,6 +4193,7 @@
4173
4193
  if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
4174
4194
  throw new Error(ERR_BAD_FORMAT);
4175
4195
  }
4196
+ const warnings = zipReader.warnings = [];
4176
4197
  const strictness = getStrictness(options, zipReader.options);
4177
4198
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
4178
4199
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
@@ -4196,8 +4217,12 @@
4196
4217
  const commentOffset = endOfDirectoryInfo.offset;
4197
4218
  const commentLength = getUint16$1(endOfDirectoryView, 20);
4198
4219
  const appendedDataOffset = commentOffset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
4199
- if (reader.size - appendedDataOffset > maxAppendedDataSize) {
4200
- throwAmbiguousArchive("appended data");
4220
+ const appendedDataLength = reader.size - appendedDataOffset;
4221
+ if (appendedDataLength > maxAppendedDataSize) {
4222
+ throwAmbiguousArchive(WARNING_APPENDED_DATA);
4223
+ }
4224
+ if (appendedDataLength > 0) {
4225
+ addWarning(warnings, WARNING_APPENDED_DATA);
4201
4226
  }
4202
4227
  let lastDiskNumber = getUint16$1(endOfDirectoryView, 4);
4203
4228
  const expectedLastDiskNumber = reader.lastDiskNumber || 0;
@@ -4251,23 +4276,23 @@
4251
4276
  }
4252
4277
  if (lastDiskNumber == MAX_16_BITS) {
4253
4278
  lastDiskNumber = getUint32$1(endOfDirectoryView, 16);
4254
- } else if (checkAmbiguity && lastDiskNumber != getUint32$1(endOfDirectoryView, 16)) {
4255
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
4279
+ } else if (lastDiskNumber != getUint32$1(endOfDirectoryView, 16)) {
4280
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4256
4281
  }
4257
4282
  if (diskNumber == MAX_16_BITS) {
4258
4283
  diskNumber = getUint32$1(endOfDirectoryView, 20);
4259
- } else if (checkAmbiguity && diskNumber != getUint32$1(endOfDirectoryView, 20)) {
4260
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
4284
+ } else if (diskNumber != getUint32$1(endOfDirectoryView, 20)) {
4285
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4261
4286
  }
4262
4287
  if (filesLength == MAX_16_BITS) {
4263
4288
  filesLength = getBigUint64(endOfDirectoryView, 32);
4264
- } else if (checkAmbiguity && filesLength != getBigUint64(endOfDirectoryView, 32)) {
4265
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
4289
+ } else if (filesLength != getBigUint64(endOfDirectoryView, 32)) {
4290
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4266
4291
  }
4267
4292
  if (directoryDataLength == MAX_32_BITS) {
4268
4293
  directoryDataLength = getBigUint64(endOfDirectoryView, 40);
4269
- } else if (checkAmbiguity && directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
4270
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
4294
+ } else if (directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
4295
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4271
4296
  }
4272
4297
  directoryDataOffset = getDiskOffset$1(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
4273
4298
  }
@@ -4336,11 +4361,23 @@
4336
4361
  declaredDirectoryDataLength = directoryArray.length;
4337
4362
  decryptedDirectory = true;
4338
4363
  }
4364
+ if (directoryEncryptionInfo && !decryptedDirectory &&
4365
+ (directoryArray.length < 4 || getUint32$1(directoryView, 0) == CENTRAL_FILE_HEADER_SIGNATURE)) {
4366
+ addWarning(warnings, WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA);
4367
+ }
4339
4368
  startOffset = directoryDataOffset;
4340
4369
  const filenameEncoding = getOptionValue$1(zipReader, options, OPTION_FILENAME_ENCODING);
4341
4370
  const commentEncoding = getOptionValue$1(zipReader, options, OPTION_COMMENT_ENCODING);
4342
- const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
4371
+ const filenames = new Set();
4343
4372
  let duplicateFilename;
4373
+ let previousEntryPosition = -1;
4374
+ const recoverWrappedFilesLength = !checkAmbiguity && !zip64EndOfDirectory;
4375
+ if (!filesLength && recoverWrappedFilesLength) {
4376
+ filesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
4377
+ if (filesLength) {
4378
+ addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
4379
+ }
4380
+ }
4344
4381
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
4345
4382
  const fileEntry = new ZipEntry$1(reader, zipReader.options);
4346
4383
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
@@ -4418,15 +4455,26 @@
4418
4455
  filename,
4419
4456
  comment
4420
4457
  });
4421
- readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
4458
+ if (readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6)) {
4459
+ addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
4460
+ }
4422
4461
  fileEntry.offset += prependedDataLength;
4423
- startOffset = Math.min(getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset, startOffset);
4424
- if (checkAmbiguity) {
4425
- if (filenames.has(fileEntry.filename)) {
4426
- duplicateFilename = true;
4427
- }
4428
- filenames.add(fileEntry.filename);
4462
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
4463
+ startOffset = Math.min(entryPosition, startOffset);
4464
+ if (entryPosition < previousEntryPosition) {
4465
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
4466
+ }
4467
+ previousEntryPosition = entryPosition;
4468
+ if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
4469
+ addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
4470
+ }
4471
+ if ((fileEntry.rawBitFlag & BITFLAG_COMPRESSED_PATCHED_DATA) == BITFLAG_COMPRESSED_PATCHED_DATA) {
4472
+ addWarning(warnings, WARNING_COMPRESSED_PATCHED_DATA, filename);
4473
+ }
4474
+ if (filenames.has(fileEntry.filename)) {
4475
+ duplicateFilename = true;
4429
4476
  }
4477
+ filenames.add(fileEntry.filename);
4430
4478
  const unixExternalUpper = (fileEntry.externalFileAttributes >> 16) & MAX_16_BITS;
4431
4479
  if (fileEntry.unixMode === UNDEFINED_VALUE && (unixExternalUpper & (FILE_ATTR_UNIX_DEFAULT_MASK | FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_TYPE_DIR)) != 0) {
4432
4480
  fileEntry.unixMode = unixExternalUpper;
@@ -4450,7 +4498,7 @@
4450
4498
  internalFileAttribute: fileEntry.internalFileAttributes,
4451
4499
  externalFileAttribute: fileEntry.externalFileAttributes,
4452
4500
  executable,
4453
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || (fileEntry.filename.endsWith(DIRECTORY_SIGNATURE) && !fileEntry.uncompressedSize),
4501
+ directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
4454
4502
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
4455
4503
  });
4456
4504
  const entry = new Entry(fileEntry);
@@ -4463,6 +4511,13 @@
4463
4511
  return arrayBufferPromise;
4464
4512
  };
4465
4513
  offset = endOffset;
4514
+ if (indexFile == filesLength - 1 && recoverWrappedFilesLength) {
4515
+ const wrappedFilesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
4516
+ if (wrappedFilesLength) {
4517
+ filesLength += wrappedFilesLength;
4518
+ addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
4519
+ }
4520
+ }
4466
4521
  const { onprogress } = options;
4467
4522
  if (onprogress) {
4468
4523
  try {
@@ -4487,18 +4542,22 @@
4487
4542
  zipReader.digitalSignature = digitalSignature;
4488
4543
  offsetAfterSignature = offset + 6 + digitalSignature.length;
4489
4544
  }
4490
- if (checkAmbiguity && offset != declaredDirectoryDataLength && offsetAfterSignature != declaredDirectoryDataLength) {
4491
- throwAmbiguousArchive("trailing central directory data");
4545
+ if ((offset != declaredDirectoryDataLength && offsetAfterSignature != declaredDirectoryDataLength) ||
4546
+ (!decryptedDirectory && offset != directoryDataLength && offsetAfterSignature != directoryDataLength)) {
4547
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_TRAILING_CENTRAL_DIRECTORY_DATA);
4492
4548
  }
4493
4549
  if (duplicateFilename) {
4494
- throwAmbiguousArchive("duplicate filename");
4550
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_DUPLICATE_FILENAME);
4495
4551
  }
4496
4552
  const extractPrependedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
4497
4553
  const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
4498
4554
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
4499
4555
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
4500
4556
  if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
4501
- throwAmbiguousArchive("prepended data");
4557
+ throwAmbiguousArchive(WARNING_PREPENDED_DATA);
4558
+ }
4559
+ if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
4560
+ addWarning(warnings, WARNING_PREPENDED_DATA);
4502
4561
  }
4503
4562
  if (extractPrependedData) {
4504
4563
  zipReader.prependedData = startOffset > splitZipSignatureLength ?
@@ -4528,6 +4587,68 @@
4528
4587
  }
4529
4588
  }
4530
4589
 
4590
+ class ZipReaderStream {
4591
+
4592
+ constructor(options = {}) {
4593
+ const { readable, writable } = new TransformStream();
4594
+ const gen = new ZipReader(readable, options).getEntriesGenerator();
4595
+ this.readable = new ReadableStream({
4596
+ async pull(controller) {
4597
+ const { done, value } = await gen.next();
4598
+ if (done)
4599
+ return controller.close();
4600
+ const chunk = {
4601
+ ...value,
4602
+ readable: (function () {
4603
+ const { readable, writable } = new TransformStream();
4604
+ if (value.getData) {
4605
+ getData();
4606
+ return readable;
4607
+ }
4608
+
4609
+ async function getData() {
4610
+ try {
4611
+ await value.getData(writable);
4612
+ } catch (error) {
4613
+ try {
4614
+ await writable.abort(error);
4615
+ } catch {
4616
+ // ignored
4617
+ }
4618
+ }
4619
+ }
4620
+ })()
4621
+ };
4622
+ delete chunk.getData;
4623
+ controller.enqueue(chunk);
4624
+ }
4625
+ });
4626
+ this.writable = writable;
4627
+ }
4628
+ }
4629
+
4630
+ async function isZipFile(reader, options = {}) {
4631
+ reader = new GenericReader(reader);
4632
+ await initStream(reader);
4633
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
4634
+ reader = new BlobReader(await streamToBlob(reader.readable));
4635
+ await initStream(reader);
4636
+ }
4637
+ if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
4638
+ return false;
4639
+ }
4640
+ const strictness = getStrictness(options, {});
4641
+ const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
4642
+ const maxAppendedDataSize = getMaxAppendedDataSize(options[OPTION_MAX_APPENDED_DATA_SIZE], strictness);
4643
+ const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
4644
+ if (!endOfDirectoryInfo || (strictness == STRICTNESS_STRICT && endOfDirectoryReachingEndCount > 1)) {
4645
+ return false;
4646
+ }
4647
+ const commentLength = getUint16$1(getDataView(endOfDirectoryInfo), 20);
4648
+ const appendedDataOffset = endOfDirectoryInfo.offset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
4649
+ return reader.size - appendedDataOffset <= maxAppendedDataSize;
4650
+ }
4651
+
4531
4652
  let ZipEntry$1 = class ZipEntry {
4532
4653
 
4533
4654
  constructor(reader, options) {
@@ -4559,6 +4680,7 @@
4559
4680
  dataDescriptor
4560
4681
  } = bitFlag;
4561
4682
  const localDirectory = fileEntry.localDirectory = {};
4683
+ const warnings = fileEntry.warnings = [];
4562
4684
  const localHeaderOffset = getDiskOffset$1(reader, diskNumberStart) + offset;
4563
4685
  const dataArray = await readUint8Array(reader, localHeaderOffset, HEADER_SIZE);
4564
4686
  const dataView = getDataView(dataArray);
@@ -4581,6 +4703,7 @@
4581
4703
  extraFieldLength,
4582
4704
  filenameLength
4583
4705
  } = localDirectory;
4706
+ const dataOffset = localDirectory.dataOffset = localHeaderOffset + HEADER_SIZE + filenameLength + extraFieldLength;
4584
4707
  const checkLocalDirectoryOption = getOptionValue$1(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
4585
4708
  const entryStrictness = getStrictness(options, zipEntry.options);
4586
4709
  const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
@@ -4598,10 +4721,10 @@
4598
4721
  if (checkLocalFilename) {
4599
4722
  localDirectory.rawFilename = rawLocalFilename;
4600
4723
  }
4601
- readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
4602
- if (checkLocalDirectory) {
4603
- validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename);
4724
+ if (readCommonFooter(zipEntry, localDirectory, dataView, 4, true)) {
4725
+ addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD);
4604
4726
  }
4727
+ validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
4605
4728
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
4606
4729
  if (lastAccessDate) {
4607
4730
  fileEntry.lastAccessDate = lastAccessDate;
@@ -4634,7 +4757,6 @@
4634
4757
  throw new Error(ERR_ENCRYPTED);
4635
4758
  }
4636
4759
  }
4637
- const dataOffset = localHeaderOffset + HEADER_SIZE + filenameLength + extraFieldLength;
4638
4760
  if (dataOffset + compressedSize > reader.size) {
4639
4761
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
4640
4762
  }
@@ -4754,6 +4876,16 @@
4754
4876
  return false;
4755
4877
  }
4756
4878
 
4879
+ function getWrappedFilesLength(directoryView, directoryArray, offset) {
4880
+ let wrappedFilesLength = 0;
4881
+ while (offset + CENTRAL_FILE_HEADER_LENGTH <= directoryArray.length && getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE) {
4882
+ offset += CENTRAL_FILE_HEADER_LENGTH +
4883
+ getUint16$1(directoryView, offset + 28) + getUint16$1(directoryView, offset + 30) + getUint16$1(directoryView, offset + 32);
4884
+ wrappedFilesLength++;
4885
+ }
4886
+ return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
4887
+ }
4888
+
4757
4889
  function readDigitalSignature(signatureRecordArray) {
4758
4890
  if (signatureRecordArray.length >= 6) {
4759
4891
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -4818,6 +4950,7 @@
4818
4950
  const extraField = directory.extraField = new Map();
4819
4951
  const rawExtraFieldView = getDataView(rawExtraField);
4820
4952
  let offsetExtraField = 0;
4953
+ let malformedExtraField = false;
4821
4954
  try {
4822
4955
  while (offsetExtraField < rawExtraField.length) {
4823
4956
  const type = getUint16$1(rawExtraFieldView, offsetExtraField);
@@ -4829,7 +4962,10 @@
4829
4962
  offsetExtraField += 4 + size;
4830
4963
  }
4831
4964
  } catch {
4832
- // ignored
4965
+ malformedExtraField = true;
4966
+ }
4967
+ if (offsetExtraField > rawExtraField.length) {
4968
+ malformedExtraField = true;
4833
4969
  }
4834
4970
  const compressionMethod = getUint16$1(dataView, offset + 4);
4835
4971
  Object.assign(directory, {
@@ -4897,6 +5033,7 @@
4897
5033
  if (extraFieldUSDZ) {
4898
5034
  directory.extraFieldUSDZ = extraFieldUSDZ;
4899
5035
  }
5036
+ return malformedExtraField;
4900
5037
  }
4901
5038
 
4902
5039
  function readExtraFieldZip64(extraFieldZip64, directory) {
@@ -4926,10 +5063,11 @@
4926
5063
  const computedCrc32View = getDataView(new Uint8Array(4));
4927
5064
  computedCrc32View.setUint32(0, computedCrc32.get(), true);
4928
5065
  const nameCrc32 = getUint32$1(extraFieldView, 1);
5066
+ const version = getUint8(extraFieldView, 0);
4929
5067
  Object.assign(extraFieldUnicode, {
4930
- version: getUint8(extraFieldView, 0),
5068
+ version,
4931
5069
  [propertyName]: decodeText(extraFieldUnicode.data.subarray(5)),
4932
- valid: !fileEntry.bitFlag.languageEncodingFlag && nameCrc32 == getUint32$1(computedCrc32View, 0)
5070
+ valid: version == 1 && !fileEntry.bitFlag.languageEncodingFlag && nameCrc32 == getUint32$1(computedCrc32View, 0)
4933
5071
  });
4934
5072
  if (extraFieldUnicode.valid) {
4935
5073
  directory[propertyName] = extraFieldUnicode[propertyName];
@@ -5357,27 +5495,46 @@
5357
5495
  return UNDEFINED_VALUE;
5358
5496
  }
5359
5497
 
5360
- function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename) {
5498
+ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, warnings) {
5361
5499
  const { rawFilename } = zipEntry;
5500
+ const reject = !warnings;
5362
5501
  const maskedLocalDirectory = zipEntry.decryptedDirectory &&
5363
5502
  (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
5364
5503
  if (checkLocalFilename && !maskedLocalDirectory &&
5365
5504
  (rawLocalFilename.length != rawFilename.length ||
5366
5505
  rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
5367
- throwAmbiguousArchive("mismatched local file header (filename)");
5506
+ reportAmbiguity(reject, warnings, "mismatched local file header (filename)");
5368
5507
  }
5369
5508
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
5370
- throwAmbiguousArchive("mismatched local file header (general purpose bit flag)");
5509
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
5371
5510
  }
5372
5511
  if (localDirectory.compressionMethod != zipEntry.compressionMethod) {
5373
- throwAmbiguousArchive("mismatched local file header (compression method)");
5512
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD);
5374
5513
  }
5375
5514
  if (!localDirectory.bitFlag.dataDescriptor && !maskedLocalDirectory &&
5376
5515
  (localDirectory.crc32 || localDirectory.compressedSize || localDirectory.uncompressedSize) &&
5377
5516
  (localDirectory.crc32 != zipEntry.crc32 ||
5378
5517
  localDirectory.compressedSize != zipEntry.compressedSize ||
5379
5518
  localDirectory.uncompressedSize != zipEntry.uncompressedSize)) {
5380
- throwAmbiguousArchive("mismatched local file header (crc32 or sizes)");
5519
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
5520
+ }
5521
+ }
5522
+
5523
+ function reportAmbiguity(reject, warnings, reason) {
5524
+ if (reject) {
5525
+ throwAmbiguousArchive(reason);
5526
+ } else {
5527
+ addWarning(warnings, reason);
5528
+ }
5529
+ }
5530
+
5531
+ function addWarning(warnings, reason, filename) {
5532
+ if (!warnings.some(warning => warning.reason == reason)) {
5533
+ const warning = { reason };
5534
+ if (filename !== UNDEFINED_VALUE) {
5535
+ warning.filename = filename;
5536
+ }
5537
+ warnings.push(warning);
5381
5538
  }
5382
5539
  }
5383
5540
 
@@ -5398,7 +5555,8 @@
5398
5555
 
5399
5556
  function getDate(timeRaw) {
5400
5557
  const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
5401
- return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
5558
+ const result = new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
5559
+ return result < MIN_DATE ? MIN_DATE : result;
5402
5560
  }
5403
5561
 
5404
5562
  function getDateNTFS(timeRaw) {
@@ -5452,7 +5610,24 @@
5452
5610
  ERR_UNSUPPORTED_COMPRESSION: ERR_UNSUPPORTED_COMPRESSION$1,
5453
5611
  ERR_UNSUPPORTED_ENCRYPTION: ERR_UNSUPPORTED_ENCRYPTION,
5454
5612
  ERR_UNSUPPORTED_UINT64: ERR_UNSUPPORTED_UINT64,
5455
- ZipReader: ZipReader
5613
+ ERR_WORKER_STARTUP_TIMEOUT: ERR_WORKER_STARTUP_TIMEOUT,
5614
+ WARNING_APPENDED_DATA: WARNING_APPENDED_DATA,
5615
+ WARNING_COMPRESSED_PATCHED_DATA: WARNING_COMPRESSED_PATCHED_DATA,
5616
+ WARNING_DUPLICATE_FILENAME: WARNING_DUPLICATE_FILENAME,
5617
+ WARNING_MALFORMED_EXTRA_FIELD: WARNING_MALFORMED_EXTRA_FIELD,
5618
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG: WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
5619
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD: WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
5620
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES: WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES,
5621
+ WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY: WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
5622
+ WARNING_PREPENDED_DATA: WARNING_PREPENDED_DATA,
5623
+ WARNING_TRAILING_CENTRAL_DIRECTORY_DATA: WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
5624
+ WARNING_UNKNOWN_VERSION: WARNING_UNKNOWN_VERSION,
5625
+ WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA: WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA,
5626
+ WARNING_UNSORTED_CENTRAL_DIRECTORY: WARNING_UNSORTED_CENTRAL_DIRECTORY,
5627
+ WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
5628
+ ZipReader: ZipReader,
5629
+ ZipReaderStream: ZipReaderStream,
5630
+ isZipFile: isZipFile
5456
5631
  });
5457
5632
 
5458
5633
  /*
@@ -5549,153 +5724,151 @@
5549
5724
  offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
5550
5725
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
5551
5726
  pendingAddFileCalls: new Set(),
5727
+ pendingErrors: [],
5552
5728
  bufferedWrites: 0,
5553
5729
  lastFileEntry: UNDEFINED_VALUE
5554
5730
  });
5555
5731
  }
5556
5732
 
5557
- async prependZip(reader) {
5558
- if (this.filenames.size) {
5559
- throw new Error(ERR_ZIP_NOT_EMPTY);
5560
- }
5561
- reader = new GenericReader(reader);
5562
- await initStream(reader);
5563
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
5564
- reader = new BlobReader(await streamToBlob(reader.readable));
5565
- await initStream(reader);
5566
- }
5567
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
5568
- const zipReader$1 = new ZipReader(reader);
5569
- const entries = await zipReader$1.getEntries();
5570
- await zipReader$1.close();
5571
- await initStream(this.writer);
5572
- const { directoryOffset } = zipReader$1;
5573
- if (this.addSplitZipSignature) {
5574
- delete this.addSplitZipSignature;
5575
- if (!await startsWithSplitZipSignature(reader)) {
5576
- await writeData(this.writer, getSplitZipSignatureArray());
5577
- this.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
5578
- }
5579
- }
5580
- const entryPositions = await copyZipData(this, reader, entries, directoryOffset);
5581
- this.filenames = new Set(entries.map(entry => entry.filename));
5582
- this.fileEntries = new Map(entries.map(entry => {
5583
- const {
5584
- version,
5585
- rawLastModDate,
5586
- lastAccessDate,
5587
- creationDate,
5588
- rawFilename,
5589
- bitFlag,
5590
- encrypted,
5591
- uncompressedSize,
5592
- compressedSize,
5593
- extraFieldZip64
5594
- } = entry;
5595
- let {
5596
- compressionMethod,
5597
- rawExtraFieldZip64,
5598
- rawExtraFieldAES,
5599
- rawExtraFieldExtendedTimestamp,
5600
- rawExtraFieldNTFS,
5601
- rawExtraFieldUnix,
5602
- rawExtraField,
5603
- } = entry;
5604
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
5605
- rawExtraFieldZip64 = rawExtraFieldZip64 || EMPTY_UINT8_ARRAY;
5606
- rawExtraFieldAES = rawExtraFieldAES || EMPTY_UINT8_ARRAY;
5607
- rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || EMPTY_UINT8_ARRAY;
5608
- rawExtraFieldNTFS = rawExtraFieldNTFS || EMPTY_UINT8_ARRAY;
5609
- rawExtraFieldUnix = rawExtraFieldUnix || EMPTY_UINT8_ARRAY;
5610
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
5611
- if (entry.extraFieldAES) {
5612
- compressionMethod = COMPRESSION_METHOD_AES;
5613
- }
5614
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
5615
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
5616
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
5617
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
5618
- const {
5619
- headerArray,
5620
- headerView
5621
- } = getHeaderArrayData({
5622
- version,
5623
- bitFlag: bitFlagValue,
5624
- compressionMethod,
5625
- uncompressedSize,
5626
- compressedSize,
5627
- rawLastModDate,
5628
- rawFilename,
5629
- zip64CompressedSize,
5630
- zip64UncompressedSize,
5631
- extraFieldLength
5632
- });
5633
- const { crc32 } = entry;
5634
- if (crc32 !== UNDEFINED_VALUE) {
5635
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
5636
- }
5637
- const { offset, diskNumberStart } = entryPositions.get(entry);
5638
- Object.assign(entry, {
5639
- zip64UncompressedSize,
5640
- zip64CompressedSize,
5641
- offset,
5642
- diskNumberStart,
5643
- zip64DiskNumberStart: false,
5644
- rawExtraFieldZip64,
5645
- rawExtraFieldAES,
5646
- rawExtraFieldExtendedTimestamp,
5647
- rawExtraFieldNTFS,
5648
- rawExtraFieldUnix,
5649
- rawExtraField,
5650
- extendedTimestamp: rawExtraFieldExtendedTimestamp.length > 0 || rawExtraFieldNTFS.length > 0,
5651
- extraFieldExtendedTimestampFlag: 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0),
5652
- headerArray,
5653
- headerView
5654
- });
5655
- return [entry.filename, entry];
5656
- }));
5733
+ prependZip(reader) {
5734
+ return watchPromiseError(this, prependZipEntries(this, reader));
5657
5735
  }
5658
5736
 
5659
- async add(name = "", reader, options = {}) {
5737
+ appendZip(reader) {
5738
+ return watchPromiseError(this, this.appendZipEntries(reader));
5739
+ }
5740
+
5741
+ async appendZipEntries(reader) {
5660
5742
  const zipWriter = this;
5661
- options = Object.assign({}, options);
5662
- const { pendingAddFileCalls } = zipWriter;
5663
- if (workers < getConfiguration().maxWorkers) {
5664
- workers++;
5665
- } else {
5666
- await new Promise(resolve => pendingEntries.push(resolve));
5743
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
5744
+ while (pendingAddFileCalls.size) {
5745
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
5667
5746
  }
5668
- let promiseAddFile;
5669
- let nameAdded;
5747
+ let resolveAppendZip;
5748
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
5749
+ pendingAddFileCalls.add(promiseAppendZip);
5750
+ const appendedFilenames = [];
5751
+ let releaseLockWriter;
5670
5752
  try {
5671
- name = name.trim();
5672
- if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
5673
- name += DIRECTORY_SIGNATURE;
5674
- }
5675
- if (zipWriter.filenames.has(name)) {
5676
- throw new Error(ERR_DUPLICATED_NAME);
5677
- }
5678
- zipWriter.filenames.add(name);
5679
- nameAdded = true;
5680
- promiseAddFile = addFile(zipWriter, name, reader, options);
5681
- pendingAddFileCalls.add(promiseAddFile);
5682
- return await promiseAddFile;
5683
- } catch (error) {
5684
- if (nameAdded) {
5685
- zipWriter.filenames.delete(name);
5753
+ reader = new GenericReader(reader);
5754
+ await initStream(reader);
5755
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
5756
+ reader = new BlobReader(await streamToBlob(reader.readable));
5757
+ await initStream(reader);
5758
+ }
5759
+ const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
5760
+ const zipReader$1 = new ZipReader(reader);
5761
+ const entries = await zipReader$1.getEntries();
5762
+ await zipReader$1.close();
5763
+ await initStream(zipWriter.writer);
5764
+ const { directoryOffset } = zipReader$1;
5765
+ entries.forEach(({ filename }) => {
5766
+ if (filenames.has(filename)) {
5767
+ throw new Error(ERR_DUPLICATED_NAME);
5768
+ }
5769
+ filenames.add(filename);
5770
+ appendedFilenames.push(filename);
5771
+ });
5772
+ zipWriter.writerLocked = true;
5773
+ const { lockWriter } = zipWriter;
5774
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
5775
+ zipWriter.writerLocked = false;
5776
+ resolve();
5777
+ });
5778
+ await lockWriter;
5779
+ if (zipWriter.addSplitZipSignature) {
5780
+ delete zipWriter.addSplitZipSignature;
5781
+ if (!await startsWithSplitZipSignature(reader)) {
5782
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
5783
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
5784
+ }
5686
5785
  }
5786
+ const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
5787
+ entries.forEach(entry => {
5788
+ const {
5789
+ version,
5790
+ rawLastModDate,
5791
+ rawFilename,
5792
+ bitFlag,
5793
+ encrypted,
5794
+ uncompressedSize,
5795
+ compressedSize,
5796
+ extraFieldZip64
5797
+ } = entry;
5798
+ let {
5799
+ compressionMethod,
5800
+ rawExtraField,
5801
+ } = entry;
5802
+ const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
5803
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
5804
+ if (entry.extraFieldAES) {
5805
+ compressionMethod = COMPRESSION_METHOD_AES;
5806
+ }
5807
+ const extraFieldLength = getLength(rawExtraField);
5808
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
5809
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
5810
+ const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
5811
+ const {
5812
+ headerArray,
5813
+ headerView
5814
+ } = getHeaderArrayData({
5815
+ version,
5816
+ bitFlag: bitFlagValue,
5817
+ compressionMethod,
5818
+ uncompressedSize,
5819
+ compressedSize,
5820
+ rawLastModDate,
5821
+ rawFilename,
5822
+ zip64CompressedSize,
5823
+ zip64UncompressedSize,
5824
+ extraFieldLength
5825
+ });
5826
+ const { crc32 } = entry;
5827
+ if (crc32 !== UNDEFINED_VALUE) {
5828
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
5829
+ }
5830
+ const { offset, diskNumberStart } = entryPositions.get(entry);
5831
+ Object.assign(entry, {
5832
+ zip64UncompressedSize,
5833
+ zip64CompressedSize,
5834
+ offset,
5835
+ diskNumberStart,
5836
+ zip64DiskNumberStart: false,
5837
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
5838
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
5839
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
5840
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
5841
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
5842
+ rawExtraField,
5843
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
5844
+ extendedTimestamp: false,
5845
+ headerArray,
5846
+ headerView
5847
+ });
5848
+ fileEntries.set(entry.filename, entry);
5849
+ });
5850
+ } catch (error) {
5851
+ appendedFilenames.forEach(filename => filenames.delete(filename));
5687
5852
  throw error;
5688
5853
  } finally {
5689
- pendingAddFileCalls.delete(promiseAddFile);
5690
- const pendingEntry = pendingEntries.shift();
5691
- if (pendingEntry) {
5692
- pendingEntry();
5693
- } else {
5694
- workers--;
5854
+ resolveAppendZip();
5855
+ pendingAddFileCalls.delete(promiseAppendZip);
5856
+ if (releaseLockWriter) {
5857
+ releaseLockWriter();
5695
5858
  }
5696
5859
  }
5697
5860
  }
5698
5861
 
5862
+ add(name = "", reader, options = {}) {
5863
+ const zipWriter = this;
5864
+ const { pendingAddFileCalls } = zipWriter;
5865
+ const promiseAddFile = addFileEntry(zipWriter, name, reader, options);
5866
+ pendingAddFileCalls.add(promiseAddFile);
5867
+ const deletePendingAddFileCall = () => pendingAddFileCalls.delete(promiseAddFile);
5868
+ Promise.prototype.then.call(promiseAddFile, deletePendingAddFileCall, deletePendingAddFileCall);
5869
+ return watchPromiseError(zipWriter, promiseAddFile);
5870
+ }
5871
+
5699
5872
  remove(entry) {
5700
5873
  const { filenames, fileEntries } = this;
5701
5874
  // deno-lint-ignore valid-typeof
@@ -5726,6 +5899,19 @@
5726
5899
  while (pendingAddFileCalls.size) {
5727
5900
  await Promise.allSettled(Array.from(pendingAddFileCalls));
5728
5901
  }
5902
+ await Promise.allSettled(zipWriter.pendingErrors.map(watcher => watcher.recorded));
5903
+ const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.error && !watcher.observed);
5904
+ if (unobservedWatchers.length) {
5905
+ const unobservedErrors = unobservedWatchers.map(watcher => watcher.error);
5906
+ unobservedWatchers.forEach(watcher => watcher.observed = true);
5907
+ const [error] = unobservedErrors;
5908
+ try {
5909
+ error.entryErrors = unobservedErrors;
5910
+ } catch {
5911
+ // ignored
5912
+ }
5913
+ throw error;
5914
+ }
5729
5915
  await closeFile(zipWriter, comment, options);
5730
5916
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
5731
5917
  if (!preventClose) {
@@ -5735,6 +5921,62 @@
5735
5921
  }
5736
5922
  }
5737
5923
 
5924
+ class WatchedPromise extends Promise {
5925
+
5926
+ then(onFulfilled, onRejected) {
5927
+ const { watcher } = this;
5928
+ if (watcher) {
5929
+ watcher.observed = true;
5930
+ }
5931
+ return super.then(onFulfilled, onRejected);
5932
+ }
5933
+ }
5934
+
5935
+ function watchPromiseError(zipWriter, promise) {
5936
+ const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
5937
+ const watcher = {};
5938
+ watchedPromise.watcher = watcher;
5939
+ watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE, error => watcher.error = error);
5940
+ zipWriter.pendingErrors.push(watcher);
5941
+ return watchedPromise;
5942
+ }
5943
+
5944
+ async function prependZipEntries(zipWriter, reader) {
5945
+ if (zipWriter.filenames.size) {
5946
+ throw new Error(ERR_ZIP_NOT_EMPTY);
5947
+ }
5948
+ await zipWriter.appendZipEntries(reader);
5949
+ }
5950
+
5951
+ async function addFileEntry(zipWriter, name, reader, options) {
5952
+ options = Object.assign({}, options);
5953
+ if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
5954
+ name += DIRECTORY_SIGNATURE;
5955
+ }
5956
+ if (zipWriter.filenames.has(name)) {
5957
+ throw new Error(ERR_DUPLICATED_NAME);
5958
+ }
5959
+ zipWriter.filenames.add(name);
5960
+ if (workers < getConfiguration().maxWorkers) {
5961
+ workers++;
5962
+ } else {
5963
+ await new Promise(resolve => pendingEntries.push(resolve));
5964
+ }
5965
+ try {
5966
+ return await addFile(zipWriter, name, reader, options);
5967
+ } catch (error) {
5968
+ zipWriter.filenames.delete(name);
5969
+ throw error;
5970
+ } finally {
5971
+ const pendingEntry = pendingEntries.shift();
5972
+ if (pendingEntry) {
5973
+ pendingEntry();
5974
+ } else {
5975
+ workers--;
5976
+ }
5977
+ }
5978
+ }
5979
+
5738
5980
  async function addFile(zipWriter, name, reader, options) {
5739
5981
  const attributesInfo = resolveAttributes(zipWriter, name, options);
5740
5982
  ({ name } = attributesInfo);
@@ -5779,7 +6021,6 @@
5779
6021
  }
5780
6022
 
5781
6023
  function resolveAttributes(zipWriter, name, options) {
5782
- name = name.trim();
5783
6024
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
5784
6025
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
5785
6026
  const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
@@ -5923,11 +6164,12 @@
5923
6164
  if (getLength(rawComment) > MAX_16_BITS) {
5924
6165
  throw new Error(ERR_INVALID_ENTRY_COMMENT);
5925
6166
  }
5926
- const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION, VERSION_DEFLATE);
5927
- if (version > MAX_16_BITS) {
6167
+ const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION);
6168
+ if (version !== UNDEFINED_VALUE && version > MAX_16_BITS) {
5928
6169
  throw new Error(ERR_INVALID_VERSION);
5929
6170
  }
5930
6171
  const lastModDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE, new Date());
6172
+ const rawLastModDate = getOptionValue(zipWriter, options, PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE);
5931
6173
  const lastAccessDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
5932
6174
  const creationDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
5933
6175
  const internalFileAttributes = getAliasedOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES, 0);
@@ -5971,7 +6213,7 @@
5971
6213
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
5972
6214
  dataDescriptor = false;
5973
6215
  }
5974
- if (dataDescriptor === UNDEFINED_VALUE || zipCrypto) {
6216
+ if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThrough)) {
5975
6217
  dataDescriptor = true;
5976
6218
  }
5977
6219
  if (level !== UNDEFINED_VALUE && level != 6) {
@@ -5983,6 +6225,7 @@
5983
6225
  }
5984
6226
  const rawExtraField = serializeExtraField(options[PROPERTY_NAME_EXTRA_FIELD]);
5985
6227
  const rawLocalExtraField = serializeExtraField(options[OPTION_LOCAL_EXTRA_FIELD]);
6228
+ const rawCentralExtraField = serializeExtraField(options[OPTION_CENTRAL_EXTRA_FIELD]);
5986
6229
  return {
5987
6230
  comment,
5988
6231
  resolvedOptions: {
@@ -5990,6 +6233,7 @@
5990
6233
  rawComment,
5991
6234
  version,
5992
6235
  lastModDate,
6236
+ rawLastModDate,
5993
6237
  lastAccessDate,
5994
6238
  creationDate,
5995
6239
  internalFileAttributes,
@@ -6017,7 +6261,8 @@
6017
6261
  dataDescriptor,
6018
6262
  zip64,
6019
6263
  rawExtraField,
6020
- rawLocalExtraField
6264
+ rawLocalExtraField,
6265
+ rawCentralExtraField
6021
6266
  }
6022
6267
  };
6023
6268
  }
@@ -6159,7 +6404,6 @@
6159
6404
  let { name } = entry;
6160
6405
  const { size } = entry;
6161
6406
  const options = Object.assign({}, entry.options);
6162
- name = name.trim();
6163
6407
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
6164
6408
  name += DIRECTORY_SIGNATURE;
6165
6409
  }
@@ -6265,10 +6509,8 @@
6265
6509
  }
6266
6510
  await initStream(fileWriter);
6267
6511
  const diskOffset = getDiskOffset(writer);
6268
- if (zipWriter.addSplitZipSignature) {
6269
- delete zipWriter.addSplitZipSignature;
6270
- await writeData(writer, getSplitZipSignatureArray());
6271
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6512
+ if (zipWriter.addSplitZipSignature && !bufferedWrite) {
6513
+ await writeSplitZipSignature(zipWriter, writer);
6272
6514
  }
6273
6515
  if (usdz && !bufferedWrite) {
6274
6516
  appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
@@ -6295,6 +6537,9 @@
6295
6537
  if (bufferedWrite) {
6296
6538
  await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
6297
6539
  await requestLockWriter();
6540
+ if (zipWriter.addSplitZipSignature) {
6541
+ await writeSplitZipSignature(zipWriter, writer);
6542
+ }
6298
6543
  writingBufferedEntryData = true;
6299
6544
  writerSizeBeforeEntry = writer.size;
6300
6545
  await skipDiskIfNeeded();
@@ -6414,6 +6659,7 @@
6414
6659
  versionMadeBy,
6415
6660
  rawComment,
6416
6661
  rawExtraField,
6662
+ rawCentralExtraField,
6417
6663
  useWebWorkers,
6418
6664
  transferStreams,
6419
6665
  onstart,
@@ -6457,6 +6703,7 @@
6457
6703
  rawExtraFieldUnix,
6458
6704
  rawExtraFieldAES,
6459
6705
  rawExtraField,
6706
+ rawCentralExtraField,
6460
6707
  extendedTimestamp,
6461
6708
  msDosCompatible,
6462
6709
  internalFileAttributes,
@@ -6566,6 +6813,7 @@
6566
6813
  const {
6567
6814
  rawFilename,
6568
6815
  lastModDate,
6816
+ rawLastModDate: rawLastModDateOption,
6569
6817
  lastAccessDate,
6570
6818
  creationDate,
6571
6819
  level,
@@ -6699,6 +6947,9 @@
6699
6947
  if (compressionMethod === UNDEFINED_VALUE) {
6700
6948
  compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
6701
6949
  }
6950
+ if (version === UNDEFINED_VALUE) {
6951
+ version = compressionMethod == COMPRESSION_METHOD_STORE && !directory && !encrypted ? VERSION_STORE : VERSION_DEFLATE;
6952
+ }
6702
6953
  const { codecVersionNeeded } = options;
6703
6954
  if (compressed && codecVersionNeeded !== UNDEFINED_VALUE) {
6704
6955
  version = version > codecVersionNeeded ? version : codecVersionNeeded;
@@ -6731,6 +6982,7 @@
6731
6982
  compressionMethod,
6732
6983
  uncompressedSize,
6733
6984
  lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
6985
+ rawLastModDate: rawLastModDateOption,
6734
6986
  rawFilename,
6735
6987
  zip64CompressedSize,
6736
6988
  zip64UncompressedSize,
@@ -6937,9 +7189,9 @@
6937
7189
 
6938
7190
  async function closeFile(zipWriter, comment, options) {
6939
7191
  const directoryDataLength = createDirectoryRecords(zipWriter.fileEntries);
6940
- const { directoryStart, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
7192
+ const { directoryStart, directoryEnd, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
6941
7193
  const signatureLength = await writeDigitalSignatureRecord(zipWriter, directoryArray, options);
6942
- await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryDataLength, signatureLength });
7194
+ await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength });
6943
7195
  }
6944
7196
 
6945
7197
  function createDirectoryRecords(files) {
@@ -6952,6 +7204,7 @@
6952
7204
  rawExtraFieldNTFS,
6953
7205
  rawExtraFieldUnix,
6954
7206
  rawExtraField,
7207
+ rawCentralExtraField,
6955
7208
  extendedTimestamp,
6956
7209
  extraFieldExtendedTimestampFlag,
6957
7210
  lastModDate,
@@ -7006,7 +7259,8 @@
7006
7259
  rawExtraFieldNTFS,
7007
7260
  rawExtraFieldUnix,
7008
7261
  rawExtraFieldTimestamp,
7009
- rawExtraField);
7262
+ rawExtraField,
7263
+ rawCentralExtraField);
7010
7264
  if (extraFieldLength > MAX_16_BITS) {
7011
7265
  throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
7012
7266
  }
@@ -7023,6 +7277,7 @@
7023
7277
  let directoryDiskOffset = 0;
7024
7278
  let directoryStartDiskNumber = getDiskNumber(writer);
7025
7279
  let directoryStartDiskOffset = getDiskOffset(writer);
7280
+ let directoryEndDiskEntriesLength = 0;
7026
7281
  for (const [indexFileEntry, fileEntry] of Array.from(fileEntries.values()).entries()) {
7027
7282
  const {
7028
7283
  offset: fileEntryOffset,
@@ -7033,6 +7288,7 @@
7033
7288
  rawExtraFieldNTFS,
7034
7289
  rawExtraFieldUnix,
7035
7290
  rawExtraField,
7291
+ rawCentralExtraField,
7036
7292
  rawComment,
7037
7293
  versionMadeBy,
7038
7294
  headerArray,
@@ -7047,11 +7303,12 @@
7047
7303
  uncompressedSize,
7048
7304
  compressedSize
7049
7305
  } = fileEntry;
7050
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
7306
+ const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField, rawCentralExtraField);
7051
7307
  const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
7052
7308
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
7053
7309
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
7054
7310
  directoryDiskOffset = offset;
7311
+ directoryEndDiskEntriesLength = 0;
7055
7312
  await writer.closeDisk();
7056
7313
  }
7057
7314
  if (indexFileEntry == 0) {
@@ -7084,9 +7341,11 @@
7084
7341
  directoryRecord.writeBytes(rawExtraFieldNTFS);
7085
7342
  directoryRecord.writeBytes(rawExtraFieldUnix);
7086
7343
  directoryRecord.writeBytes(rawExtraField);
7344
+ directoryRecord.writeBytes(rawCentralExtraField);
7087
7345
  directoryRecord.writeBytes(rawComment);
7088
7346
  arraySet(directoryArray, directoryRecord.array, offset);
7089
7347
  offset += directoryRecordLength;
7348
+ directoryEndDiskEntriesLength++;
7090
7349
  if (options.onprogress) {
7091
7350
  try {
7092
7351
  await options.onprogress(indexFileEntry + 1, fileEntries.size, new Entry(fileEntry));
@@ -7098,6 +7357,7 @@
7098
7357
  await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
7099
7358
  return {
7100
7359
  directoryStart: { diskNumber: directoryStartDiskNumber, diskOffset: directoryStartDiskOffset },
7360
+ directoryEnd: { diskNumber: getDiskNumber(writer), entriesLength: directoryEndDiskEntriesLength },
7101
7361
  directoryArray
7102
7362
  };
7103
7363
  }
@@ -7126,16 +7386,20 @@
7126
7386
 
7127
7387
  async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
7128
7388
  const { writer } = zipWriter;
7129
- const { directoryStart, signatureLength } = cdInfo;
7389
+ const { directoryStart, directoryEnd, signatureLength } = cdInfo;
7130
7390
  let { directoryDataLength } = cdInfo;
7131
7391
  let fileEntriesLength = zipWriter.fileEntries.size;
7132
7392
  let diskNumber = directoryStart.diskNumber;
7133
7393
  let directoryOffset = getSegmentOffset(zipWriter, directoryStart);
7394
+ const commentLength = getLength(comment);
7395
+ if (commentLength > MAX_16_BITS) {
7396
+ throw new Error(ERR_INVALID_COMMENT);
7397
+ }
7398
+ let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
7134
7399
  let lastDiskNumber = getDiskNumber(writer);
7135
- if (exceedsAvailableSize(writer, END_OF_CENTRAL_DIR_LENGTH)) {
7400
+ if (exceedsAvailableSize(writer, (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH) + commentLength)) {
7136
7401
  lastDiskNumber++;
7137
7402
  }
7138
- let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
7139
7403
  if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || fileEntriesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
7140
7404
  if (zip64 === false) {
7141
7405
  throw new Error(ERR_UNSUPPORTED_FORMAT);
@@ -7143,15 +7407,12 @@
7143
7407
  zip64 = true;
7144
7408
  }
7145
7409
  }
7146
- const commentLength = getLength(comment);
7147
- if (commentLength > MAX_16_BITS) {
7148
- throw new Error(ERR_INVALID_COMMENT);
7149
- }
7150
7410
  const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
7151
7411
  if (exceedsAvailableSize(writer, getLength(endOfdirectoryRecord.array) + commentLength)) {
7152
7412
  await writer.closeDisk();
7153
7413
  }
7154
7414
  lastDiskNumber = getDiskNumber(writer);
7415
+ let diskFileEntriesLength = lastDiskNumber == directoryEnd.diskNumber ? directoryEnd.entriesLength : 0;
7155
7416
  if (zip64) {
7156
7417
  endOfdirectoryRecord.writeUint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
7157
7418
  endOfdirectoryRecord.writeUint64(44);
@@ -7159,7 +7420,7 @@
7159
7420
  endOfdirectoryRecord.writeUint16(45);
7160
7421
  endOfdirectoryRecord.writeUint32(lastDiskNumber);
7161
7422
  endOfdirectoryRecord.writeUint32(diskNumber);
7162
- endOfdirectoryRecord.writeUint64(fileEntriesLength);
7423
+ endOfdirectoryRecord.writeUint64(diskFileEntriesLength);
7163
7424
  endOfdirectoryRecord.writeUint64(fileEntriesLength);
7164
7425
  endOfdirectoryRecord.writeUint64(directoryDataLength);
7165
7426
  endOfdirectoryRecord.writeUint64(directoryOffset);
@@ -7172,6 +7433,7 @@
7172
7433
  lastDiskNumber = MAX_16_BITS;
7173
7434
  diskNumber = MAX_16_BITS;
7174
7435
  }
7436
+ diskFileEntriesLength = MAX_16_BITS;
7175
7437
  fileEntriesLength = MAX_16_BITS;
7176
7438
  directoryOffset = MAX_32_BITS;
7177
7439
  directoryDataLength = MAX_32_BITS;
@@ -7179,7 +7441,7 @@
7179
7441
  endOfdirectoryRecord.writeUint32(END_OF_CENTRAL_DIR_SIGNATURE);
7180
7442
  endOfdirectoryRecord.writeUint16(lastDiskNumber);
7181
7443
  endOfdirectoryRecord.writeUint16(diskNumber);
7182
- endOfdirectoryRecord.writeUint16(fileEntriesLength);
7444
+ endOfdirectoryRecord.writeUint16(diskFileEntriesLength);
7183
7445
  endOfdirectoryRecord.writeUint16(fileEntriesLength);
7184
7446
  endOfdirectoryRecord.writeUint32(directoryDataLength);
7185
7447
  endOfdirectoryRecord.writeUint32(directoryOffset);
@@ -7278,9 +7540,21 @@
7278
7540
  async function copyData(zipWriter, reader, offset, size) {
7279
7541
  if (size > 0) {
7280
7542
  const { writer } = zipWriter;
7281
- await createReadable(reader, { offset, size }).pipeTo(writer.writable, { preventClose: true, preventAbort: true });
7282
- writer.size += size;
7283
- zipWriter.offset += size;
7543
+ let copiedLength = 0;
7544
+ try {
7545
+ await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
7546
+ } catch (error) {
7547
+ zipWriter.hasCorruptedEntries = true;
7548
+ try {
7549
+ error.corruptedEntry = true;
7550
+ } catch {
7551
+ // ignored
7552
+ }
7553
+ throw error;
7554
+ } finally {
7555
+ writer.size += copiedLength;
7556
+ zipWriter.offset += copiedLength;
7557
+ }
7284
7558
  }
7285
7559
  }
7286
7560
 
@@ -7305,6 +7579,12 @@
7305
7579
  return signatureArray;
7306
7580
  }
7307
7581
 
7582
+ async function writeSplitZipSignature(zipWriter, writer) {
7583
+ delete zipWriter.addSplitZipSignature;
7584
+ await writeData(writer, getSplitZipSignatureArray());
7585
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
7586
+ }
7587
+
7308
7588
  async function writeData(writer, array) {
7309
7589
  const { writable } = writer;
7310
7590
  const streamWriter = writable.getWriter();
@@ -7536,6 +7816,7 @@
7536
7816
  const ERR_READABLE_CONSUMED = "Readable stream already consumed";
7537
7817
  const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
7538
7818
  const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
7819
+ const ERR_ZIP_CRYPTO_LAST_MOD_DATE = "The last modification date of an entry encrypted with ZipCrypto cannot be changed when passThrough is set";
7539
7820
  const ERR_ABORT_EXPORT = "zipjs-abort-export";
7540
7821
  const ERR_ABORTED = "The operation was aborted";
7541
7822
  const ABORT_ERROR_NAME = "AbortError";
@@ -8403,6 +8684,7 @@
8403
8684
  versionMadeBy,
8404
8685
  comment,
8405
8686
  lastModDate,
8687
+ rawLastModDate,
8406
8688
  creationDate,
8407
8689
  lastAccessDate,
8408
8690
  uncompressedSize,
@@ -8413,6 +8695,7 @@
8413
8695
  extraFieldAES,
8414
8696
  internalFileAttributes,
8415
8697
  extraField,
8698
+ bitFlag,
8416
8699
  uid,
8417
8700
  gid
8418
8701
  } = child.data;
@@ -8450,6 +8733,16 @@
8450
8733
  encryptionStrength,
8451
8734
  compressionMethod
8452
8735
  };
8736
+ if (bitFlag) {
8737
+ passThroughOptions.dataDescriptor = bitFlag.dataDescriptor;
8738
+ }
8739
+ const lastModDateOverride = childOptions.lastModDate === UNDEFINED_VALUE ? options.lastModDate : childOptions.lastModDate;
8740
+ if (lastModDateOverride === UNDEFINED_VALUE) {
8741
+ passThroughOptions.rawLastModDate = rawLastModDate;
8742
+ } else if (zipCrypto && (!bitFlag || bitFlag.dataDescriptor) && lastModDateOverride instanceof Date &&
8743
+ getDosTimeHighByte(lastModDateOverride) != ((rawLastModDate >>> 8) & MAX_8_BITS)) {
8744
+ throw new Error(ERR_ZIP_CRYPTO_LAST_MOD_DATE);
8745
+ }
8453
8746
  }
8454
8747
  }
8455
8748
  const entryOptions = Object.assign({ lastModDate: child.defaultLastModDate }, zipEntryMetadata, options, childOptions, passThroughOptions, { directory: child.directory });
@@ -8459,6 +8752,16 @@
8459
8752
  return { name, entryOptions };
8460
8753
  }
8461
8754
 
8755
+ function getDosTimeHighByte(lastModDate) {
8756
+ let dosLastModDate = new Date(Math.ceil(Math.floor(lastModDate.getTime() / 1000) / 2) * 2000);
8757
+ if (dosLastModDate < MIN_DATE) {
8758
+ dosLastModDate = MIN_DATE;
8759
+ } else if (dosLastModDate > MAX_DATE) {
8760
+ dosLastModDate = MAX_DATE;
8761
+ }
8762
+ return ((dosLastModDate.getHours() << 3) | (dosLastModDate.getMinutes() >> 3)) & MAX_8_BITS;
8763
+ }
8764
+
8462
8765
  function getDeterminedSize(child, passThrough) {
8463
8766
  const { reader } = child;
8464
8767
  if (reader && reader.size !== UNDEFINED_VALUE) {
@@ -8802,6 +9105,7 @@
8802
9105
  exports.ERR_INVALID_PASS_THROUGH = ERR_INVALID_PASS_THROUGH;
8803
9106
  exports.ERR_INVALID_READER_OPTIONS = ERR_INVALID_READER_OPTIONS;
8804
9107
  exports.ERR_READABLE_CONSUMED = ERR_READABLE_CONSUMED;
9108
+ exports.ERR_ZIP_CRYPTO_LAST_MOD_DATE = ERR_ZIP_CRYPTO_LAST_MOD_DATE;
8805
9109
  exports.ZipDirectoryEntry = ZipDirectoryEntry;
8806
9110
  exports.ZipEntry = ZipEntry;
8807
9111
  exports.ZipFS = ZipFS;