@zip.js/zip.js 2.8.57 → 2.8.58

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.
@@ -129,6 +129,7 @@ import {
129
129
  FILE_ATTR_UNIX_SETUID_MASK,
130
130
  FILE_ATTR_UNIX_SETGID_MASK,
131
131
  FILE_ATTR_UNIX_STICKY_MASK,
132
+ VERSION_STORE,
132
133
  VERSION_DEFLATE,
133
134
  VERSION_MADE_BY_MSDOS,
134
135
  VERSION_MADE_BY_UNIX,
@@ -174,6 +175,7 @@ import { concat, getDataView } from "./util/array.js";
174
175
  import { toCompatibleReadable, streamToBlob } from "./util/compatible-streams.js";
175
176
  import {
176
177
  PROPERTY_NAME_LAST_MODIFICATION_DATE,
178
+ PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE,
177
179
  PROPERTY_NAME_LAST_ACCESS_DATE,
178
180
  PROPERTY_NAME_CREATION_DATE,
179
181
  PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES,
@@ -305,153 +307,150 @@ class ZipWriter {
305
307
  offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
306
308
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
307
309
  pendingAddFileCalls: new Set(),
310
+ pendingErrors: [],
308
311
  bufferedWrites: 0,
309
312
  lastFileEntry: UNDEFINED_VALUE
310
313
  });
311
314
  }
312
315
 
313
- async prependZip(reader) {
314
- if (this.filenames.size) {
315
- throw new Error(ERR_ZIP_NOT_EMPTY);
316
- }
317
- reader = new GenericReader(reader);
318
- await initStream(reader);
319
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
320
- reader = new BlobReader(await streamToBlob(reader.readable));
321
- await initStream(reader);
322
- }
323
- const { ZipReader } = await import("./zip-reader.js");
324
- const zipReader = new ZipReader(reader);
325
- const entries = await zipReader.getEntries();
326
- await zipReader.close();
327
- await initStream(this.writer);
328
- const { directoryOffset } = zipReader;
329
- if (this.addSplitZipSignature) {
330
- delete this.addSplitZipSignature;
331
- if (!await startsWithSplitZipSignature(reader)) {
332
- await writeData(this.writer, getSplitZipSignatureArray());
333
- this.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
334
- }
335
- }
336
- const entryPositions = await copyZipData(this, reader, entries, directoryOffset);
337
- this.filenames = new Set(entries.map(entry => entry.filename));
338
- this.fileEntries = new Map(entries.map(entry => {
339
- const {
340
- version,
341
- rawLastModDate,
342
- lastAccessDate,
343
- creationDate,
344
- rawFilename,
345
- bitFlag,
346
- encrypted,
347
- uncompressedSize,
348
- compressedSize,
349
- extraFieldZip64
350
- } = entry;
351
- let {
352
- compressionMethod,
353
- rawExtraFieldZip64,
354
- rawExtraFieldAES,
355
- rawExtraFieldExtendedTimestamp,
356
- rawExtraFieldNTFS,
357
- rawExtraFieldUnix,
358
- rawExtraField,
359
- } = entry;
360
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
361
- rawExtraFieldZip64 = rawExtraFieldZip64 || EMPTY_UINT8_ARRAY;
362
- rawExtraFieldAES = rawExtraFieldAES || EMPTY_UINT8_ARRAY;
363
- rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || EMPTY_UINT8_ARRAY;
364
- rawExtraFieldNTFS = rawExtraFieldNTFS || EMPTY_UINT8_ARRAY;
365
- rawExtraFieldUnix = rawExtraFieldUnix || EMPTY_UINT8_ARRAY;
366
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
367
- if (entry.extraFieldAES) {
368
- compressionMethod = COMPRESSION_METHOD_AES;
369
- }
370
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
371
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
372
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
373
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
374
- const {
375
- headerArray,
376
- headerView
377
- } = getHeaderArrayData({
378
- version,
379
- bitFlag: bitFlagValue,
380
- compressionMethod,
381
- uncompressedSize,
382
- compressedSize,
383
- rawLastModDate,
384
- rawFilename,
385
- zip64CompressedSize,
386
- zip64UncompressedSize,
387
- extraFieldLength
388
- });
389
- const { crc32 } = entry;
390
- if (crc32 !== UNDEFINED_VALUE) {
391
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
392
- }
393
- const { offset, diskNumberStart } = entryPositions.get(entry);
394
- Object.assign(entry, {
395
- zip64UncompressedSize,
396
- zip64CompressedSize,
397
- offset,
398
- diskNumberStart,
399
- zip64DiskNumberStart: false,
400
- rawExtraFieldZip64,
401
- rawExtraFieldAES,
402
- rawExtraFieldExtendedTimestamp,
403
- rawExtraFieldNTFS,
404
- rawExtraFieldUnix,
405
- rawExtraField,
406
- extendedTimestamp: rawExtraFieldExtendedTimestamp.length > 0 || rawExtraFieldNTFS.length > 0,
407
- extraFieldExtendedTimestampFlag: 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0),
408
- headerArray,
409
- headerView
410
- });
411
- return [entry.filename, entry];
412
- }));
316
+ prependZip(reader) {
317
+ return watchPromiseError(this, prependZipEntries(this, reader));
318
+ }
319
+
320
+ appendZip(reader) {
321
+ return watchPromiseError(this, this.appendZipEntries(reader));
413
322
  }
