@zip.js/zip.js 2.8.57 → 2.8.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,
@@ -228,6 +230,7 @@ import {
228
230
  OPTION_USDZ,
229
231
  OPTION_UNIX_EXTRA_FIELD_TYPE,
230
232
  OPTION_LOCAL_EXTRA_FIELD,
233
+ OPTION_CENTRAL_EXTRA_FIELD,
231
234
  OPTION_SIGN_CENTRAL_DIRECTORY,
232
235
  TEXT_TYPE_FILENAME,
233
236
  TEXT_TYPE_COMMENT,
@@ -305,153 +308,151 @@ class ZipWriter {
305
308
  offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
306
309
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
307
310
  pendingAddFileCalls: new Set(),
311
+ pendingErrors: [],
308
312
  bufferedWrites: 0,
309
313
  lastFileEntry: UNDEFINED_VALUE
310
314
  });
311
315
  }
312
316
 
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
- }));
317
+ prependZip(reader) {
318
+ return watchPromiseError(this, prependZipEntries(this, reader));
319
+ }
320
+
321
+ appendZip(reader) {
322
+ return watchPromiseError(this, this.appendZipEntries(reader));
413
323
  }
414
324
 
415
- async add(name = "", reader, options = {}) {
325
+ async appendZipEntries(reader) {
416
326
  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));
327
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
328
+ while (pendingAddFileCalls.size) {
329
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
423
330
  }
424
- let promiseAddFile;
425
- let nameAdded;
331
+ let resolveAppendZip;
332
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
333
+ pendingAddFileCalls.add(promiseAppendZip);
334
+ const appendedFilenames = [];
335
+ let releaseLockWriter;
426
336
  try {
427
- name = name.trim();
428
- if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
429
- name += DIRECTORY_SIGNATURE;
337
+ reader = new GenericReader(reader);
338
+ await initStream(reader);
339
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
340
+ reader = new BlobReader(await streamToBlob(reader.readable));
341
+ await initStream(reader);
430
342
  }
431
- if (zipWriter.filenames.has(name)) {
432
- throw new Error(ERR_DUPLICATED_NAME);
343
+ const { ZipReader } = await import("./zip-reader.js");
344
+ const zipReader = new ZipReader(reader);
345
+ const entries = await zipReader.getEntries();
346
+ await zipReader.close();
347
+ await initStream(zipWriter.writer);
348
+ const { directoryOffset } = zipReader;
349
+ entries.forEach(({ filename }) => {
350
+ if (filenames.has(filename)) {
351
+ throw new Error(ERR_DUPLICATED_NAME);
352
+ }
353
+ filenames.add(filename);
354
+ appendedFilenames.push(filename);
355
+ });
356
+ zipWriter.writerLocked = true;
357
+ const { lockWriter } = zipWriter;
358
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
359
+ zipWriter.writerLocked = false;
360
+ resolve();
361
+ });
362
+ await lockWriter;
363
+ if (zipWriter.addSplitZipSignature) {
364
+ delete zipWriter.addSplitZipSignature;
365
+ if (!await startsWithSplitZipSignature(reader)) {
366
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
367
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
368
+ }
433
369
  }
