@zip.js/zip.js 2.8.56 → 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,
@@ -170,10 +171,11 @@ import {
170
171
  ownsWritable
171
172
  } from "./io.js";
172
173
  import { encodeText } from "./util/encode-text.js";
173
- import { getDataView } from "./util/array.js";
174
+ 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
- zip64
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 = 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 = zip64 && uncompressedSize >= MAX_32_BITS;
372
- const zip64CompressedSize = zip64 && compressedSize >= MAX_32_BITS;
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));
413
318
  }
414
319
 
415
- async add(name = "", reader, options = {}) {
320
+ appendZip(reader) {
321
+ return watchPromiseError(this, this.appendZipEntries(reader));
322
+ }
323
+
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,
@@ -963,6 +1055,10 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
963
1055
  maximumCompressedSize = contentSize === UNDEFINED_VALUE ? getMaximumCompressedSize(uncompressedSize) + encryptionOverhead : contentSize;
964
1056
  }
965
1057
  }
1058
+ const emptyEntry = !encryptedEntry && (!hasContent || (contentSize === 0 && !passThrough)) && !isCompressed(compressionMethod, level);
1059
+ if (emptyEntry && !zipCrypto && getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR) === UNDEFINED_VALUE) {
1060
+ dataDescriptor = false;
1061
+ }
966
1062
  const zip64UncompressedSize = zip64Enabled || uncompressedSize >= MAX_32_BITS;
967
1063
  const zip64CompressedSize = zip64Enabled || maximumCompressedSize >= MAX_32_BITS;
