@zip.js/zip.js 2.8.50 → 2.8.52

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.
@@ -89,6 +89,8 @@
89
89
  const FILE_ATTR_MSDOS_ARCHIVE_MASK = 0x20;
90
90
  const FILE_ATTR_UNIX_TYPE_MASK = 0o170000;
91
91
  const FILE_ATTR_UNIX_TYPE_DIR = 0o040000;
92
+ const FILE_ATTR_UNIX_TYPE_SYMLINK = 0o120000;
93
+ const FILE_ATTR_UNIX_TYPE_FILE = 0o100000;
92
94
  const FILE_ATTR_UNIX_EXECUTABLE_MASK = 0o111;
93
95
  const FILE_ATTR_UNIX_DEFAULT_MASK = 0o644;
94
96
  const FILE_ATTR_UNIX_SETUID_MASK = 0o4000;
@@ -257,8 +259,6 @@
257
259
  */
258
260
 
259
261
 
260
- const HTTP_HEADER_CONTENT_TYPE = "Content-Type";
261
-
262
262
  function toCompatibleReadable(readable) {
263
263
  if (readable instanceof ReadableStream) {
264
264
  return readable;
@@ -281,12 +281,9 @@
281
281
 
282
282
  function streamToBlob(readable, contentType) {
283
283
  readable = toCompatibleReadable(readable);
284
+ const blobOptions = contentType ? { type: contentType } : {};
284
285
  if (responseSupportsGlobalReadable()) {
285
- const options = {};
286
- if (contentType) {
287
- options.headers = [[HTTP_HEADER_CONTENT_TYPE, contentType]];
288
- }
289
- return new Response(readable, options).blob();
286
+ return new Response(readable).blob().then(blob => contentType ? new Blob([blob], blobOptions) : blob);
290
287
  }
291
288
  const chunks = [];
292
289
  return readable
@@ -295,7 +292,7 @@
295
292
  chunks.push(chunk);
296
293
  }
297
294
  }))
298
- .then(() => new Blob(chunks, contentType ? { type: contentType } : {}));
295
+ .then(() => new Blob(chunks, blobOptions));
299
296
  }
300
297
 
301
298
  function responseSupportsGlobalReadable() {
@@ -396,8 +393,10 @@
396
393
  async pull(controller) {
397
394
  const dataSize = size === UNDEFINED_VALUE ? chunkSize : Math.min(chunkSize, size - chunkOffset);
398
395
  const data = await readUint8Array(reader, offset + chunkOffset, dataSize);
399
- controller.enqueue(data);
400
- if ((chunkOffset + chunkSize > size) || (size === UNDEFINED_VALUE && !data.length && dataSize)) {
396
+ if (data.length) {
397
+ controller.enqueue(data);
398
+ }
399
+ if ((chunkOffset + chunkSize >= size) || (size === UNDEFINED_VALUE && !data.length && dataSize)) {
401
400
  controller.close();
402
401
  } else {
403
402
  chunkOffset += chunkSize;
@@ -475,6 +474,7 @@
475
474
  constructor(contentType) {
476
475
  super();
477
476
  Object.assign(this, {
477
+ contentType,
478
478
  data: "data:" + (contentType || "") + ";base64,",
479
479
  pending: []
480
480
  });
@@ -576,6 +576,7 @@
576
576
  return transformStream.writable;
577
577
  }
578
578
  });
579
+ writer.contentType = contentType;
579
580
  writer.blob = streamToBlob(transformStream.readable, contentType);
580
581
  writer.blob.catch(() => { });
581
582
  }
@@ -592,6 +593,26 @@
592
593
  }
593
594
  }
594
595
 
596
+ function getTextSize(text) {
597
+ let size = 0;
598
+ for (let indexCharacter = 0; indexCharacter < text.length; indexCharacter++) {
599
+ const characterCode = text.charCodeAt(indexCharacter);
600
+ if (characterCode < 0x80) {
601
+ size += 1;
602
+ } else if (characterCode < 0x800) {
603
+ size += 2;
604
+ } else if (characterCode < 0xd800 || characterCode >= 0xe000) {
605
+ size += 3;
606
+ } else if (characterCode < 0xdc00 && (text.charCodeAt(indexCharacter + 1) & 0xfc00) == 0xdc00) {
607
+ size += 4;
608
+ indexCharacter++;
609
+ } else {
610
+ size += 3;
611
+ }
612
+ }
613
+ return size;
614
+ }
615
+
595
616
  class TextWriter extends BlobWriter {
596
617
 
597
618
  constructor(encoding) {
@@ -1248,6 +1269,10 @@
1248
1269
  }
1249
1270
  }
1250
1271
 
1272
+ function ownsWritable(writer) {
1273
+ return Boolean(writer && writer.getData);
1274
+ }
1275
+
1251
1276
  function isHttpFamily(url) {
1252
1277
  const { baseURI } = getConfiguration();
1253
1278
  const { protocol } = new URL(url, baseURI);
@@ -3783,6 +3808,8 @@
3783
3808
  const PROPERTY_NAME_RAW_CREATION_DATE = "rawCreationDate";
3784
3809
  const PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES = "internalFileAttributes";
3785
3810
  const PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES = "externalFileAttributes";
3811
+ const PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES = "internalFileAttribute";
3812
+ const PROPERTY_NAME_DEPRECATED_EXTERNAL_FILE_ATTRIBUTES = "externalFileAttribute";
3786
3813
  const PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW = "msdosAttributesRaw";
3787
3814
  const PROPERTY_NAME_MSDOS_ATTRIBUTES = "msdosAttributes";
3788
3815
  const PROPERTY_NAME_MS_DOS_COMPATIBLE = "msDosCompatible";
@@ -3793,6 +3820,7 @@
3793
3820
  const PROPERTY_NAME_ZIPCRYPTO = "zipCrypto";
3794
3821
  const PROPERTY_NAME_DIRECTORY = "directory";
3795
3822
  const PROPERTY_NAME_EXECUTABLE = "executable";
3823
+ const PROPERTY_NAME_SYMLINK = "symlink";
3796
3824
  const PROPERTY_NAME_COMPRESSION_METHOD = "compressionMethod";
3797
3825
  const PROPERTY_NAME_SIGNATURE = "signature";
3798
3826
  const PROPERTY_NAME_CRC32 = "crc32";
@@ -3836,6 +3864,8 @@
3836
3864
  PROPERTY_NAME_DISK_NUMBER_START,
3837
3865
  PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES,
3838
3866
  PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES,
3867
+ PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES,
3868
+ PROPERTY_NAME_DEPRECATED_EXTERNAL_FILE_ATTRIBUTES,
3839
3869
  PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW,
3840
3870
  PROPERTY_NAME_MSDOS_ATTRIBUTES,
3841
3871
  PROPERTY_NAME_MS_DOS_COMPATIBLE,
@@ -3846,6 +3876,7 @@
3846
3876
  PROPERTY_NAME_ZIPCRYPTO,
3847
3877
  PROPERTY_NAME_DIRECTORY,
3848
3878
  PROPERTY_NAME_EXECUTABLE,
3879
+ PROPERTY_NAME_SYMLINK,
3849
3880
  PROPERTY_NAME_COMPRESSION_METHOD,
3850
3881
  PROPERTY_NAME_SIGNATURE,
3851
3882
  PROPERTY_NAME_CRC32,
@@ -4308,15 +4339,18 @@
4308
4339
  const setuid = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_SETUID_MASK);