434
- zipWriter.filenames.add(name);
435
- nameAdded = true;
436
- promiseAddFile = addFile(zipWriter, name, reader, options);
437
- pendingAddFileCalls.add(promiseAddFile);
438
- return await promiseAddFile;
370
+ const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
371
+ entries.forEach(entry => {
372
+ const {
373
+ version,
374
+ rawLastModDate,
375
+ rawFilename,
376
+ bitFlag,
377
+ encrypted,
378
+ uncompressedSize,
379
+ compressedSize,
380
+ extraFieldZip64
381
+ } = entry;
382
+ let {
383
+ compressionMethod,
384
+ rawExtraField,
385
+ } = entry;
386
+ const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
387
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
388
+ if (entry.extraFieldAES) {
389
+ compressionMethod = COMPRESSION_METHOD_AES;
390
+ }
391
+ const extraFieldLength = getLength(rawExtraField);
392
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
393
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
394
+ const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
395
+ const {
396
+ headerArray,
397
+ headerView
398
+ } = getHeaderArrayData({
399
+ version,
400
+ bitFlag: bitFlagValue,
401
+ compressionMethod,
402
+ uncompressedSize,
403
+ compressedSize,
404
+ rawLastModDate,
405
+ rawFilename,
406
+ zip64CompressedSize,
407
+ zip64UncompressedSize,
408
+ extraFieldLength
409
+ });
410
+ const { crc32 } = entry;
411
+ if (crc32 !== UNDEFINED_VALUE) {
412
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
413
+ }
414
+ const { offset, diskNumberStart } = entryPositions.get(entry);
415
+ Object.assign(entry, {
416
+ zip64UncompressedSize,
417
+ zip64CompressedSize,
418
+ offset,
419
+ diskNumberStart,
420
+ zip64DiskNumberStart: false,
421
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
422
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
423
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
424
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
425
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
426
+ rawExtraField,
427
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
428
+ extendedTimestamp: false,
429
+ headerArray,
430
+ headerView
431
+ });
432
+ fileEntries.set(entry.filename, entry);
433
+ });
439
434
  } catch (error) {
440
- if (nameAdded) {
441
- zipWriter.filenames.delete(name);
442
- }
435
+ appendedFilenames.forEach(filename => filenames.delete(filename));
443
436
  throw error;
444
437
  } finally {
445
- pendingAddFileCalls.delete(promiseAddFile);
446
- const pendingEntry = pendingEntries.shift();
447
- if (pendingEntry) {
448
- pendingEntry();
449
- } else {
450
- workers--;
438
+ resolveAppendZip();
439
+ pendingAddFileCalls.delete(promiseAppendZip);
440
+ if (releaseLockWriter) {
441
+ releaseLockWriter();
451
442
  }
452
443
  }
453
444
  }
454
445
 
446
+ add(name = "", reader, options = {}) {
447
+ const zipWriter = this;
448
+ const { pendingAddFileCalls } = zipWriter;
449
+ const promiseAddFile = addFileEntry(zipWriter, name, reader, options);
450
+ pendingAddFileCalls.add(promiseAddFile);
451
+ const deletePendingAddFileCall = () => pendingAddFileCalls.delete(promiseAddFile);
452
+ Promise.prototype.then.call(promiseAddFile, deletePendingAddFileCall, deletePendingAddFileCall);
453
+ return watchPromiseError(zipWriter, promiseAddFile);
454
+ }
455
+
455
456
  remove(entry) {
456
457
  const { filenames, fileEntries } = this;
457
458
  // deno-lint-ignore valid-typeof
@@ -482,6 +483,19 @@ class ZipWriter {
482
483
  while (pendingAddFileCalls.size) {
483
484
  await Promise.allSettled(Array.from(pendingAddFileCalls));
484
485
  }
486
+ await Promise.allSettled(zipWriter.pendingErrors.map(watcher => watcher.recorded));
487
+ const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.error && !watcher.observed);
488
+ if (unobservedWatchers.length) {
489
+ const unobservedErrors = unobservedWatchers.map(watcher => watcher.error);
490
+ unobservedWatchers.forEach(watcher => watcher.observed = true);
491
+ const [error] = unobservedErrors;
492
+ try {
493
+ error.entryErrors = unobservedErrors;
494
+ } catch {
495
+ // ignored
496
+ }
497
+ throw error;
498
+ }
485
499
  await closeFile(zipWriter, comment, options);
486
500
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
487
501
  if (!preventClose) {
@@ -516,11 +530,7 @@ class ZipWriterStream {
516
530
  try {
517
531
  await zipWriter.close();
518
532
  } catch (error) {
519
- try {
520
- await zipWriter.writer.writable.abort(error);
521
- } catch {
522
- // ignored
523
- }
533
+ await abortWritable(zipWriter, error);
524
534
  }
525
535
  }
526
536
  }
@@ -537,8 +547,89 @@ class ZipWriterStream {
537
547
  }
538
548
 
539
549
  async close(comment = UNDEFINED_VALUE, options = {}) {
540
- await Promise.all(Array.from(this.pendingAddFileCalls));
541
- return this.zipWriter.close(comment, options);
550
+ const { zipWriter } = this;
551
+ const results = await Promise.allSettled(Array.from(this.pendingAddFileCalls));
552
+ const entryErrors = results.filter(result => result.status == "rejected").map(result => result.reason);
553
+ if (entryErrors.length) {
554
+ const [error] = entryErrors;
555
+ try {
556
+ error.entryErrors = entryErrors;
557
+ } catch {
558
+ // ignored
559
+ }
560
+ await abortWritable(zipWriter, error);
561
+ throw error;
562
+ }
563
+ try {
564
+ return await zipWriter.close(comment, options);
565
+ } catch (error) {
566
+ await abortWritable(zipWriter, error);
567
+ throw error;
568
+ }
569
+ }
570
+ }
571
+
572
+ class WatchedPromise extends Promise {
573
+
574
+ then(onFulfilled, onRejected) {
575
+ const { watcher } = this;
576
+ if (watcher) {
577
+ watcher.observed = true;
578
+ }
579
+ return super.then(onFulfilled, onRejected);
580
+ }
581
+ }
582
+
583
+ function watchPromiseError(zipWriter, promise) {
584
+ const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
585
+ const watcher = {};
586
+ watchedPromise.watcher = watcher;
587
+ watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE, error => watcher.error = error);
588
+ zipWriter.pendingErrors.push(watcher);
589
+ return watchedPromise;
590
+ }
591
+
592
+ async function prependZipEntries(zipWriter, reader) {
593
+ if (zipWriter.filenames.size) {
594
+ throw new Error(ERR_ZIP_NOT_EMPTY);
595
+ }
596
+ await zipWriter.appendZipEntries(reader);
597
+ }
598
+
599
+ async function addFileEntry(zipWriter, name, reader, options) {
600
+ options = Object.assign({}, options);
601
+ if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
602
+ name += DIRECTORY_SIGNATURE;
603
+ }
604
+ if (zipWriter.filenames.has(name)) {
605
+ throw new Error(ERR_DUPLICATED_NAME);
606
+ }
607
+ zipWriter.filenames.add(name);
608
+ if (workers < getConfiguration().maxWorkers) {
609
+ workers++;
610
+ } else {
611
+ await new Promise(resolve => pendingEntries.push(resolve));
612
+ }
613
+ try {
614
+ return await addFile(zipWriter, name, reader, options);
615
+ } catch (error) {
616
+ zipWriter.filenames.delete(name);
617
+ throw error;
618
+ } finally {
619
+ const pendingEntry = pendingEntries.shift();
620
+ if (pendingEntry) {
621
+ pendingEntry();
622
+ } else {
623
+ workers--;
624
+ }
625
+ }
626
+ }
627
+
628
+ async function abortWritable(zipWriter, error) {
629
+ try {
630
+ await zipWriter.writer.writable.abort(error);
631
+ } catch {
632
+ // ignored
542
633
  }
543
634
  }