968
1064
  if (zip64UncompressedSize || zip64CompressedSize) {
@@ -977,6 +1073,7 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
977
1073
  maximumCompressedSize,
978
1074
  resolvedOptions: {
979
1075
  dataDescriptor,
1076
+ emptyEntry,
980
1077
  zip64,
981
1078
  zip64UncompressedSize,
982
1079
  zip64CompressedSize,
@@ -1075,6 +1172,7 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1075
1172
  const {
1076
1173
  keepOrder,
1077
1174
  dataDescriptor,
1175
+ emptyEntry,
1078
1176
  signal
1079
1177
  } = options;
1080
1178
  const {
@@ -1099,7 +1197,7 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1099
1197
  lockPreviousFileEntry = previousFileEntry && previousFileEntry.lockFileEntry;
1100
1198
  requestLockCurrentFileEntry();
1101
1199
  }
1102
- if (options.bufferedWrite || !keepOrder || zipWriter.writerLocked || zipWriter.bufferedWrites || !dataDescriptor) {
1200
+ if (options.bufferedWrite || !keepOrder || zipWriter.writerLocked || zipWriter.bufferedWrites || (!dataDescriptor && !emptyEntry)) {
1103
1201
  bufferedWrite = true;
1104
1202
  zipWriter.bufferedWrites++;
1105
1203
  if (options.createTempStream) {
@@ -1115,10 +1213,8 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1115
1213
  }
1116
1214
  await initStream(fileWriter);
1117
1215
  const diskOffset = getDiskOffset(writer);
1118
- if (zipWriter.addSplitZipSignature) {
1119
- delete zipWriter.addSplitZipSignature;
1120
- await writeData(writer, getSplitZipSignatureArray());
1121
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
1216
+ if (zipWriter.addSplitZipSignature && !bufferedWrite) {
1217
+ await writeSplitZipSignature(zipWriter, writer);
1122
1218
  }
1123
1219
  if (usdz && !bufferedWrite) {
1124
1220
  appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
@@ -1145,6 +1241,9 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1145
1241
  if (bufferedWrite) {
1146
1242
  await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
1147
1243
  await requestLockWriter();
1244
+ if (zipWriter.addSplitZipSignature) {
1245
+ await writeSplitZipSignature(zipWriter, writer);
1246
+ }
1148
1247
  writingBufferedEntryData = true;
1149
1248
  writerSizeBeforeEntry = writer.size;
1150
1249
  await skipDiskIfNeeded();
@@ -1416,6 +1515,7 @@ function getHeaderInfo(options) {
1416
1515
  const {
1417
1516
  rawFilename,
1418
1517
  lastModDate,
1518
+ rawLastModDate: rawLastModDateOption,
1419
1519
  lastAccessDate,
1420
1520
  creationDate,
1421
1521
  level,
@@ -1521,8 +1621,8 @@ function getHeaderInfo(options) {
1521
1621
  try {
1522
1622
  const { uid, gid, unixExtraFieldType } = options;
1523
1623
  if (unixExtraFieldType == INFOZIP_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
1524
- const uidBytes = packUnixId(uid);
1525
- const gidBytes = packUnixId(gid);
1624
+ const uidBytes = packUnixId(uid === UNDEFINED_VALUE ? 0 : uid);
1625
+ const gidBytes = packUnixId(gid === UNDEFINED_VALUE ? 0 : gid);
1526
1626
  const payloadLength = 3 + uidBytes.length + gidBytes.length;
1527
1627
  const extraFieldUnix = createRecordWriter(4 + payloadLength);
1528
1628
  extraFieldUnix.writeUint16(EXTRAFIELD_TYPE_INFOZIP);
@@ -1549,6 +1649,9 @@ function getHeaderInfo(options) {
1549
1649
  if (compressionMethod === UNDEFINED_VALUE) {
1550
1650
  compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
1551
1651
  }
1652
+ if (version === UNDEFINED_VALUE) {
1653
+ version = compressionMethod == COMPRESSION_METHOD_STORE && !directory && !encrypted ? VERSION_STORE : VERSION_DEFLATE;
1654
+ }
1552
1655
  const { codecVersionNeeded } = options;
1553
1656
  if (compressed && codecVersionNeeded !== UNDEFINED_VALUE) {
1554
1657
  version = version > codecVersionNeeded ? version : codecVersionNeeded;
@@ -1581,6 +1684,7 @@ function getHeaderInfo(options) {
1581
1684
  compressionMethod,
1582
1685
  uncompressedSize,
1583
1686
  lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
1687
+ rawLastModDate: rawLastModDateOption,
1584
1688
  rawFilename,
1585
1689
  zip64CompressedSize,
1586
1690
  zip64UncompressedSize,
@@ -1653,18 +1757,14 @@ function appendExtraFieldUSDZ(entryInfo, zipWriterOffset) {
1653
1757
  }
1654
1758
 
1655
1759
  function packUnixId(id) {
1656
- if (id === UNDEFINED_VALUE) {
1657
- return EMPTY_UINT8_ARRAY;
1658
- } else {
1659
- const dataArray = new Uint8Array(4);
1660
- const dataView = getDataView(dataArray);
1661
- dataView.setUint32(0, id, true);
1662
- let length = 4;
1663
- while (length > 1 && dataArray[length - 1] === 0) {
1664
- length--;
1665
- }
1666
- return dataArray.subarray(0, length);
1667
- }
1760
+ const dataArray = new Uint8Array(4);
1761
+ const dataView = getDataView(dataArray);
1762
+ dataView.setUint32(0, id, true);
1763
+ let length = 4;
1764
+ while (length > 1 && dataArray[length - 1] === 0) {
1765
+ length--;
1766
+ }
1767
+ return dataArray.subarray(0, length);
1668
1768
  }
1669
1769
 
1670
1770
  function normalizeMsdosAttributes(msdosAttributesRaw, msdosAttributes) {
@@ -1791,9 +1891,9 @@ function updateLocalHeader({
1791
1891
 
1792
1892
  async function closeFile(zipWriter, comment, options) {
1793
1893
  const directoryDataLength = createDirectoryRecords(zipWriter.fileEntries);
1794
- const { directoryStart, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
1894
+ const { directoryStart, directoryEnd, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
1795
1895
  const signatureLength = await writeDigitalSignatureRecord(zipWriter, directoryArray, options);
1796
- await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryDataLength, signatureLength });
1896
+ await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength });
1797
1897
  }
1798
1898
 
1799
1899
  function createDirectoryRecords(files) {
@@ -1877,6 +1977,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1877
1977
  let directoryDiskOffset = 0;
1878
1978
  let directoryStartDiskNumber = getDiskNumber(writer);
1879
1979
  let directoryStartDiskOffset = getDiskOffset(writer);
1980
+ let directoryEndDiskEntriesLength = 0;
1880
1981
  for (const [indexFileEntry, fileEntry] of Array.from(fileEntries.values()).entries()) {
1881
1982
  const {
1882
1983
  offset: fileEntryOffset,
@@ -1906,6 +2007,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1906
2007
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
1907
2008
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
1908
2009
  directoryDiskOffset = offset;
2010
+ directoryEndDiskEntriesLength = 0;
1909
2011
  await writer.closeDisk();
1910
2012
  }
1911
2013
  if (indexFileEntry == 0) {
@@ -1941,6 +2043,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1941
2043
  directoryRecord.writeBytes(rawComment);
1942
2044
  arraySet(directoryArray, directoryRecord.array, offset);
1943
2045
  offset += directoryRecordLength;
2046
+ directoryEndDiskEntriesLength++;
1944
2047
  if (options.onprogress) {
1945
2048
  try {
1946
2049
  await options.onprogress(indexFileEntry + 1, fileEntries.size, new Entry(fileEntry));
@@ -1952,6 +2055,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1952
2055
  await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
1953
2056
  return {
1954
2057
  directoryStart: { diskNumber: directoryStartDiskNumber, diskOffset: directoryStartDiskOffset },
2058
+ directoryEnd: { diskNumber: getDiskNumber(writer), entriesLength: directoryEndDiskEntriesLength },
1955
2059
  directoryArray
1956
2060
  };
1957
2061
  }
@@ -1980,16 +2084,20 @@ async function writeDigitalSignatureRecord(zipWriter, directoryArray, options) {
1980
2084
 
1981
2085
  async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
1982
2086
  const { writer } = zipWriter;
1983
- const { directoryStart, signatureLength } = cdInfo;
2087
+ const { directoryStart, directoryEnd, signatureLength } = cdInfo;
1984
2088
  let { directoryDataLength } = cdInfo;
1985
2089
  let fileEntriesLength = zipWriter.fileEntries.size;
1986
2090
  let diskNumber = directoryStart.diskNumber;
1987
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);
1988
2097
  let lastDiskNumber = getDiskNumber(writer);
1989
- 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)) {
1990
2099
  lastDiskNumber++;
1991
2100
  }
1992
- let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
1993
2101
  if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || fileEntriesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
1994
2102
  if (zip64 === false) {
1995
2103
  throw new Error(ERR_UNSUPPORTED_FORMAT);
@@ -1997,15 +2105,12 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
1997
2105
  zip64 = true;
1998
2106
  }
1999
2107
  }
2000
- const commentLength = getLength(comment);
2001
- if (commentLength > MAX_16_BITS) {
2002
- throw new Error(ERR_INVALID_COMMENT);
2003
- }
2004
2108
  const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
2005
2109
  if (exceedsAvailableSize(writer, getLength(endOfdirectoryRecord.array) + commentLength)) {
2006
2110
  await writer.closeDisk();
2007
2111
  }
2008
2112
  lastDiskNumber = getDiskNumber(writer);
2113
+ let diskFileEntriesLength = lastDiskNumber == directoryEnd.diskNumber ? directoryEnd.entriesLength : 0;
2009
2114
  if (zip64) {
2010
2115
  endOfdirectoryRecord.writeUint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
2011
2116
  endOfdirectoryRecord.writeUint64(44);
@@ -2013,7 +2118,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2013
2118
  endOfdirectoryRecord.writeUint16(45);
2014
2119
  endOfdirectoryRecord.writeUint32(lastDiskNumber);
2015
2120
  endOfdirectoryRecord.writeUint32(diskNumber);
2016
- endOfdirectoryRecord.writeUint64(fileEntriesLength);
2121
+ endOfdirectoryRecord.writeUint64(diskFileEntriesLength);
2017
2122
  endOfdirectoryRecord.writeUint64(fileEntriesLength);
2018
2123
  endOfdirectoryRecord.writeUint64(directoryDataLength);
2019
2124
  endOfdirectoryRecord.writeUint64(directoryOffset);
@@ -2026,6 +2131,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2026
2131
  lastDiskNumber = MAX_16_BITS;
2027
2132
  diskNumber = MAX_16_BITS;
2028
2133
  }
2134
+ diskFileEntriesLength = MAX_16_BITS;
2029
2135
  fileEntriesLength = MAX_16_BITS;
2030
2136
  directoryOffset = MAX_32_BITS;
2031
2137
  directoryDataLength = MAX_32_BITS;
@@ -2033,7 +2139,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2033
2139
  endOfdirectoryRecord.writeUint32(END_OF_CENTRAL_DIR_SIGNATURE);
2034
2140
  endOfdirectoryRecord.writeUint16(lastDiskNumber);
2035
2141
  endOfdirectoryRecord.writeUint16(diskNumber);
2036
- endOfdirectoryRecord.writeUint16(fileEntriesLength);
2142
+ endOfdirectoryRecord.writeUint16(diskFileEntriesLength);
2037
2143
  endOfdirectoryRecord.writeUint16(fileEntriesLength);
2038
2144
  endOfdirectoryRecord.writeUint32(directoryDataLength);
2039
2145
  endOfdirectoryRecord.writeUint32(directoryOffset);
@@ -2083,6 +2189,21 @@ async function startsWithSplitZipSignature(reader) {
2083
2189
  return getUint32(getDataView(signatureArray), 0) == SPLIT_ZIP_FILE_SIGNATURE;
2084
2190
  }
2085
2191
 
2192
+ function removeExtraFieldZip64(rawExtraField) {
2193
+ const rawExtraFieldView = getDataView(rawExtraField);
2194
+ let offsetExtraField = 0;
2195
+ while (offsetExtraField + 4 <= getLength(rawExtraField)) {
2196
+ const size = 4 + getUint16(rawExtraFieldView, offsetExtraField + 2);
2197
+ if (getUint16(rawExtraFieldView, offsetExtraField) == EXTRAFIELD_TYPE_ZIP64) {
2198
+ return removeExtraFieldZip64(concat(
2199
+ rawExtraField.subarray(0, offsetExtraField),
2200
+ rawExtraField.subarray(Math.min(offsetExtraField + size, getLength(rawExtraField)))));
2201
+ }
2202
+ offsetExtraField += size;
2203
+ }
2204
+ return rawExtraField;
2205
+ }
2206
+
2086
2207
  async function copyZipData(zipWriter, reader, entries, directoryOffset) {
2087
2208
  const { writer } = zipWriter;
2088
2209
  const entryPositions = new Map();
@@ -2117,9 +2238,21 @@ async function copyZipData(zipWriter, reader, entries, directoryOffset) {
2117
2238
  async function copyData(zipWriter, reader, offset, size) {
2118
2239
  if (size > 0) {
2119
2240
  const { writer } = zipWriter;
2120
- await createReadable(reader, { offset, size }).pipeTo(writer.writable, { preventClose: true, preventAbort: true });
2121
- writer.size += size;
2122
- 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
+ }
2123
2256
  }
2124
2257
  }
2125
2258
 
@@ -2144,6 +2277,12 @@ function getSplitZipSignatureArray() {
2144
2277
  return signatureArray;
2145
2278
  }
2146
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
+
2147
2286
  async function writeData(writer, array) {
2148
2287
  const { writable } = writer;
2149
2288
  const streamWriter = writable.getWriter();