4309
4340
  const setgid = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_SETGID_MASK);
4310
4341
  const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
4311
- const executable = (fileEntry.unixMode !== UNDEFINED_VALUE)
4342
+ const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
4343
+ const symlink = (unixType & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK;
4344
+ const executable = !symlink && ((fileEntry.unixMode !== UNDEFINED_VALUE)
4312
4345
  ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
4313
- : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0));
4346
+ : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
4314
4347
  const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
4315
4348
  const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
4316
4349
  Object.assign(fileEntry, {
4317
4350
  setuid,
4318
4351
  setgid,
4319
4352
  sticky,
4353
+ symlink,
4320
4354
  unixExternalUpper,
4321
4355
  internalFileAttribute: fileEntry.internalFileAttributes,
4322
4356
  externalFileAttribute: fileEntry.externalFileAttributes,
@@ -4382,6 +4416,10 @@
4382
4416
  }
4383
4417
 
4384
4418
  async close() {
4419
+ const { reader } = this;
4420
+ if (!reader.readUint8Array && reader.readable && !reader.readable.locked) {
4421
+ await reader.readable.cancel();
4422
+ }
4385
4423
  }
4386
4424
  }
4387
4425
 
@@ -4566,7 +4604,7 @@
4566
4604
  throw error;
4567
4605
  }
4568
4606
  } finally {
4569
- const preventClose = getOptionValue$1(zipEntry, options, OPTION_PREVENT_CLOSE);
4607
+ const preventClose = !ownsWritable(writer) && getOptionValue$1(zipEntry, options, OPTION_PREVENT_CLOSE);
4570
4608
  if (!preventClose && writable && !writable.locked) {
4571
4609
  const writableWriter = writable.getWriter();
4572
4610
  if (abortError) {
@@ -4810,8 +4848,8 @@
4810
4848
  return;
4811
4849
  }
4812
4850
  const extraFieldView = getDataView(extraField.data);
4813
- const lastAccessDate = new Date(getUint32(extraFieldView, 0) * 1000);
4814
- const lastModDate = new Date(getUint32(extraFieldView, 4) * 1000);
4851
+ const lastAccessDate = new Date((getUint32(extraFieldView, 0) | 0) * 1000);
4852
+ const lastModDate = new Date((getUint32(extraFieldView, 4) | 0) * 1000);
4815
4853
  const extraFieldData = { lastAccessDate, lastModDate };
4816
4854
  if (extraField.data.length >= 12) {
4817
4855
  extraFieldData.uid = getUint16(extraFieldView, 8);
@@ -5240,6 +5278,7 @@
5240
5278
  const ERR_INVALID_VERSION = "Version exceeds 65535";
5241
5279
  const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
5242
5280
  const ERR_UNSUPPORTED_ENCRYPTION_USDZ = "Encryption is not supported in USDZ files";
5281
+ const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set";
5243
5282
  const ERR_INVALID_EXTRAFIELD_TYPE = "Extra field type exceeds 65535";
5244
5283
  const ERR_INVALID_EXTRAFIELD_DATA = "Extra field data exceeds 64KB";
5245
5284
  const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
@@ -5249,6 +5288,7 @@
5249
5288
  const MAX_NTFS_TIME = BigInt("0x7fffffffffffffff");
5250
5289
  const ERR_UNSUPPORTED_FORMAT = "Zip64 is not supported (set the 'zip64' option to 'true')";
5251
5290
  const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
5291
+ const ERR_UNDETERMINED_SIZE = "Undetermined size";
5252
5292
  const ERR_UNDEFINED_READER = "Undefined reader";
5253
5293
  const ERR_ZIP_NOT_EMPTY = "Zip file not empty";
5254
5294
  const ERR_INVALID_UID = "Invalid uid (must be integer 0..2^32-1)";
@@ -5456,7 +5496,7 @@
5456
5496
  await Promise.allSettled(Array.from(pendingAddFileCalls));
5457
5497
  }
5458
5498
  await closeFile(zipWriter, comment, options);
5459
- const preventClose = getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
5499
+ const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
5460
5500
  if (!preventClose) {
5461
5501
  await writable.getWriter().close();
5462
5502
  }
@@ -5553,7 +5593,7 @@
5553
5593
  if (versionMadeBy > MAX_16_BITS) {
5554
5594
  throw new Error(ERR_INVALID_VERSION);
5555
5595
  }
5556
- let externalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES);
5596
+ let externalFileAttributes = getAliasedOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_DEPRECATED_EXTERNAL_FILE_ATTRIBUTES);
5557
5597
  const externalFileAttributesProvided = externalFileAttributes !== UNDEFINED_VALUE;
5558
5598
  if (!externalFileAttributesProvided) {
5559
5599
  externalFileAttributes = 0;
@@ -5601,7 +5641,9 @@
5601
5641
  }
5602
5642
  if (!externalFileAttributesProvided || unixModeProvided) {
5603
5643
  if (directory) {
5604
- unixMode |= FILE_ATTR_UNIX_TYPE_DIR;
5644
+ unixMode = (unixMode & ~FILE_ATTR_UNIX_TYPE_MASK) | FILE_ATTR_UNIX_TYPE_DIR;
5645
+ } else if (!(unixMode & FILE_ATTR_UNIX_TYPE_MASK)) {
5646
+ unixMode |= FILE_ATTR_UNIX_TYPE_FILE;
5605
5647
  }
5606
5648
  externalFileAttributes = ((unixMode & MAX_16_BITS) << 16) | (externalFileAttributes & MAX_16_BITS);
5607
5649
  }