544
635
 
@@ -635,7 +726,6 @@ async function addFile(zipWriter, name, reader, options) {
635
726
  }
636
727
 
637
728
  function resolveAttributes(zipWriter, name, options) {
638
- name = name.trim();
639
729
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
640
730
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
641
731
  const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
@@ -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) {
@@ -839,6 +930,7 @@ function resolveMetadata(zipWriter, name, options) {
839
930
  }
840
931
  const rawExtraField = serializeExtraField(options[PROPERTY_NAME_EXTRA_FIELD]);
841
932
  const rawLocalExtraField = serializeExtraField(options[OPTION_LOCAL_EXTRA_FIELD]);
933
+ const rawCentralExtraField = serializeExtraField(options[OPTION_CENTRAL_EXTRA_FIELD]);
842
934
  return {
843
935
  comment,
844
936
  resolvedOptions: {
@@ -846,6 +938,7 @@ function resolveMetadata(zipWriter, name, options) {
846
938
  rawComment,
847
939
  version,
848
940
  lastModDate,
941
+ rawLastModDate,
849
942
  lastAccessDate,
850
943
  creationDate,
851
944
  internalFileAttributes,
@@ -873,7 +966,8 @@ function resolveMetadata(zipWriter, name, options) {
873
966
  dataDescriptor,
874
967
  zip64,
875
968
  rawExtraField,
876
- rawLocalExtraField
969
+ rawLocalExtraField,
970
+ rawCentralExtraField
877
971
  }
878
972
  };
879
973
  }
@@ -1015,7 +1109,6 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1015
1109
  let { name } = entry;
1016
1110
  const { size } = entry;
1017
1111
  const options = Object.assign({}, entry.options);
1018
- name = name.trim();
1019
1112
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
1020
1113
  name += DIRECTORY_SIGNATURE;
1021
1114
  }
@@ -1121,10 +1214,8 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1121
1214
  }