414
323
 
415
- async add(name = "", reader, options = {}) {
324
+ async appendZipEntries(reader) {
416
325
  const zipWriter = this;
417
- options = Object.assign({}, options);
418
- const { pendingAddFileCalls } = zipWriter;
419
- if (workers < getConfiguration().maxWorkers) {
420
- workers++;
421
- } else {
422
- await new Promise(resolve => pendingEntries.push(resolve));
326
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
327
+ while (pendingAddFileCalls.size) {
328
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
423
329
  }
424
- let promiseAddFile;
425
- let nameAdded;
330
+ let resolveAppendZip;
331
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
332
+ pendingAddFileCalls.add(promiseAppendZip);
333
+ const appendedFilenames = [];
334
+ let releaseLockWriter;
426
335
  try {
427
- name = name.trim();
428
- if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
429
- name += DIRECTORY_SIGNATURE;
336
+ reader = new GenericReader(reader);
337
+ await initStream(reader);
338
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
339
+ reader = new BlobReader(await streamToBlob(reader.readable));
340
+ await initStream(reader);
430
341
  }
431
- if (zipWriter.filenames.has(name)) {
432
- throw new Error(ERR_DUPLICATED_NAME);
342
+ const { ZipReader } = await import("./zip-reader.js");
343
+ const zipReader = new ZipReader(reader);
344
+ const entries = await zipReader.getEntries();
345
+ await zipReader.close();
346
+ await initStream(zipWriter.writer);
347
+ const { directoryOffset } = zipReader;
348
+ entries.forEach(({ filename }) => {
349
+ if (filenames.has(filename)) {
350
+ throw new Error(ERR_DUPLICATED_NAME);
351
+ }
352
+ filenames.add(filename);
353
+ appendedFilenames.push(filename);
354
+ });
355
+ zipWriter.writerLocked = true;
356
+ const { lockWriter } = zipWriter;
357
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
358
+ zipWriter.writerLocked = false;
359
+ resolve();
360
+ });
361
+ await lockWriter;
362
+ if (zipWriter.addSplitZipSignature) {
363
+ delete zipWriter.addSplitZipSignature;
364
+ if (!await startsWithSplitZipSignature(reader)) {
365
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
366
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
367
+ }
433
368
  }
434
- zipWriter.filenames.add(name);
435
- nameAdded = true;
436
- promiseAddFile = addFile(zipWriter, name, reader, options);
437
- pendingAddFileCalls.add(promiseAddFile);
438
- return await promiseAddFile;
369
+ const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
370
+ entries.forEach(entry => {
371
+ const {
372
+ version,
373
+ rawLastModDate,
374
+ rawFilename,
375
+ bitFlag,
376
+ encrypted,
377
+ uncompressedSize,
378
+ compressedSize,
379
+ extraFieldZip64
380
+ } = entry;
381
+ let {
382
+ compressionMethod,
383
+ rawExtraField,
384
+ } = entry;
385
+ const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
386
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
387
+ if (entry.extraFieldAES) {
388
+ compressionMethod = COMPRESSION_METHOD_AES;
389
+ }
390
+ const extraFieldLength = getLength(rawExtraField);
391
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
392
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
393
+ const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
394
+ const {
395
+ headerArray,
396
+ headerView
397
+ } = getHeaderArrayData({
398
+ version,
399
+ bitFlag: bitFlagValue,
400
+ compressionMethod,
401
+ uncompressedSize,
402
+ compressedSize,
403
+ rawLastModDate,
404
+ rawFilename,
405
+ zip64CompressedSize,
406
+ zip64UncompressedSize,
407
+ extraFieldLength
408
+ });
409
+ const { crc32 } = entry;
410
+ if (crc32 !== UNDEFINED_VALUE) {
411
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
412
+ }
413
+ const { offset, diskNumberStart } = entryPositions.get(entry);
414
+ Object.assign(entry, {
415
+ zip64UncompressedSize,
416
+ zip64CompressedSize,
417
+ offset,
418
+ diskNumberStart,
419
+ zip64DiskNumberStart: false,
420
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
421
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
422
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
423
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
424
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
425
+ rawExtraField,
426
+ extendedTimestamp: false,
427
+ headerArray,
428
+ headerView
429
+ });
430
+ fileEntries.set(entry.filename, entry);
431
+ });
439
432
  } catch (error) {
440
- if (nameAdded) {
441
- zipWriter.filenames.delete(name);
442
- }
433
+ appendedFilenames.forEach(filename => filenames.delete(filename));
443
434
  throw error;
444
435
  } finally {
445
- pendingAddFileCalls.delete(promiseAddFile);
446
- const pendingEntry = pendingEntries.shift();
447
- if (pendingEntry) {
448
- pendingEntry();
449
- } else {
450
- workers--;
436
+ resolveAppendZip();
437
+ pendingAddFileCalls.delete(promiseAppendZip);
438
+ if (releaseLockWriter) {
439
+ releaseLockWriter();
451
440
  }
452
441
  }
453
442
  }
454
443
 
444
+ add(name = "", reader, options = {}) {
445
+ const zipWriter = this;
446
+ const { pendingAddFileCalls } = zipWriter;
447
+ const promiseAddFile = addFileEntry(zipWriter, name, reader, options);
448
+ pendingAddFileCalls.add(promiseAddFile);
449
+ const deletePendingAddFileCall = () => pendingAddFileCalls.delete(promiseAddFile);
450
+ Promise.prototype.then.call(promiseAddFile, deletePendingAddFileCall, deletePendingAddFileCall);
451
+ return watchPromiseError(zipWriter, promiseAddFile);
452
+ }
453
+
455
454
  remove(entry) {
456
455
  const { filenames, fileEntries } = this;
457
456
  // deno-lint-ignore valid-typeof
@@ -482,6 +481,19 @@ class ZipWriter {
482
481
  while (pendingAddFileCalls.size) {
483
482
  await Promise.allSettled(Array.from(pendingAddFileCalls));
484
483
  }
484
+ await Promise.allSettled(zipWriter.pendingErrors.map(watcher => watcher.recorded));
485
+ const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.error && !watcher.observed);
486
+ if (unobservedWatchers.length) {
487
+ const unobservedErrors = unobservedWatchers.map(watcher => watcher.error);
488
+ unobservedWatchers.forEach(watcher => watcher.observed = true);
489
+ const [error] = unobservedErrors;
490
+ try {
491
+ error.entryErrors = unobservedErrors;
492
+ } catch {
493
+ // ignored
494
+ }
495
+ throw error;
496
+ }
485
497
  await closeFile(zipWriter, comment, options);
486
498
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
487
499
  if (!preventClose) {
@@ -516,11 +528,7 @@ class ZipWriterStream {
516
528
  try {
517
529
  await zipWriter.close();
518
530
  } catch (error) {
519
- try {
520
- await zipWriter.writer.writable.abort(error);
521
- } catch {
522
- // ignored
523
- }
531
+ await abortWritable(zipWriter, error);
524
532
  }
525
533
  }
526
534
  }
@@ -537,8 +545,90 @@ class ZipWriterStream {
537
545
  }
538
546
 
539
547
  async close(comment = UNDEFINED_VALUE, options = {}) {
540
- await Promise.all(Array.from(this.pendingAddFileCalls));
541
- return this.zipWriter.close(comment, options);
548
+ const { zipWriter } = this;
549
+ const results = await Promise.allSettled(Array.from(this.pendingAddFileCalls));
550
+ const entryErrors = results.filter(result => result.status == "rejected").map(result => result.reason);
551
+ if (entryErrors.length) {
552
+ const [error] = entryErrors;
553
+ try {
554
+ error.entryErrors = entryErrors;
555
+ } catch {
556
+ // ignored
557
+ }
558
+ await abortWritable(zipWriter, error);
559
+ throw error;
560
+ }
561
+ try {
562
+ return await zipWriter.close(comment, options);
563
+ } catch (error) {
564
+ await abortWritable(zipWriter, error);
565
+ throw error;
566
+ }
567
+ }
568
+ }
569
+
570
+ class WatchedPromise extends Promise {
571
+
572
+ then(onFulfilled, onRejected) {
573
+ const { watcher } = this;
574
+ if (watcher) {
575
+ watcher.observed = true;
576
+ }
577
+ return super.then(onFulfilled, onRejected);
578
+ }
579
+ }
580
+
581
+ function watchPromiseError(zipWriter, promise) {
582
+ const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
583
+ const watcher = {};
584
+ watchedPromise.watcher = watcher;
585
+ watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE, error => watcher.error = error);
586
+ zipWriter.pendingErrors.push(watcher);
587
+ return watchedPromise;
588
+ }
589
+
590
+ async function prependZipEntries(zipWriter, reader) {
591
+ if (zipWriter.filenames.size) {
592
+ throw new Error(ERR_ZIP_NOT_EMPTY);
593
+ }
594
+ await zipWriter.appendZipEntries(reader);
595
+ }
596
+
597
+ async function addFileEntry(zipWriter, name, reader, options) {
598
+ options = Object.assign({}, options);
599
+ name = name.trim();
600
+ if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
601
+ name += DIRECTORY_SIGNATURE;
602
+ }
603
+ if (zipWriter.filenames.has(name)) {
604
+ throw new Error(ERR_DUPLICATED_NAME);
605
+ }
606
+ zipWriter.filenames.add(name);
607
+ if (workers < getConfiguration().maxWorkers) {
608
+ workers++;
609
+ } else {
610
+ await new Promise(resolve => pendingEntries.push(resolve));
611
+ }
612
+ try {
613
+ return await addFile(zipWriter, name, reader, options);
614
+ } catch (error) {
615
+ zipWriter.filenames.delete(name);
616
+ throw error;
617
+ } finally {
618
+ const pendingEntry = pendingEntries.shift();
619
+ if (pendingEntry) {
620
+ pendingEntry();
621
+ } else {
622
+ workers--;
623
+ }
624
+ }
625
+ }
626
+
627
+ async function abortWritable(zipWriter, error) {
628
+ try {
629
+ await zipWriter.writer.writable.abort(error);
630
+ } catch {
631
+ // ignored
542
632
  }
543
633
  }