@@ -5654,15 +5696,12 @@
5654
5696
  const lastModDate = getOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE, new Date());
5655
5697
  const lastAccessDate = getOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
5656
5698
  const creationDate = getOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
5657
- const internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, 0);
5699
+ const internalFileAttributes = getAliasedOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES, 0);
5658
5700
  const passThrough = getOptionValue(zipWriter, options, OPTION_PASS_THROUGH);
5659
- let password, rawPassword;
5660
- if (!passThrough) {
5661
- password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
5662
- rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
5663
- if ((password && typeof password != STRING_TYPE) || (rawPassword && !(rawPassword instanceof Uint8Array))) {
5664
- throw new Error(ERR_INVALID_PASSWORD_TYPE);
5665
- }
5701
+ const password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
5702
+ const rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
5703
+ if ((password && typeof password != STRING_TYPE) || (rawPassword && !(rawPassword instanceof Uint8Array))) {
5704
+ throw new Error(ERR_INVALID_PASSWORD_TYPE);
5666
5705
  }
5667
5706
  const encryptionStrength = getNumberOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
5668
5707
  const zipCrypto = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIPCRYPTO);
@@ -5775,14 +5814,24 @@
5775
5814
  }
5776
5815
 
5777
5816
  async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, options) {
5817
+ if (metadata.passThrough && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
5818
+ throw new Error(ERR_UNDEFINED_READER);
5819
+ }
5820
+ let contentSize;
5821
+ if (reader) {
5822
+ reader = new GenericReader(reader);
5823
+ await initStream(reader);
5824
+ ({ size: contentSize } = reader);
5825
+ }
5826
+ return Object.assign({ reader }, resolveEntrySizes(zipWriter, Boolean(reader), contentSize, metadata, options));
5827
+ }
5828
+
5829
+ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options) {
5778
5830
  const { passThrough, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
5779
5831
  let { dataDescriptor, zip64, level, compressionMethod } = metadata;
5780
5832
  let maximumCompressedSize = 0;
5781
5833
  let uncompressedSize = 0;
5782
- if (passThrough) {
5783
- if (!reader) {
5784
- throw new Error(ERR_UNDEFINED_READER);
5785
- }
5834
+ if (passThrough && hasContent) {
5786
5835
  uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
5787
5836
  if (uncompressedSize === UNDEFINED_VALUE) {
5788
5837
  throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
@@ -5790,29 +5839,30 @@
5790
5839
  }
5791
5840
  const zip64Enabled = zip64 === true;
5792
5841
  const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
5793
- const encryptedEntry = Boolean(reader) && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
5794
- if (!reader) {
5842
+ if (hasContent && passThrough && !encrypted && getLength(password, rawPassword)) {
5843
+ throw new Error(ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH);
5844
+ }
5845
+ const encryptedEntry = hasContent && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
5846
+ if (!hasContent) {
5795
5847
  level = 0;
5796
5848
  compressionMethod = COMPRESSION_METHOD_STORE;
5797
5849
  }
5798
5850
  const encryptionOverhead = encryptedEntry ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
5799
- if (reader) {
5800
- reader = new GenericReader(reader);
5801
- await initStream(reader);
5851
+ if (hasContent) {
5802
5852
  if (!passThrough) {
5803
- if (reader.size === UNDEFINED_VALUE) {
5853
+ if (contentSize === UNDEFINED_VALUE) {
5804
5854
  dataDescriptor = true;
5805
5855
  if (zip64 || zip64 === UNDEFINED_VALUE) {
5806
5856
  zip64 = true;
5807
5857
  uncompressedSize = maximumCompressedSize = MAX_32_BITS + 1;
5808
5858
  }
5809
5859
  } else {
5810
- options.uncompressedSize = uncompressedSize = reader.size;
5811
- maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
5860
+ options.uncompressedSize = uncompressedSize = contentSize;
5861
+ maximumCompressedSize = (isCompressed(compressionMethod, level) ? getMaximumCompressedSize(uncompressedSize) : uncompressedSize) + encryptionOverhead;
5812
5862
  }
5813
5863
  } else {
5814
5864
  options.uncompressedSize = uncompressedSize;
5815
- maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
5865
+ maximumCompressedSize = contentSize === UNDEFINED_VALUE ? getMaximumCompressedSize(uncompressedSize) + encryptionOverhead : contentSize;
5816
5866
  }
5817
5867
  }
5818
5868
  const zip64UncompressedSize = zip64Enabled || uncompressedSize >= MAX_32_BITS;
@@ -5826,7 +5876,7 @@
5826
5876
  }
5827
5877
  zip64 = zip64 || false;
5828
5878
  return {
5829
- reader,
5879
+ maximumCompressedSize,
5830
5880
  resolvedOptions: {
5831
5881
  dataDescriptor,
5832
5882
  zip64,
@@ -5840,6 +5890,78 @@
5840
5890
  };
5841
5891
  }
5842
5892
 
5893
+ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comment) {
5894
+ const zipWriter = { options: writerOptions, config: getConfiguration() };
5895
+ if (writerOptions[OPTION_SIGN_CENTRAL_DIRECTORY]) {
5896
+ throw new Error(ERR_UNDETERMINED_SIZE);
5897
+ }
5898
+ const commentLength = getLength(comment);
5899
+ if (commentLength > MAX_16_BITS) {
5900
+ throw new Error(ERR_INVALID_COMMENT);
5901
+ }
5902
+ const usdz = writerOptions[OPTION_USDZ];
5903
+ const files = new Map();
5904
+ let layoutDependsOnWriteOrder = Boolean(usdz);
5905
+ let offset = 0;
5906
+ for (const entry of entries) {
5907
+ let { name } = entry;
5908
+ const { size } = entry;
5909
+ const options = Object.assign({}, entry.options);
5910
+ name = name.trim();
5911
+ if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
5912
+ name += DIRECTORY_SIGNATURE;
5913
+ }
5914
+ const attributesInfo = resolveAttributes(zipWriter, name, options);
5915
+ ({ name } = attributesInfo);
5916
+ const { resolvedOptions: metadata } = resolveMetadata(zipWriter, name, options);
5917
+ if (metadata.level != 0 && metadata.compressionMethod === UNDEFINED_VALUE &&
5918
+ !metadata.passThrough && !(await supportsDeflate(zipWriter.config))) {
5919
+ metadata.level = 0;
5920
+ }
5921
+ const hasContent = !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY);
5922
+ if (hasContent && size === UNDEFINED_VALUE) {
5923
+ throw new Error(ERR_UNDETERMINED_SIZE);
5924
+ }
5925
+ const { maximumCompressedSize, resolvedOptions: sizes } = resolveEntrySizes(zipWriter, hasContent, size, metadata, options);
5926
+ if (hasContent && !metadata.passThrough && isCompressed(sizes.compressionMethod, sizes.level)) {
5927
+ throw new Error(ERR_UNDETERMINED_SIZE);
5928
+ }
5929
+ const entryOptions = Object.assign({}, options, attributesInfo.resolvedOptions, metadata, sizes);
5930
+ const headerInfo = getHeaderInfo(entryOptions);
5931
+ const dataDescriptorInfo = getDataDescriptorInfo(entryOptions);
5932
+ const entryInfo = {
5933
+ headerInfo,
5934
+ metadataSize: getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray)
5935
+ };
5936
+ if (usdz) {
5937
+ appendExtraFieldUSDZ(entryInfo, offset);
5938
+ }
5939
+ const compressedSize = hasContent ? maximumCompressedSize : 0;
5940
+ if (offset >= MAX_32_BITS) {
5941
+ layoutDependsOnWriteOrder = true;
5942
+ }
5943
+ files.set(name, Object.assign({}, entryOptions, headerInfo, {
5944
+ offset,
5945
+ diskNumberStart: 0,
5946
+ compressedSize
5947
+ }));
5948
+ offset += entryInfo.metadataSize + compressedSize;
5949
+ }
5950
+ if (layoutDependsOnWriteOrder && !writeOrderGuaranteed) {
5951
+ throw new Error(ERR_UNDETERMINED_SIZE);
5952
+ }
5953
+ const directoryDataLength = createDirectoryRecords(files);
5954
+ let zip64 = getOptionValue(zipWriter, writerOptions, PROPERTY_NAME_ZIP64);
5955
+ if (offset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || files.size >= MAX_16_BITS) {
5956
+ if (zip64 === false) {
5957
+ throw new Error(ERR_UNSUPPORTED_FORMAT);
5958
+ } else {
5959
+ zip64 = true;
5960
+ }
5961
+ }
5962
+ return offset + directoryDataLength + commentLength + (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
5963
+ }
5964
+
5843
5965
  async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
5844
5966
  const {
5845
5967
  files,
@@ -6211,9 +6333,7 @@
6211
6333
  crc32
6212
6334
  } = options;
6213
6335
  let { version, compressionMethod } = options;
6214
- const compressed = !directory && (compressionMethod === UNDEFINED_VALUE
6215
- ? (level === UNDEFINED_VALUE || level > 0)
6216
- : compressionMethod !== COMPRESSION_METHOD_STORE);
6336
+ const compressed = !directory && isCompressed(compressionMethod, level);
6217
6337
  let rawLocalExtraFieldZip64;
6218
6338
  const uncompressedFile = passThrough || !compressed;
6219
6339
  const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || uncompressedFile));
@@ -6900,6 +7020,16 @@
6900
7020
  return result === UNDEFINED_VALUE ? defaultValue : result;
6901
7021
  }