1122
1215
  await initStream(fileWriter);
1123
1216
  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;
1217
+ if (zipWriter.addSplitZipSignature && !bufferedWrite) {
1218
+ await writeSplitZipSignature(zipWriter, writer);
1128
1219
  }
1129
1220
  if (usdz && !bufferedWrite) {
1130
1221
  appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
@@ -1151,6 +1242,9 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1151
1242
  if (bufferedWrite) {
1152
1243
  await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
1153
1244
  await requestLockWriter();
1245
+ if (zipWriter.addSplitZipSignature) {
1246
+ await writeSplitZipSignature(zipWriter, writer);
1247
+ }
1154
1248
  writingBufferedEntryData = true;
1155
1249
  writerSizeBeforeEntry = writer.size;
1156
1250
  await skipDiskIfNeeded();
@@ -1270,6 +1364,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1270
1364
  versionMadeBy,
1271
1365
  rawComment,
1272
1366
  rawExtraField,
1367
+ rawCentralExtraField,
1273
1368
  useWebWorkers,
1274
1369
  transferStreams,
1275
1370
  onstart,
@@ -1313,6 +1408,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1313
1408
  rawExtraFieldUnix,
1314
1409
  rawExtraFieldAES,
1315
1410
  rawExtraField,
1411
+ rawCentralExtraField,
1316
1412
  extendedTimestamp,
1317
1413
  msDosCompatible,
1318
1414
  internalFileAttributes,
@@ -1422,6 +1518,7 @@ function getHeaderInfo(options) {
1422
1518
  const {
1423
1519
  rawFilename,
1424
1520
  lastModDate,
1521
+ rawLastModDate: rawLastModDateOption,
1425
1522
  lastAccessDate,
1426
1523
  creationDate,
1427
1524
  level,
@@ -1555,6 +1652,9 @@ function getHeaderInfo(options) {
1555
1652
  if (compressionMethod === UNDEFINED_VALUE) {
1556
1653
  compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
1557
1654
  }
1655
+ if (version === UNDEFINED_VALUE) {
1656
+ version = compressionMethod == COMPRESSION_METHOD_STORE && !directory && !encrypted ? VERSION_STORE : VERSION_DEFLATE;
1657
+ }
1558
1658
  const { codecVersionNeeded } = options;
1559
1659
  if (compressed && codecVersionNeeded !== UNDEFINED_VALUE) {
1560
1660
  version = version > codecVersionNeeded ? version : codecVersionNeeded;
@@ -1587,6 +1687,7 @@ function getHeaderInfo(options) {
1587
1687
  compressionMethod,
1588
1688
  uncompressedSize,
1589
1689
  lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
1690
+ rawLastModDate: rawLastModDateOption,
1590
1691
  rawFilename,
1591
1692
  zip64CompressedSize,
1592
1693
  zip64UncompressedSize,
@@ -1793,9 +1894,9 @@ function updateLocalHeader({
1793
1894
 
1794
1895
  async function closeFile(zipWriter, comment, options) {
1795
1896
  const directoryDataLength = createDirectoryRecords(zipWriter.fileEntries);
1796
- const { directoryStart, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
1897
+ const { directoryStart, directoryEnd, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
1797
1898
  const signatureLength = await writeDigitalSignatureRecord(zipWriter, directoryArray, options);
1798
- await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryDataLength, signatureLength });
1899
+ await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength });
1799
1900
  }
1800
1901
 
1801
1902
  function createDirectoryRecords(files) {
@@ -1808,6 +1909,7 @@ function createDirectoryRecords(files) {
1808
1909
  rawExtraFieldNTFS,
1809
1910
  rawExtraFieldUnix,
1810
1911
  rawExtraField,
1912
+ rawCentralExtraField,
1811
1913
  extendedTimestamp,
1812
1914
  extraFieldExtendedTimestampFlag,
1813
1915
  lastModDate,
@@ -1862,7 +1964,8 @@ function createDirectoryRecords(files) {
1862
1964
  rawExtraFieldNTFS,
1863
1965
  rawExtraFieldUnix,
1864
1966
  rawExtraFieldTimestamp,
1865
- rawExtraField);
1967
+ rawExtraField,
1968
+ rawCentralExtraField);
1866
1969
  if (extraFieldLength > MAX_16_BITS) {
1867
1970
  throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
1868
1971
  }
@@ -1879,6 +1982,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1879
1982
  let directoryDiskOffset = 0;
1880
1983
  let directoryStartDiskNumber = getDiskNumber(writer);
1881
1984
  let directoryStartDiskOffset = getDiskOffset(writer);
1985
+ let directoryEndDiskEntriesLength = 0;
1882
1986
  for (const [indexFileEntry, fileEntry] of Array.from(fileEntries.values()).entries()) {
1883
1987
  const {
1884
1988
  offset: fileEntryOffset,
@@ -1889,6 +1993,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1889
1993
  rawExtraFieldNTFS,
1890
1994
  rawExtraFieldUnix,
1891
1995
  rawExtraField,
1996
+ rawCentralExtraField,
1892
1997
  rawComment,
1893
1998
  versionMadeBy,
1894
1999
  headerArray,
@@ -1903,11 +2008,12 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1903
2008
  uncompressedSize,
1904
2009
  compressedSize
1905
2010
  } = fileEntry;
1906
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
2011
+ const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField, rawCentralExtraField);
1907
2012
  const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
1908
2013
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
1909
2014
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
1910
2015
  directoryDiskOffset = offset;
2016
+ directoryEndDiskEntriesLength = 0;
1911
2017
  await writer.closeDisk();
1912
2018
  }
1913
2019
  if (indexFileEntry == 0) {
@@ -1940,9 +2046,11 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1940
2046
  directoryRecord.writeBytes(rawExtraFieldNTFS);
1941
2047
  directoryRecord.writeBytes(rawExtraFieldUnix);
1942
2048
  directoryRecord.writeBytes(rawExtraField);
2049
+ directoryRecord.writeBytes(rawCentralExtraField);
1943
2050
  directoryRecord.writeBytes(rawComment);
1944
2051
  arraySet(directoryArray, directoryRecord.array, offset);
1945
2052
  offset += directoryRecordLength;
2053
+ directoryEndDiskEntriesLength++;
1946
2054
  if (options.onprogress) {
1947
2055
  try {
1948
2056
  await options.onprogress(indexFileEntry + 1, fileEntries.size, new Entry(fileEntry));
@@ -1954,6 +2062,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1954
2062
  await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
1955
2063
  return {
1956
2064
  directoryStart: { diskNumber: directoryStartDiskNumber, diskOffset: directoryStartDiskOffset },
2065
+ directoryEnd: { diskNumber: getDiskNumber(writer), entriesLength: directoryEndDiskEntriesLength },
1957
2066
  directoryArray
1958
2067
  };
1959
2068
  }
@@ -1982,16 +2091,20 @@ async function writeDigitalSignatureRecord(zipWriter, directoryArray, options) {
1982
2091
 
1983
2092
  async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
1984
2093
  const { writer } = zipWriter;
1985
- const { directoryStart, signatureLength } = cdInfo;
2094
+ const { directoryStart, directoryEnd, signatureLength } = cdInfo;
1986
2095
  let { directoryDataLength } = cdInfo;
1987
2096
  let fileEntriesLength = zipWriter.fileEntries.size;
1988
2097
  let diskNumber = directoryStart.diskNumber;
1989
2098
  let directoryOffset = getSegmentOffset(zipWriter, directoryStart);
2099
+ const commentLength = getLength(comment);
2100
+ if (commentLength > MAX_16_BITS) {
2101
+ throw new Error(ERR_INVALID_COMMENT);
2102
+ }
2103
+ let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
1990
2104
  let lastDiskNumber = getDiskNumber(writer);
1991
- if (exceedsAvailableSize(writer, END_OF_CENTRAL_DIR_LENGTH)) {
2105
+ if (exceedsAvailableSize(writer, (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH) + commentLength)) {
1992
2106
  lastDiskNumber++;
1993
2107
  }
1994
- let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
1995
2108
  if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || fileEntriesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
1996
2109
  if (zip64 === false) {
1997
2110
  throw new Error(ERR_UNSUPPORTED_FORMAT);
@@ -1999,15 +2112,12 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
1999
2112
  zip64 = true;
2000
2113
  }
2001
2114
  }
2002
- const commentLength = getLength(comment);
2003
- if (commentLength > MAX_16_BITS) {
2004
- throw new Error(ERR_INVALID_COMMENT);
2005
- }
2006
2115
  const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
2007
2116
  if (exceedsAvailableSize(writer, getLength(endOfdirectoryRecord.array) + commentLength)) {
2008
2117
  await writer.closeDisk();
2009
2118
  }
2010
2119
  lastDiskNumber = getDiskNumber(writer);
2120
+ let diskFileEntriesLength = lastDiskNumber == directoryEnd.diskNumber ? directoryEnd.entriesLength : 0;
2011
2121
  if (zip64) {
2012
2122
  endOfdirectoryRecord.writeUint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
2013
2123
  endOfdirectoryRecord.writeUint64(44);
@@ -2015,7 +2125,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2015
2125
  endOfdirectoryRecord.writeUint16(45);
2016
2126
  endOfdirectoryRecord.writeUint32(lastDiskNumber);
2017
2127
  endOfdirectoryRecord.writeUint32(diskNumber);
2018
- endOfdirectoryRecord.writeUint64(fileEntriesLength);
2128
+ endOfdirectoryRecord.writeUint64(diskFileEntriesLength);
2019
2129
  endOfdirectoryRecord.writeUint64(fileEntriesLength);
2020
2130
  endOfdirectoryRecord.writeUint64(directoryDataLength);
2021
2131
  endOfdirectoryRecord.writeUint64(directoryOffset);
@@ -2028,6 +2138,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2028
2138
  lastDiskNumber = MAX_16_BITS;
2029
2139
  diskNumber = MAX_16_BITS;
2030
2140
  }
2141
+ diskFileEntriesLength = MAX_16_BITS;
2031
2142
  fileEntriesLength = MAX_16_BITS;
2032
2143
  directoryOffset = MAX_32_BITS;
2033
2144
  directoryDataLength = MAX_32_BITS;
@@ -2035,7 +2146,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2035
2146
  endOfdirectoryRecord.writeUint32(END_OF_CENTRAL_DIR_SIGNATURE);
2036
2147
  endOfdirectoryRecord.writeUint16(lastDiskNumber);
2037
2148
  endOfdirectoryRecord.writeUint16(diskNumber);
2038
- endOfdirectoryRecord.writeUint16(fileEntriesLength);
2149
+ endOfdirectoryRecord.writeUint16(diskFileEntriesLength);
2039
2150
  endOfdirectoryRecord.writeUint16(fileEntriesLength);
2040
2151
  endOfdirectoryRecord.writeUint32(directoryDataLength);
2041
2152
  endOfdirectoryRecord.writeUint32(directoryOffset);
@@ -2134,9 +2245,21 @@ async function copyZipData(zipWriter, reader, entries, directoryOffset) {
2134
2245
  async function copyData(zipWriter, reader, offset, size) {
2135
2246
  if (size > 0) {
2136
2247
  const { writer } = zipWriter;
2137
- await createReadable(reader, { offset, size }).pipeTo(writer.writable, { preventClose: true, preventAbort: true });
2138
- writer.size += size;
2139
- zipWriter.offset += size;
2248
+ let copiedLength = 0;
2249
+ try {
2250
+ await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
2251
+ } catch (error) {
2252
+ zipWriter.hasCorruptedEntries = true;
2253
+ try {
2254
+ error.corruptedEntry = true;
2255
+ } catch {
2256
+ // ignored
2257
+ }
2258
+ throw error;
2259
+ } finally {
2260
+ writer.size += copiedLength;
2261
+ zipWriter.offset += copiedLength;
2262
+ }
2140
2263
  }
2141
2264
  }
2142
2265
 
@@ -2161,6 +2284,12 @@ function getSplitZipSignatureArray() {
2161
2284
  return signatureArray;
2162
2285
  }
2163
2286
 
2287
+ async function writeSplitZipSignature(zipWriter, writer) {
2288
+ delete zipWriter.addSplitZipSignature;
2289
+ await writeData(writer, getSplitZipSignatureArray());
2290
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
2291
+ }
2292
+
2164
2293
  async function writeData(writer, array) {
2165
2294
  const { writable } = writer;
2166
2295
  const streamWriter = writable.getWriter();