544
634
 
@@ -779,11 +869,12 @@ function resolveMetadata(zipWriter, name, options) {
779
869
  if (getLength(rawComment) > MAX_16_BITS) {
780
870
  throw new Error(ERR_INVALID_ENTRY_COMMENT);
781
871
  }
782
- const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION, VERSION_DEFLATE);
783
- if (version > MAX_16_BITS) {
872
+ const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION);
873
+ if (version !== UNDEFINED_VALUE && version > MAX_16_BITS) {
784
874
  throw new Error(ERR_INVALID_VERSION);
785
875
  }
786
876
  const lastModDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE, new Date());
877
+ const rawLastModDate = getOptionValue(zipWriter, options, PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE);
787
878
  const lastAccessDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
788
879
  const creationDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
789
880
  const internalFileAttributes = getAliasedOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES, 0);
@@ -827,7 +918,7 @@ function resolveMetadata(zipWriter, name, options) {
827
918
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
828
919
  dataDescriptor = false;
829
920
  }
830
- if (dataDescriptor === UNDEFINED_VALUE || zipCrypto) {
921
+ if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThrough)) {
831
922
  dataDescriptor = true;
832
923
  }
833
924
  if (level !== UNDEFINED_VALUE && level != 6) {
@@ -846,6 +937,7 @@ function resolveMetadata(zipWriter, name, options) {
846
937
  rawComment,
847
938
  version,
848
939
  lastModDate,
940
+ rawLastModDate,
849
941
  lastAccessDate,
850
942
  creationDate,
851
943
  internalFileAttributes,
@@ -1121,10 +1213,8 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1121
1213
  }
1122
1214
  await initStream(fileWriter);
1123
1215
  const diskOffset = getDiskOffset(writer);
1124
- if (zipWriter.addSplitZipSignature) {
1125
- delete zipWriter.addSplitZipSignature;
1126
- await writeData(writer, getSplitZipSignatureArray());
1127
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
1216
+ if (zipWriter.addSplitZipSignature && !bufferedWrite) {
1217
+ await writeSplitZipSignature(zipWriter, writer);
1128
1218
  }
1129
1219
  if (usdz && !bufferedWrite) {
1130
1220
  appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
@@ -1151,6 +1241,9 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1151
1241
  if (bufferedWrite) {
1152
1242
  await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
1153
1243
  await requestLockWriter();
1244
+ if (zipWriter.addSplitZipSignature) {
1245
+ await writeSplitZipSignature(zipWriter, writer);
1246
+ }
1154
1247
  writingBufferedEntryData = true;
1155
1248
  writerSizeBeforeEntry = writer.size;
1156
1249
  await skipDiskIfNeeded();
@@ -1422,6 +1515,7 @@ function getHeaderInfo(options) {
1422
1515
  const {
1423
1516
  rawFilename,
1424
1517
  lastModDate,
1518
+ rawLastModDate: rawLastModDateOption,
1425
1519
  lastAccessDate,
1426
1520
  creationDate,
1427
1521
  level,
@@ -1555,6 +1649,9 @@ function getHeaderInfo(options) {
1555
1649
  if (compressionMethod === UNDEFINED_VALUE) {
1556
1650
  compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
1557
1651
  }
1652
+ if (version === UNDEFINED_VALUE) {
1653
+ version = compressionMethod == COMPRESSION_METHOD_STORE && !directory && !encrypted ? VERSION_STORE : VERSION_DEFLATE;
1654
+ }
1558
1655
  const { codecVersionNeeded } = options;
1559
1656
  if (compressed && codecVersionNeeded !== UNDEFINED_VALUE) {
1560
1657
  version = version > codecVersionNeeded ? version : codecVersionNeeded;
@@ -1587,6 +1684,7 @@ function getHeaderInfo(options) {
1587
1684
  compressionMethod,
1588
1685
  uncompressedSize,
1589
1686
  lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
1687
+ rawLastModDate: rawLastModDateOption,
1590
1688
  rawFilename,
1591
1689
  zip64CompressedSize,
1592
1690
  zip64UncompressedSize,
@@ -1793,9 +1891,9 @@ function updateLocalHeader({
1793
1891
 
1794
1892
  async function closeFile(zipWriter, comment, options) {
1795
1893
  const directoryDataLength = createDirectoryRecords(zipWriter.fileEntries);
1796
- const { directoryStart, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
1894
+ const { directoryStart, directoryEnd, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
1797
1895
  const signatureLength = await writeDigitalSignatureRecord(zipWriter, directoryArray, options);
1798
- await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryDataLength, signatureLength });
1896
+ await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength });
1799
1897
  }
1800
1898
 
1801
1899
  function createDirectoryRecords(files) {
@@ -1879,6 +1977,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1879
1977
  let directoryDiskOffset = 0;
1880
1978
  let directoryStartDiskNumber = getDiskNumber(writer);
1881
1979
  let directoryStartDiskOffset = getDiskOffset(writer);
1980
+ let directoryEndDiskEntriesLength = 0;
1882
1981
  for (const [indexFileEntry, fileEntry] of Array.from(fileEntries.values()).entries()) {
1883
1982
  const {
1884
1983
  offset: fileEntryOffset,
@@ -1908,6 +2007,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1908
2007
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
1909
2008
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
1910
2009
  directoryDiskOffset = offset;
2010
+ directoryEndDiskEntriesLength = 0;
1911
2011
  await writer.closeDisk();
1912
2012
  }
1913
2013
  if (indexFileEntry == 0) {
@@ -1943,6 +2043,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1943
2043
  directoryRecord.writeBytes(rawComment);
1944
2044
  arraySet(directoryArray, directoryRecord.array, offset);
1945
2045
  offset += directoryRecordLength;
2046
+ directoryEndDiskEntriesLength++;
1946
2047
  if (options.onprogress) {
1947
2048
  try {
1948
2049
  await options.onprogress(indexFileEntry + 1, fileEntries.size, new Entry(fileEntry));
@@ -1954,6 +2055,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1954
2055
  await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
1955
2056
  return {
1956
2057
  directoryStart: { diskNumber: directoryStartDiskNumber, diskOffset: directoryStartDiskOffset },
2058
+ directoryEnd: { diskNumber: getDiskNumber(writer), entriesLength: directoryEndDiskEntriesLength },
1957
2059
  directoryArray
1958
2060
  };
1959
2061
  }
@@ -1982,16 +2084,20 @@ async function writeDigitalSignatureRecord(zipWriter, directoryArray, options) {
1982
2084
 
1983
2085
  async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
1984
2086
  const { writer } = zipWriter;
1985
- const { directoryStart, signatureLength } = cdInfo;
2087
+ const { directoryStart, directoryEnd, signatureLength } = cdInfo;
1986
2088
  let { directoryDataLength } = cdInfo;
1987
2089
  let fileEntriesLength = zipWriter.fileEntries.size;
1988
2090
  let diskNumber = directoryStart.diskNumber;
1989
2091
  let directoryOffset = getSegmentOffset(zipWriter, directoryStart);
2092
+ const commentLength = getLength(comment);
2093
+ if (commentLength > MAX_16_BITS) {
2094
+ throw new Error(ERR_INVALID_COMMENT);
2095
+ }
2096
+ let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
1990
2097
  let lastDiskNumber = getDiskNumber(writer);
1991
- if (exceedsAvailableSize(writer, END_OF_CENTRAL_DIR_LENGTH)) {
2098
+ if (exceedsAvailableSize(writer, (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH) + commentLength)) {
1992
2099
  lastDiskNumber++;
1993
2100
  }
1994
- let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
1995
2101
  if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || fileEntriesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
1996
2102
  if (zip64 === false) {
1997
2103
  throw new Error(ERR_UNSUPPORTED_FORMAT);
@@ -1999,15 +2105,12 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
1999
2105
  zip64 = true;
2000
2106
  }
2001
2107
  }
2002
- const commentLength = getLength(comment);
2003
- if (commentLength > MAX_16_BITS) {
2004
- throw new Error(ERR_INVALID_COMMENT);
2005
- }
2006
2108
  const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
2007
2109
  if (exceedsAvailableSize(writer, getLength(endOfdirectoryRecord.array) + commentLength)) {
2008
2110
  await writer.closeDisk();
2009
2111
  }
2010
2112
  lastDiskNumber = getDiskNumber(writer);
2113
+ let diskFileEntriesLength = lastDiskNumber == directoryEnd.diskNumber ? directoryEnd.entriesLength : 0;
2011
2114
  if (zip64) {
2012
2115
  endOfdirectoryRecord.writeUint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
2013
2116
  endOfdirectoryRecord.writeUint64(44);
@@ -2015,7 +2118,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2015
2118
  endOfdirectoryRecord.writeUint16(45);
2016
2119
  endOfdirectoryRecord.writeUint32(lastDiskNumber);
2017
2120
  endOfdirectoryRecord.writeUint32(diskNumber);
2018
- endOfdirectoryRecord.writeUint64(fileEntriesLength);
2121
+ endOfdirectoryRecord.writeUint64(diskFileEntriesLength);
2019
2122
  endOfdirectoryRecord.writeUint64(fileEntriesLength);
2020
2123
  endOfdirectoryRecord.writeUint64(directoryDataLength);
2021
2124
  endOfdirectoryRecord.writeUint64(directoryOffset);
@@ -2028,6 +2131,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2028
2131
  lastDiskNumber = MAX_16_BITS;
2029
2132
  diskNumber = MAX_16_BITS;
2030
2133
  }
2134
+ diskFileEntriesLength = MAX_16_BITS;
2031
2135
  fileEntriesLength = MAX_16_BITS;
2032
2136
  directoryOffset = MAX_32_BITS;
2033
2137
  directoryDataLength = MAX_32_BITS;
@@ -2035,7 +2139,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2035
2139
  endOfdirectoryRecord.writeUint32(END_OF_CENTRAL_DIR_SIGNATURE);
2036
2140
  endOfdirectoryRecord.writeUint16(lastDiskNumber);
2037
2141
  endOfdirectoryRecord.writeUint16(diskNumber);
2038
- endOfdirectoryRecord.writeUint16(fileEntriesLength);
2142
+ endOfdirectoryRecord.writeUint16(diskFileEntriesLength);
2039
2143
  endOfdirectoryRecord.writeUint16(fileEntriesLength);
2040
2144
  endOfdirectoryRecord.writeUint32(directoryDataLength);
2041
2145
  endOfdirectoryRecord.writeUint32(directoryOffset);
@@ -2134,9 +2238,21 @@ async function copyZipData(zipWriter, reader, entries, directoryOffset) {
2134
2238
  async function copyData(zipWriter, reader, offset, size) {
2135
2239
  if (size > 0) {
2136
2240
  const { writer } = zipWriter;
2137
- await createReadable(reader, { offset, size }).pipeTo(writer.writable, { preventClose: true, preventAbort: true });
2138
- writer.size += size;
2139
- zipWriter.offset += size;
2241
+ let copiedLength = 0;
2242
+ try {
2243
+ await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
2244
+ } catch (error) {
2245
+ zipWriter.hasCorruptedEntries = true;
2246
+ try {
2247
+ error.corruptedEntry = true;
2248
+ } catch {
2249
+ // ignored
2250
+ }
2251
+ throw error;
2252
+ } finally {
2253
+ writer.size += copiedLength;
2254
+ zipWriter.offset += copiedLength;
2255
+ }
2140
2256
  }
2141
2257
  }
2142
2258
 
@@ -2161,6 +2277,12 @@ function getSplitZipSignatureArray() {
2161
2277
  return signatureArray;
2162
2278
  }
2163
2279
 
2280
+ async function writeSplitZipSignature(zipWriter, writer) {
2281
+ delete zipWriter.addSplitZipSignature;
2282
+ await writeData(writer, getSplitZipSignatureArray());
2283
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
2284
+ }
2285
+
2164
2286
  async function writeData(writer, array) {
2165
2287
  const { writable } = writer;
2166
2288
  const streamWriter = writable.getWriter();