6902
7022
 
7023
+ function getAliasedOptionValue(zipWriter, options, name, deprecatedName, defaultValue) {
7024
+ const value = getAliasedValue(options, name, deprecatedName);
7025
+ const result = value === UNDEFINED_VALUE ? getAliasedValue(zipWriter.options, name, deprecatedName) : value;
7026
+ return result === UNDEFINED_VALUE ? defaultValue : result;
7027
+ }
7028
+
7029
+ function getAliasedValue(options, name, deprecatedName) {
7030
+ return options[name] === UNDEFINED_VALUE ? options[deprecatedName] : options[name];
7031
+ }
7032
+
6903
7033
  function getNumberOptionValue(zipWriter, options, name, defaultValue) {
6904
7034
  return toNumber(getOptionValue(zipWriter, options, name, defaultValue));
6905
7035
  }
@@ -6912,6 +7042,12 @@
6912
7042
  return uncompressedSize + (5 * (Math.floor(uncompressedSize / 16383) + 1));
6913
7043
  }
6914
7044
 
7045
+ function isCompressed(compressionMethod, level) {
7046
+ return compressionMethod === UNDEFINED_VALUE
7047
+ ? (level === UNDEFINED_VALUE || level > 0)
7048
+ : compressionMethod !== COMPRESSION_METHOD_STORE;
7049
+ }
7050
+
6915
7051
  function setUint8(view, offset, value) {
6916
7052
  view.setUint8(offset, value);
6917
7053
  }
@@ -7042,7 +7178,10 @@
7042
7178
 
7043
7179
  const ERR_ENTRY_EXISTS = "Entry filename already exists";
7044
7180
  const ERR_READABLE_CONSUMED = "Readable stream already consumed";
7181
+ const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
7045
7182
  const ERR_ABORT_EXPORT = "zipjs-abort-export";
7183
+ const ERR_ABORTED = "The operation was aborted";
7184
+ const ABORT_ERROR_NAME = "AbortError";
7046
7185
  const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
7047
7186
  const INTERPRETED_EXTRA_FIELD_TYPES = new Set([
7048
7187
  EXTRAFIELD_TYPE_ZIP64,
@@ -7077,6 +7216,7 @@
7077
7216
  parent,
7078
7217
  children: [],
7079
7218
  uncompressedSize: params.uncompressedSize || 0,
7219
+ undeterminedSize: params.undeterminedSize || params.uncompressedSize === UNDEFINED_VALUE,
7080
7220
  passThrough: params.passThrough
7081
7221
  });
7082
7222
  if (parent || !fs.root) {
@@ -7144,16 +7284,17 @@
7144
7284
 
7145
7285
  async getData(writer, options = {}) {
7146
7286
  const zipEntry = this;
7147
- if (!writer || (writer.constructor == zipEntry.Writer && zipEntry.data)) {
7287
+ if (!writer || (writer.constructor == zipEntry.Writer && zipEntry.data && keepsContentType(writer, zipEntry.data))) {
7148
7288
  return zipEntry.data;
7149
7289
  } else {
7150
7290
  const reader = zipEntry.reader = createReader(zipEntry.Reader, zipEntry.data, options);
7151
- const uncompressedSize = zipEntry.data ? zipEntry.data.uncompressedSize : reader.size;
7152
- await Promise.all([initStream(reader), initStream(writer, uncompressedSize)]);
7153
- const { readable } = reader;
7291
+ const dataSize = zipEntry.uncompressedSize || reader.size;
7292
+ await Promise.all([initStream(reader), initStream(writer, dataSize)]);
7154
7293
  const { signal } = options;
7294
+ const readable = createProgressReadable(zipEntry, reader, options, signal);
7295
+ const preventClose = !ownsWritable(writer) && Boolean(options.preventClose);
7155
7296
  zipEntry.uncompressedSize = reader.size;
7156
- await toCompatibleReadable(readable).pipeTo(toCompatibleWritable(writer.writable), { signal });
7297
+ await toCompatibleReadable(readable).pipeTo(toCompatibleWritable(writer.writable), { signal, preventClose, preventAbort: preventClose });
7157
7298
  return writer.getData ? writer.getData() : writer.writable;
7158
7299
  }
7159
7300
  }
@@ -7284,7 +7425,7 @@
7284
7425
  Reader: TextReader,
7285
7426
  Writer: TextWriter,
7286
7427
  options,
7287
- uncompressedSize: text.length
7428
+ uncompressedSize: getTextSize(text)
7288
7429
  });
7289
7430
  }
7290
7431
 
@@ -7413,10 +7554,16 @@
7413
7554
  }
7414
7555
 
7415
7556
  async importZip(reader, options = {}) {
7416
- await initStream(reader);
7417
- const zipReader = new ZipReader(reader, options);
7557
+ let zipReader;
7558
+ if (reader && typeof reader.getEntries == FUNCTION_TYPE) {
7559
+ zipReader = reader;
7560
+ options = Object.assign({}, zipReader.options, options);
7561
+ } else {
7562
+ await initStream(reader);
7563
+ zipReader = new ZipReader(reader, options);
7564
+ }
7418
7565
  const importedEntries = [];
7419
- const entries = await zipReader.getEntries();
7566
+ const entries = await zipReader.getEntries(options);
7420
7567
  for (const entry of entries) {
7421
7568
  let parent = this;
7422
7569
  try {
@@ -7438,7 +7585,7 @@
7438
7585
  importedEntries.push(addChild(parent, name, {
7439
7586
  data: entry,
7440
7587
  Reader: getZipBlobReader(Object.assign({}, options)),
7441
- uncompressedSize: entry.uncompressedSize,
7588
+ uncompressedSize: options.passThrough ? entry.compressedSize : entry.uncompressedSize,
7442
7589
  passThrough: options.passThrough
7443
7590
  }));
7444
7591
  } else {
@@ -7480,11 +7627,25 @@
7480
7627
  }
7481
7628
  const [readers] = await Promise.all([initReaders(zipEntry, options.readerOptions), initStream(writer)]);
7482
7629
  const zipWriter = new ZipWriter(writer, options);
7483
- await exportZip(zipWriter, zipEntry, getTotalSize([zipEntry], "uncompressedSize"), options, readers);
7484
- await zipWriter.close();
7630
+ await exportZip(zipWriter, zipEntry, getTotalSize([zipEntry], getUncompressedSize), options, readers);
7631
+ await zipWriter.close(options.globalComment);
7485
7632
  return writer.getData ? writer.getData() : writer.writable;
7486
7633
  }
7487
7634
 
7635
+ getExportedSize(options = {}) {
7636
+ const zipEntry = this;
7637
+ options = Object.assign({}, options);
7638
+ if (options.bufferedWrite === UNDEFINED_VALUE) {
7639
+ options.bufferedWrite = true;
7640
+ }
7641
+ const writeOrderGuaranteed = !options.bufferedWrite ||
7642
+ (options.keepOrder !== false && zipEntry.children.every(child => !child.children.length));
7643
+ return getEntriesSize(options, Array.from(zipEntry.getChildren({ recursive: true }), child => {
7644
+ const { name, entryOptions } = getChildEntryOptions(child, zipEntry, options);
7645
+ return { name, size: child.directory ? 0 : getDeterminedSize(child, isPassThrough(child, options)), options: entryOptions };
7646
+ }), writeOrderGuaranteed, options.globalComment);
7647
+ }
7648
+
7488
7649
  getChildByName(name) {
7489
7650
  const children = this.children;
7490
7651
  for (let childIndex = 0; childIndex < children.length; childIndex++) {
@@ -7495,6 +7656,10 @@
7495
7656
  }
7496
7657
  }
7497
7658
 
7659
+ getChildren(options = {}) {
7660
+ return collectChildren(this, options.recursive);
7661
+ }
7662
+
7498
7663
  isPasswordProtected() {
7499
7664
  const children = this.children;
7500
7665
  for (let childIndex = 0; childIndex < children.length; childIndex++) {
@@ -7586,6 +7751,10 @@
7586
7751
  return this.root.getChildByName(name);
7587
7752
  }
7588
7753
 
7754
+ getChildren(options) {
7755
+ return this.root.getChildren(options);
7756
+ }
7757
+
7589
7758
  addDirectory(name, options) {
7590
7759
  return this.root.addDirectory(name, options);
7591
7760
  }
@@ -7684,6 +7853,10 @@
7684
7853
  return this.root.exportZip(writer, options);
7685
7854
  }
7686
7855
 
7856
+ getExportedSize(options) {
7857
+ return this.root.getExportedSize(options);
7858
+ }
7859
+
7687
7860
  isPasswordProtected() {
7688
7861
  return this.root.isPasswordProtected();
7689
7862
  }
@@ -7695,12 +7868,12 @@
7695
7868
 
7696
7869
  const fs = { FS, ZipDirectoryEntry, ZipFileEntry };
7697
7870
 
7698
- function getTotalSize(entries, propertyName) {
7871
+ function getTotalSize(entries, getEntrySize) {
7699
7872
  let size = 0;
7700
7873
  const pendingEntries = Array.from(entries);
7701
7874
  while (pendingEntries.length) {
7702
7875
  const entry = pendingEntries.pop();
7703
- size += entry[propertyName] || 0;
7876
+ size += getEntrySize(entry) || 0;
7704
7877
  for (const child of entry.children) {
7705
7878
  pendingEntries.push(child);
7706
7879
  }
@@ -7708,6 +7881,15 @@
7708
7881
  return size;
7709
7882
  }
7710
7883
 
7884
+ function getUncompressedSize(entry) {
7885
+ return entry.uncompressedSize;
7886
+ }
7887
+
7888
+ function getExtractedSize(entry, passThrough) {
7889
+ const { data } = entry;
7890
+ return passThrough && data instanceof Entry ? data.compressedSize : entry.uncompressedSize;
7891
+ }
7892
+
7711
7893
  function getReadableReader(readable) {
7712
7894
  let consumed;
7713
7895
  return function () {
@@ -7730,10 +7912,17 @@
7730
7912
 
7731
7913
  async init() {
7732
7914
  const zipBlobReader = this;
7733
- zipBlobReader.size = zipBlobReader.entry.uncompressedSize;
7734
- const data = await zipBlobReader.entry.getData(new BlobWriter(), Object.assign({}, options, zipBlobReader.options));
7915
+ const readerOptions = Object.assign({}, options, zipBlobReader.options);
7916
+ const { checkOverlappingEntry, checkOverlappingEntryOnly } = readerOptions;
7917
+ const data = await zipBlobReader.entry.getData(new BlobWriter(), Object.assign(readerOptions, {
7918
+ checkPasswordOnly: false,
7919
+ checkOverlappingEntry: checkOverlappingEntryOnly || checkOverlappingEntry,
7920
+ checkOverlappingEntryOnly: false,
7921
+ preventClose: false
7922
+ }));
7735
7923
  zipBlobReader.data = data;
7736
7924
  zipBlobReader.blobReader = new BlobReader(data);
7925
+ zipBlobReader.size = data.size;
7737
7926
  super.init();
7738
7927
  }
7739
7928
 
@@ -7747,6 +7936,30 @@
7747
7936
  return Reader.prototype ? new Reader(data, options) : Reader(data, options);
7748
7937
  }
7749
7938
 
7939
+ function keepsContentType(writer, data) {
7940
+ const { contentType } = writer;
7941
+ if (contentType === UNDEFINED_VALUE) {
7942
+ return true;
7943
+ } else if (writer.constructor == BlobWriter) {
7944
+ return data.type == contentType;
7945
+ } else if (writer.constructor == Data64URIWriter) {
7946
+ return data.startsWith(writer.data);
7947
+ } else {
7948
+ return true;
7949
+ }
7950
+ }
7951
+
7952
+ function createProgressReadable(zipEntry, reader, options, signal) {
7953
+ const { onstart, onprogress, onend } = options;
7954
+ const { readable } = reader;
7955
+ const coreReaderReportsProgress = zipEntry.data instanceof Entry;
7956
+ if (coreReaderReportsProgress || (!onstart && !onprogress && !onend)) {
7957
+ return readable;
7958
+ } else {
7959
+ return toCompatibleReadable(readable).pipeThrough(new ProgressWatcherStream({ onstart, onprogress, onend, size: reader.size }), { signal });
7960
+ }
7961
+ }
7962
+
7750
7963
  async function initReaders(entry, options) {
7751
7964
  const fileEntries = [];
7752
7965
  const pendingEntries = [entry];
@@ -7779,6 +7992,7 @@
7779
7992
  }
7780
7993
  if (reader.size !== UNDEFINED_VALUE) {
7781
7994
  child.uncompressedSize = reader.size;
7995
+ child.undeterminedSize = false;
7782
7996
  }
7783
7997
  }));
7784
7998
  return readers;
@@ -7828,6 +8042,90 @@
7828
8042
  return error;
7829
8043
  }
7830
8044
 
8045
+ function getChildEntryOptions(child, selectedEntry, options) {
8046
+ const name = options.relativePath ? child.getRelativeName(selectedEntry) : child.getFullname();
8047
+ const childOptions = child.options || {};
8048
+ let zipEntryMetadata = {};
8049
+ let passThroughOptions = {};
8050
+ if (child.data instanceof Entry) {
8051
+ const {
8052
+ externalFileAttributes,
8053
+ versionMadeBy,
8054
+ comment,
8055
+ lastModDate,
8056
+ creationDate,
8057
+ lastAccessDate,
8058
+ uncompressedSize,
8059
+ encrypted,
8060
+ zipCrypto,
8061
+ crc32,
8062
+ compressionMethod,
8063
+ extraFieldAES,
8064
+ internalFileAttributes,
8065
+ extraField,
8066
+ uid,
8067
+ gid
8068
+ } = child.data;
8069
+ zipEntryMetadata = {
8070
+ externalFileAttributes,
8071
+ versionMadeBy,
8072
+ comment,
8073
+ lastModDate,
8074
+ creationDate,
8075
+ lastAccessDate,
8076
+ internalFileAttributes
8077
+ };
8078
+ const userExtraField = getUserExtraField(extraField);
8079
+ if (userExtraField) {
8080
+ zipEntryMetadata.extraField = userExtraField;
8081
+ }
8082
+ if (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE) {
8083
+ Object.assign(zipEntryMetadata, {
8084
+ uid,
8085
+ gid,
8086
+ unixExtraFieldType: INFOZIP_EXTRA_FIELD_TYPE
8087
+ });
8088
+ }
8089
+ if (isPassThrough(child, options)) {
8090
+ let level, encryptionStrength;
8091
+ if (compressionMethod === 0) {
8092
+ level = 0;
8093
+ }
8094
+ if (extraFieldAES) {
8095
+ encryptionStrength = extraFieldAES.strength;
8096
+ }
8097
+ passThroughOptions = {
8098
+ passThrough: true,
8099
+ encrypted,
8100
+ zipCrypto,
8101
+ crc32,
8102
+ uncompressedSize,
8103
+ level,
8104
+ encryptionStrength,
8105
+ compressionMethod
8106
+ };
8107
+ }
8108
+ }
8109
+ const entryOptions = Object.assign({}, zipEntryMetadata, options, passThroughOptions, childOptions, { directory: child.directory });
8110
+ if (!child.directory && entryOptions.passThrough && entryOptions.uncompressedSize === UNDEFINED_VALUE) {
8111
+ throw new Error(ERR_INVALID_PASS_THROUGH);
8112
+ }
8113
+ return { name, entryOptions };
8114
+ }
8115
+
8116
+ function getDeterminedSize(child, passThrough) {
8117
+ const { reader } = child;
8118
+ if (reader && reader.size !== UNDEFINED_VALUE) {
8119
+ return reader.size;
8120
+ }
8121
+ return child.undeterminedSize ? UNDEFINED_VALUE : getExtractedSize(child, passThrough);
8122
+ }
8123
+
8124
+ function isPassThrough(child, options) {
8125
+ const { readerOptions } = options;
8126
+ return Boolean(!child.directory && (child.passThrough || (readerOptions && readerOptions.passThrough)));
8127
+ }
8128
+
7831
8129
  function getUserExtraField(extraField) {
7832
8130
  if (extraField) {
7833
8131
  const userExtraField = new Map();
@@ -7843,104 +8141,53 @@
7843
8141
  }
7844
8142
 
7845
8143
  async function exportZip(zipWriter, entry, totalSize, options, readers) {
8144
+ const { onstart, onprogress, onend, onentryprogress } = options;
7846
8145
  const selectedEntry = entry;
7847
- const entryOffsets = new Map();
7848
- await process(zipWriter, entry);
7849
-
7850
- async function process(zipWriter, entry) {
7851
- await exportChild();
7852
-
7853
- async function exportChild() {
7854
- if (options.bufferedWrite) {
7855
- const results = await Promise.allSettled(entry.children.map(processChild));
7856
- const errorResult = results.find(result => result.status == "rejected");
7857
- if (errorResult) {
7858
- throw errorResult.reason;
7859
- }
7860
- } else {
7861
- for (const child of entry.children) {
7862
- await processChild(child);
7863
- }
7864
- }
8146
+ const totalEntries = getTotalSize(entry.children, () => 1);
8147
+ let writtenSize = 0;
8148
+ let writtenEntries = 0;
8149
+ if (onstart) {
8150
+ await callHandler(onstart, totalSize);
8151
+ }
8152
+ if (options.bufferedWrite) {
8153
+ await processChildren(entry);
8154
+ } else {
8155
+ for (const child of entry.getChildren({ recursive: true })) {
8156
+ await addChild(child);
7865
8157
  }
8158
+ }
8159
+ if (onend) {
8160
+ await callHandler(onend, writtenSize);
8161
+ }
7866
8162
 
7867
- async function processChild(child) {
7868
- const name = options.relativePath ? child.getRelativeName(selectedEntry) : child.getFullname();
7869
- const childOptions = child.options || {};
7870
- let zipEntryOptions = {};
7871
- if (child.data instanceof Entry) {
7872
- const {
7873
- externalFileAttributes,
7874
- versionMadeBy,
7875
- comment,
7876
- lastModDate,
7877
- creationDate,
7878
- lastAccessDate,
7879
- uncompressedSize,
7880
- encrypted,
7881
- zipCrypto,
7882
- crc32,
7883
- compressionMethod,
7884
- extraFieldAES,
7885
- internalFileAttributes,
7886
- extraField,
7887
- uid,
7888
- gid
7889
- } = child.data;
7890
- zipEntryOptions = {
7891
- externalFileAttributes,
7892
- versionMadeBy,
7893
- comment,
7894
- lastModDate,
7895
- creationDate,
7896
- lastAccessDate,
7897
- internalFileAttributes
7898
- };
7899
- const userExtraField = getUserExtraField(extraField);
7900
- if (userExtraField) {
7901
- zipEntryOptions.extraField = userExtraField;
7902
- }
7903
- if (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE) {
7904
- Object.assign(zipEntryOptions, {
7905
- uid,
7906
- gid,
7907
- unixExtraFieldType: options.unixExtraFieldType || INFOZIP_EXTRA_FIELD_TYPE
7908
- });
7909
- }
7910
- if (child.passThrough) {
7911
- let level, encryptionStrength;
7912
- if (compressionMethod === 0) {
7913
- level = 0;
7914
- }
7915
- if (extraFieldAES) {
7916
- encryptionStrength = extraFieldAES.strength;
7917
- }
7918
- zipEntryOptions = Object.assign(zipEntryOptions, {
7919
- passThrough: true,
7920
- encrypted,
7921
- zipCrypto,
7922
- crc32,
7923
- uncompressedSize,
7924
- level,
7925
- encryptionStrength,
7926
- compressionMethod
7927
- });
8163
+ async function processChildren(entry) {
8164
+ const results = await Promise.allSettled(entry.children.map(async child => {
8165
+ await addChild(child);
8166
+ await processChildren(child);
8167
+ }));
8168
+ const errorResult = results.find(result => result.status == "rejected");
8169
+ if (errorResult) {
8170
+ throw errorResult.reason;
8171
+ }
8172
+ }
8173
+
8174
+ async function addChild(child) {
8175
+ const { name, entryOptions } = getChildEntryOptions(child, selectedEntry, options);
8176
+ let entryWrittenSize = 0;
8177
+ const entryMetadata = await zipWriter.add(name, readers.get(child), Object.assign(entryOptions, {
8178
+ onstart: UNDEFINED_VALUE,
8179
+ onend: UNDEFINED_VALUE,
8180
+ onprogress: async indexProgress => {
8181
+ writtenSize += indexProgress - entryWrittenSize;
8182
+ entryWrittenSize = indexProgress;
8183
+ if (onprogress) {
8184
+ await callHandler(onprogress, writtenSize, totalSize);
7928
8185
  }
7929
8186
  }
7930
- await zipWriter.add(name, readers.get(child), Object.assign({}, options, zipEntryOptions, childOptions, {
7931
- directory: child.directory,
7932
- onprogress: async indexProgress => {
7933
- if (options.onprogress) {
7934
- entryOffsets.set(name, indexProgress);
7935
- try {
7936
- await options.onprogress(Array.from(entryOffsets.values()).reduce((previousValue, currentValue) => previousValue + currentValue), totalSize);
7937
- } catch {
7938
- // ignored
7939
- }
7940
- }
7941
- }
7942
- }));
7943
- await process(zipWriter, child);
8187
+ }));
8188
+ writtenEntries++;
8189
+ if (onentryprogress) {
8190
+ await callHandler(onentryprogress, writtenEntries, totalEntries, entryMetadata);
7944
8191
  }
7945
8192
  }
7946
8193
  }
@@ -7969,7 +8216,7 @@
7969
8216
  })
7970
8217
  );
7971
8218
  } else if (handle.kind == "directory") {
7972
- const directoryEntry = parentEntry.addDirectory(handle.name);
8219
+ const directoryEntry = parentEntry.addDirectory(handle.name, options);
7973
8220
  addedEntries.push(directoryEntry);
7974
8221
  for await (const childHandle of handle.values()) {
7975
8222
  await addFile(directoryEntry, childHandle, addedEntries, entryName);
@@ -7991,14 +8238,29 @@
7991
8238
  }
7992
8239
 
7993
8240
  async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
7994
- const totalSize = getTotalSize([zipEntry], "uncompressedSize");
7995
- const writtenSizes = new Map();
8241
+ const { onstart, onprogress, onend } = options;
7996
8242
  const abortController = new AbortController();
7997
8243
  const { signal } = abortController;
7998
8244
  const releaseSignal = forwardAbort(options.signal, abortController);
8245
+ const getDataOptions = Object.assign({}, options, options.readerOptions, {
8246
+ signal,
8247
+ onstart: UNDEFINED_VALUE,
8248
+ onprogress: UNDEFINED_VALUE,
8249
+ onend: UNDEFINED_VALUE,
8250
+ preventClose: false
8251
+ });
8252
+ const totalSize = getTotalSize([zipEntry], entry => getExtractedSize(entry, getDataOptions.passThrough));
7999
8253
  const exportedEntryNames = [];
8254
+ let exportAborted = false;
8255
+ let writtenSize = 0;
8000
8256
  try {
8257
+ if (onstart) {
8258
+ await callHandler(onstart, totalSize);
8259
+ }
8001
8260
  await exportChildren(zipEntry, directoryHandle);
8261
+ if (onend) {
8262
+ await callHandler(onend, writtenSize);
8263
+ }
8002
8264
  } catch (error) {
8003
8265
  try {
8004
8266
  error.exportedEntryNames = exportedEntryNames;
@@ -8011,6 +8273,25 @@
8011
8273
  }
8012
8274
  return directoryHandle;
8013
8275
 
8276
+ function createProgressWritable(writable) {
8277
+ const writer = writable.getWriter();
8278
+ return new WritableStream({
8279
+ async write(chunk) {
8280
+ await writer.write(chunk);
8281
+ writtenSize += chunk.length;
8282
+ if (onprogress) {
8283
+ await callHandler(onprogress, writtenSize, totalSize);
8284
+ }
8285
+ },
8286
+ close() {
8287
+ return writer.close();
8288
+ },
8289
+ abort(reason) {
8290
+ return writer.abort(reason);
8291
+ }
8292
+ });
8293
+ }
8294
+
8014
8295
  async function exportChildren(entry, parentHandle) {
8015
8296
  if (options.concurrent) {
8016
8297
  const results = await Promise.allSettled(entry.children.map(child => exportChild(child, parentHandle)));
@@ -8029,10 +8310,10 @@
8029
8310
 
8030
8311
  async function exportChild(child, parentHandle) {
8031
8312
  if (signal.aborted) {
8032
- if (isExportAborted(signal.reason)) {
8313
+ if (exportAborted || isExportAborted(signal.reason)) {
8033
8314
  return;
8034
8315
  }
8035
- throw signal.reason;
8316
+ throw signal.reason === UNDEFINED_VALUE ? new DOMException(ERR_ABORTED, ABORT_ERROR_NAME) : signal.reason;
8036
8317
  }
8037
8318
  try {
8038
8319
  if (child.directory) {
@@ -8041,22 +8322,15 @@
8041
8322
  } else {
8042
8323
  const fileHandle = await parentHandle.getFileHandle(child.name, { create: true });
8043
8324
  const writable = await fileHandle.createWritable();
8044
- await child.getData({ writable }, Object.assign({}, options, {
8045
- signal,
8046
- onprogress: async progress => {
8047
- if (options.onprogress) {
8048
- writtenSizes.set(child.id, progress);
8049
- try {
8050
- await options.onprogress(Array.from(writtenSizes.values()).reduce((previousValue, currentValue) => previousValue + currentValue, 0), totalSize);
8051
- } catch {
8052
- // ignored
8053
- }
8054
- }
8055
- }
8056
- }));
8325
+ try {
8326
+ await child.getData({ writable: createProgressWritable(writable) }, getDataOptions);
8327
+ } catch (error) {
8328
+ throw exportAborted ? new Error(ERR_ABORT_EXPORT) : error;
8329
+ }
8057
8330
  exportedEntryNames.push(child.getRelativeName(zipEntry));
8058
8331
  }
8059
8332
  } catch (error) {
8333
+ exportAborted = true;
8060
8334
  abortController.abort(new Error(ERR_ABORT_EXPORT));
8061
8335
  try {
8062
8336
  if (error.entryName === UNDEFINED_VALUE) {
@@ -8128,6 +8402,21 @@
8128
8402
  fs.root = new ZipDirectoryEntry(fs);
8129
8403
  }
8130
8404
 
8405
+ function collectChildren(directory, recursive) {
8406
+ const children = [];
8407
+ const pendingDirectories = [directory];
8408
+ let directoryIndex = 0;
8409
+ while (directoryIndex < pendingDirectories.length) {
8410
+ for (const child of pendingDirectories[directoryIndex++].children) {
8411
+ children.push(child);
8412
+ if (recursive) {
8413
+ pendingDirectories.push(child);
8414
+ }
8415
+ }
8416
+ }
8417
+ return children;
8418
+ }
8419
+
8131
8420
  function registerEntries(fs, entry) {
8132
8421
  const pendingEntries = [entry];
8133
8422
  while (pendingEntries.length) {
@@ -8147,7 +8436,9 @@
8147
8436
  }
8148
8437
  }
8149
8438
 
8439
+ exports.ERR_ABORTED = ERR_ABORTED;
8150
8440
  exports.ERR_ENTRY_EXISTS = ERR_ENTRY_EXISTS;
8441
+ exports.ERR_INVALID_PASS_THROUGH = ERR_INVALID_PASS_THROUGH;
8151
8442
  exports.ERR_READABLE_CONSUMED = ERR_READABLE_CONSUMED;
8152
8443
  exports.fs = fs;
8153
8444