@zip.js/zip.js 2.11.4 → 2.13.0

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.
Files changed (52) hide show
  1. package/deno.json +1 -1
  2. package/dist/zip-core-external.js +549 -139
  3. package/dist/zip-core-external.min.js +1 -1
  4. package/dist/zip-core.js +559 -136
  5. package/dist/zip-core.min.js +1 -1
  6. package/dist/zip-fs-core-external.js +615 -260
  7. package/dist/zip-fs-core-external.min.js +1 -1
  8. package/dist/zip-fs-core.js +628 -257
  9. package/dist/zip-fs-core.min.js +1 -1
  10. package/dist/zip-fs-external.js +615 -260
  11. package/dist/zip-fs-external.min.js +1 -1
  12. package/dist/zip-fs-native.js +630 -259
  13. package/dist/zip-fs-native.min.js +1 -1
  14. package/dist/zip-fs.js +631 -260
  15. package/dist/zip-fs.min.js +1 -1
  16. package/dist/zip-legacy.js +561 -138
  17. package/dist/zip-legacy.min.js +1 -1
  18. package/dist/zip-native.js +561 -138
  19. package/dist/zip-native.min.js +1 -1
  20. package/dist/zip-web-worker-native.js +1 -1
  21. package/dist/zip-web-worker.js +1 -1
  22. package/dist/zip.js +562 -139
  23. package/dist/zip.min.js +1 -1
  24. package/index-native.cjs +630 -259
  25. package/index-native.min.js +1 -1
  26. package/index.cjs +631 -260
  27. package/index.d.cts +474 -59
  28. package/index.d.ts +474 -59
  29. package/index.min.js +1 -1
  30. package/lib/core/codec-worker-web.js +72 -22
  31. package/lib/core/codec-worker.js +18 -5
  32. package/lib/core/configuration.js +20 -5
  33. package/lib/core/constants.js +2 -0
  34. package/lib/core/io.js +13 -5
  35. package/lib/core/options.js +16 -0
  36. package/lib/core/streams/codec-stream.js +6 -0
  37. package/lib/core/util/error.js +35 -0
  38. package/lib/core/util/warnings.js +42 -0
  39. package/lib/core/version.js +1 -1
  40. package/lib/core/web-worker-base.js +46 -9
  41. package/lib/core/web-worker-inline-native.js +1 -1
  42. package/lib/core/web-worker-inline-wasm.js +1 -1
  43. package/lib/core/zip-entry.js +49 -1
  44. package/lib/core/zip-fs.js +45 -133
  45. package/lib/core/zip-reader.js +115 -52
  46. package/lib/core/zip-writer.js +244 -57
  47. package/lib/zip-core-base.js +9 -3
  48. package/lib/zip-core-reader.js +2 -0
  49. package/lib/zip-core-writer.js +6 -1
  50. package/lib/zip-module-wasm-base.js +2 -2
  51. package/package.json +5 -3
  52. package/worker-message-property-names.js +1 -1
@@ -154,7 +154,8 @@ import {
154
154
  OBJECT_TYPE,
155
155
  STRING_TYPE,
156
156
  FUNCTION_TYPE,
157
- EMPTY_UINT8_ARRAY
157
+ EMPTY_UINT8_ARRAY,
158
+ SYMBOL_ASYNC_DISPOSE
158
159
  } from "./constants.js";
159
160
  import { getConfiguration } from "./configuration.js";
160
161
  import { getRegisteredCodec, ERR_UNSUPPORTED_COMPRESSION } from "./codec-registry.js";
@@ -175,7 +176,9 @@ import {
175
176
  } from "./io.js";
176
177
  import { encodeText } from "./util/encode-text.js";
177
178
  import { concat, getDataView } from "./util/array.js";
179
+ import { addWarning } from "./util/warnings.js";
178
180
  import { toCompatibleReadable, streamToBlob } from "./util/compatible-streams.js";
181
+ import { isErrorObject } from "./util/error.js";
179
182
  import {
180
183
  PROPERTY_NAME_LAST_MODIFICATION_DATE,
181
184
  PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE,
@@ -204,7 +207,9 @@ import {
204
207
  PROPERTY_NAME_STICKY,
205
208
  PROPERTY_NAME_MSDOS_ATTRIBUTES,
206
209
  PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW,
207
- Entry
210
+ Entry,
211
+ getEncryptionOverhead,
212
+ getUserExtraField
208
213
  } from "./zip-entry.js";
209
214
  import {
210
215
  OPTION_PASSWORD,
@@ -240,9 +245,12 @@ import {
240
245
  checkSignalOption,
241
246
  throwIfAborted,
242
247
  checkPasswordOption,
248
+ checkPassThroughOption,
243
249
  checkInteger,
244
250
  checkIntegerOption,
245
- toNumber
251
+ toNumber,
252
+ OPTION_ENTRY,
253
+ PASS_THROUGH_COMPRESSED
246
254
  } from "./options.js";
247
255
 
248
256
  const ERR_DUPLICATED_NAME = "File already exists";
@@ -256,7 +264,7 @@ const ERR_INVALID_VERSION = "Version exceeds 65535";
256
264
  const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
257
265
  const ERR_UNSUPPORTED_ENCRYPTION_USDZ = "Encryption is not supported in USDZ files";
258
266
  const ERR_UNSUPPORTED_SPLIT_USDZ = "Split zip files are not supported in USDZ files";
259
- const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set";
267
+ const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set to true (use 'compressed' instead)";
260
268
  const ERR_INVALID_EXTRAFIELD = "Invalid extra field (must be a Map)";
261
269
  const ERR_INVALID_EXTRAFIELD_TYPE = "Invalid extra field type (must be integer 0..65535)";
262
270
  const ERR_INVALID_EXTRAFIELD_DATA_TYPE = "Invalid extra field data (must be a Uint8Array)";
@@ -268,6 +276,7 @@ const MAX_NTFS_TIME = BigInt("0x7fffffffffffffff");
268
276
  const ERR_UNSUPPORTED_FORMAT = "Zip64 is not supported (set the 'zip64' option to 'true')";
269
277
  const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
270
278
  const ERR_UNDEFINED_COMPRESSION_METHOD = "Undefined compression method";
279
+ const ERR_UNDEFINED_CRC32 = "Undefined CRC32";
271
280
  const ERR_UNDETERMINED_SIZE = "Undetermined size";
272
281
  const ERR_UNDEFINED_READER = "Undefined reader";
273
282
  const ERR_INVALID_READER = "Invalid reader (must be a Reader instance, a ReadableStream instance, or an object with a 'readable' property)";
@@ -281,14 +290,21 @@ const ERR_INVALID_MSDOS_ATTRIBUTES = "Invalid msdosAttributesRaw (must be intege
281
290
  const ERR_INVALID_MSDOS_DATA = "Invalid msdosAttributes (must be an object with boolean flags)";
282
291
  const ERR_INVALID_LEVEL = "Invalid level (must be integer 0..9)";
283
292
  const ERR_INVALID_SIGNATURE_DATA = "Signature data exceeds 64KB";
293
+ const ERR_INVALID_ENTRY = "Invalid entry option (must be an entry returned by ZipReader#getEntries())";
294
+ const ERR_ZIP_CRYPTO_LAST_MOD_DATE = "The last modification date of an entry encrypted with ZipCrypto cannot be changed when passThrough is set";
295
+ const WARNING_COMPRESSION_UNAVAILABLE = "compression unavailable";
296
+ const WARNING_CLAMPED_LAST_MODIFICATION_DATE = "clamped last modification date";
284
297
 
285
298
  const EXTRAFIELD_DATA_AES = new Uint8Array([0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00]);
286
299
  const EXTRAFIELD_OFFSET_AES_VENDOR_VERSION = 4;
287
300
  const EXTRAFIELD_OFFSET_AES_COMPRESSION_METHOD = 9;
288
301
  const EXTRAFIELD_USDZ_MAX_LENGTH = 67;
302
+ const MIN_PRINTABLE_ASCII_CHARACTER_CODE = 0x20;
303
+ const MAX_PRINTABLE_ASCII_CHARACTER_CODE = 0x7e;
289
304
  const VENDOR_VERSION_AE_1 = 1;
290
305
  const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
291
306
  const UNIX_EXTRA_FIELD_TYPE = "unix";
307
+ const LEVEL_BY_BITFLAG_LEVEL = [8, 9, 5, 3];
292
308
  const MAX_LEVEL = 9;
293
309
 
294
310
  let workers = 0;
@@ -315,9 +331,11 @@ class ZipWriter {
315
331
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
316
332
  pendingAddFileCalls: new Set(),
317
333
  pendingErrors: [],
334
+ warnings: [],
318
335
  bufferedWrites: 0,
319
336
  directWrites: 0,
320
- lastFileEntry: UNDEFINED_VALUE
337
+ lastFileEntry: UNDEFINED_VALUE,
338
+ archiveClosed: false
321
339
  });
322
340
  }
323
341
 
@@ -482,6 +500,9 @@ class ZipWriter {
482
500
  const zipWriter = this;
483
501
  const { pendingAddFileCalls, writer } = this;
484
502
  const { writable } = writer;
503
+ if (zipWriter.archiveClosed) {
504
+ return getWriterData(writer);
505
+ }
485
506
  if (!(comment instanceof Uint8Array)) {
486
507
  throw new Error(ERR_INVALID_COMMENT_TYPE);
487
508
  }
@@ -492,7 +513,7 @@ class ZipWriter {
492
513
  await Promise.allSettled(Array.from(pendingAddFileCalls));
493
514
  }
494
515
  await Promise.allSettled(zipWriter.pendingErrors.map(watcher => watcher.recorded));
495
- const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.error && !watcher.observed);
516
+ const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.failed && !watcher.observed);
496
517
  if (unobservedWatchers.length) {
497
518
  const unobservedErrors = unobservedWatchers.map(watcher => watcher.error);
498
519
  unobservedWatchers.forEach(watcher => watcher.observed = true);
@@ -505,11 +526,16 @@ class ZipWriter {
505
526
  throw error;
506
527
  }
507
528
  await closeFile(zipWriter, comment, options);
529
+ zipWriter.archiveClosed = true;
508
530
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
509
531
  if (!preventClose) {
510
532
  await writable.getWriter().close();
511
533
  }
512
- return writer.getData ? writer.getData() : writable;
534
+ return getWriterData(writer);
535
+ }
536
+
537
+ [SYMBOL_ASYNC_DISPOSE]() {
538
+ return this.close();
513
539
  }
514
540
  }
515
541
 
@@ -588,11 +614,16 @@ class WatchedPromise extends Promise {
588
614
  }
589
615
  }
590
616
 
617
+ function getWriterData(writer) {
618
+ return writer.getData ? writer.getData() : writer.writable;
619
+ }
620
+
591
621
  function watchPromiseError(zipWriter, promise) {
592
622
  const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
593
623
  const watcher = {};
594
624
  watchedPromise.watcher = watcher;
595
- watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE, error => watcher.error = error);
625
+ watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE,
626
+ error => Object.assign(watcher, { failed: true, error }));
596
627
  zipWriter.pendingErrors.push(watcher);
597
628
  return watchedPromise;
598
629
  }
@@ -606,6 +637,14 @@ async function prependZipEntries(zipWriter, reader) {
606
637
 
607
638
  async function addFileEntry(zipWriter, name, reader, options) {
608
639
  options = Object.assign({}, options);
640
+ const entry = options[OPTION_ENTRY];
641
+ if (entry !== UNDEFINED_VALUE) {
642
+ const { entryOptions, passThroughOptions } = getSourceEntryOptions(entry,
643
+ checkPassThroughOption(getOptionValue(zipWriter, options, OPTION_PASS_THROUGH)),
644
+ getOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE));
645
+ delete options[OPTION_ENTRY];
646
+ options = Object.assign(entryOptions, passThroughOptions, options);
647
+ }
609
648
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
610
649
  name += DIRECTORY_SIGNATURE;
611
650
  }
@@ -656,6 +695,7 @@ export {
656
695
  ZipWriter,
657
696
  ZipWriterStream,
658
697
  getEntriesSize,
698
+ getSourceEntryOptions,
659
699
  ERR_DUPLICATED_NAME,
660
700
  ERR_INVALID_COMMENT,
661
701
  ERR_INVALID_COMMENT_TYPE,
@@ -675,11 +715,14 @@ export {
675
715
  ERR_UNSUPPORTED_FORMAT,
676
716
  ERR_UNDEFINED_UNCOMPRESSED_SIZE,
677
717
  ERR_UNDEFINED_COMPRESSION_METHOD,
718
+ ERR_UNDEFINED_CRC32,
678
719
  ERR_UNDETERMINED_SIZE,
679
720
  ERR_UNDEFINED_READER,
680
721
  ERR_INVALID_READER,
681
722
  ERR_ZIP_NOT_EMPTY,
682
723
  ERR_INVALID_SIGNATURE_DATA,
724
+ ERR_INVALID_ENTRY,
725
+ ERR_ZIP_CRYPTO_LAST_MOD_DATE,
683
726
  ERR_INVALID_UID,
684
727
  ERR_INVALID_GID,
685
728
  ERR_INVALID_UNIX_MODE,
@@ -689,7 +732,9 @@ export {
689
732
  ERR_INVALID_MSDOS_DATA,
690
733
  ERR_INVALID_LEVEL,
691
734
  ERR_INVALID_PASSWORD_TYPE,
692
- ERR_UNSUPPORTED_CRYPTO_API
735
+ ERR_UNSUPPORTED_CRYPTO_API,
736
+ WARNING_COMPRESSION_UNAVAILABLE,
737
+ WARNING_CLAMPED_LAST_MODIFICATION_DATE
693
738
  };
694
739
 
695
740
  async function addFile(zipWriter, name, reader, options) {
@@ -710,21 +755,33 @@ async function addFile(zipWriter, name, reader, options) {
710
755
  try {
711
756
  const { resolvedOptions } = metadataInfo;
712
757
  if (resolvedOptions.level != 0 && resolvedOptions.compressionMethod === UNDEFINED_VALUE &&
713
- !resolvedOptions.passThrough && !(await supportsDeflate(getConfiguration()))) {
758
+ !resolvedOptions.passThroughCompression && !(await supportsDeflate(getConfiguration()))) {
714
759
  resolvedOptions.level = 0;
760
+ addWarning(zipWriter.warnings, WARNING_COMPRESSION_UNAVAILABLE, name);
715
761
  }
716
762
  const sizesInfo = await resolveSizes(zipWriter, reader, metadataInfo, options);
717
763
  ({ reader } = sizesInfo);
718
764
  const diskOffset = getDiskOffset(zipWriter.writer);
719
765
  const diskNumber = getDiskNumber(zipWriter.writer);
766
+ let crc32 = options.crc32 === UNDEFINED_VALUE ? options[PROPERTY_NAME_SIGNATURE] : options.crc32;
767
+ const storesAE2 = sizesInfo.resolvedOptions.encrypted && !resolvedOptions.zipCrypto;
768
+ if (resolvedOptions.passThroughCompression && !resolvedOptions.passThroughEncryption && storesAE2) {
769
+ crc32 = UNDEFINED_VALUE;
770
+ }
771
+ if (resolvedOptions.passThroughCompression && reader && !storesAE2 && crc32 === UNDEFINED_VALUE) {
772
+ throw new Error(ERR_UNDEFINED_CRC32);
773
+ }
720
774
  options = Object.assign({}, options, attributesInfo.resolvedOptions, metadataInfo.resolvedOptions, sizesInfo.resolvedOptions, {
721
775
  signature: options[PROPERTY_NAME_SIGNATURE],
722
- crc32: options.crc32 === UNDEFINED_VALUE ? options[PROPERTY_NAME_SIGNATURE] : options.crc32,
776
+ crc32,
723
777
  offset: zipWriter.offset - diskOffset,
724
778
  diskNumberStart: diskNumber,
725
779
  [OPTION_USDZ]: zipWriter.options[OPTION_USDZ]
726
780
  });
727
781
  const headerInfo = getHeaderInfo(options);
782
+ if (headerInfo.lastModDateClamped) {
783
+ addWarning(zipWriter.warnings, WARNING_CLAMPED_LAST_MODIFICATION_DATE, name);
784
+ }
728
785
  const dataDescriptorInfo = getDataDescriptorInfo(options);
729
786
  const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
730
787
  fileEntry = await getFileEntry(zipWriter, name, reader, {
@@ -751,6 +808,95 @@ async function addFile(zipWriter, name, reader, options) {
751
808
  return new Entry(fileEntry);
752
809
  }
753
810
 
811
+ function getSourceEntryOptions(entry, passThrough, lastModDateOverride) {
812
+ if (entry === null || typeof entry != OBJECT_TYPE || Array.isArray(entry)) {
813
+ throw new Error(ERR_INVALID_ENTRY);
814
+ }
815
+ const {
816
+ externalFileAttributes,
817
+ versionMadeBy,
818
+ comment,
819
+ lastModDate,
820
+ rawLastModDate,
821
+ creationDate,
822
+ lastAccessDate,
823
+ uncompressedSize,
824
+ encrypted,
825
+ zipCrypto,
826
+ crc32,
827
+ compressionMethod,
828
+ extraFieldAES,
829
+ extraFieldUnix,
830
+ internalFileAttributes,
831
+ extraField,
832
+ bitFlag,
833
+ directory,
834
+ uid,
835
+ gid
836
+ } = entry;
837
+ const entryOptions = {
838
+ externalFileAttributes,
839
+ versionMadeBy,
840
+ comment,
841
+ lastModDate,
842
+ creationDate,
843
+ lastAccessDate,
844
+ internalFileAttributes,
845
+ directory
846
+ };
847
+ if (bitFlag && bitFlag.languageEncodingFlag) {
848
+ entryOptions[OPTION_USE_UNICODE_FILE_NAMES] = true;
849
+ }
850
+ const userExtraField = getUserExtraField(extraField);
851
+ if (userExtraField) {
852
+ entryOptions[PROPERTY_NAME_EXTRA_FIELD] = userExtraField;
853
+ }
854
+ if (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE) {
855
+ Object.assign(entryOptions, {
856
+ uid,
857
+ gid,
858
+ unixExtraFieldType: extraFieldUnix ? UNIX_EXTRA_FIELD_TYPE : INFOZIP_EXTRA_FIELD_TYPE
859
+ });
860
+ }
861
+ const passThroughOptions = {};
862
+ if (passThrough && !directory) {
863
+ Object.assign(passThroughOptions, {
864
+ uncompressedSize,
865
+ crc32,
866
+ compressionMethod
867
+ });
868
+ if (passThrough !== PASS_THROUGH_COMPRESSED) {
869
+ Object.assign(passThroughOptions, {
870
+ encrypted,
871
+ zipCrypto,
872
+ encryptionStrength: extraFieldAES ? extraFieldAES.strength : UNDEFINED_VALUE
873
+ });
874
+ }
875
+ if (bitFlag) {
876
+ passThroughOptions.dataDescriptor = bitFlag.dataDescriptor;
877
+ passThroughOptions[OPTION_LEVEL] = LEVEL_BY_BITFLAG_LEVEL[bitFlag.level];
878
+ }
879
+ if (lastModDateOverride === UNDEFINED_VALUE) {
880
+ passThroughOptions.rawLastModDate = rawLastModDate;
881
+ } else if (passThrough !== PASS_THROUGH_COMPRESSED && zipCrypto && (!bitFlag || bitFlag.dataDescriptor) &&
882
+ lastModDateOverride instanceof Date &&
883
+ getDosTimeHighByte(lastModDateOverride) != ((rawLastModDate >>> 8) & MAX_8_BITS)) {
884
+ throw new Error(ERR_ZIP_CRYPTO_LAST_MOD_DATE);
885
+ }
886
+ }
887
+ return { entryOptions, passThroughOptions };
888
+ }
889
+
890
+ function getDosTimeHighByte(lastModDate) {
891
+ let dosLastModDate = new Date(Math.ceil(Math.floor(lastModDate.getTime() / 1000) / 2) * 2000);
892
+ if (dosLastModDate < MIN_DATE) {
893
+ dosLastModDate = MIN_DATE;
894
+ } else if (dosLastModDate > MAX_DATE) {
895
+ dosLastModDate = MAX_DATE;
896
+ }
897
+ return ((dosLastModDate.getHours() << 3) | (dosLastModDate.getMinutes() >> 3)) & MAX_8_BITS;
898
+ }
899
+
754
900
  function resolveAttributes(zipWriter, name, options) {
755
901
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
756
902
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
@@ -904,7 +1050,9 @@ function resolveMetadata(zipWriter, name, options) {
904
1050
  const lastAccessDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
905
1051
  const creationDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
906
1052
  const internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, 0);
907
- const passThrough = getOptionValue(zipWriter, options, OPTION_PASS_THROUGH);
1053
+ const passThrough = checkPassThroughOption(getOptionValue(zipWriter, options, OPTION_PASS_THROUGH));
1054
+ const passThroughCompression = Boolean(passThrough);
1055
+ const passThroughEncryption = passThrough === true;
908
1056
  const password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
909
1057
  const rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
910
1058
  checkPasswordOption(password, rawPassword);
@@ -920,10 +1068,11 @@ function resolveMetadata(zipWriter, name, options) {
920
1068
  const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, true);
921
1069
  const signal = checkSignalOption(getOptionValue(zipWriter, options, OPTION_SIGNAL));
922
1070
  throwIfAborted(signal);
923
- const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES, true);
1071
+ const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES,
1072
+ !isPrintableASCIIText(rawFilename) || !isPrintableASCIIText(rawComment));
924
1073
  const compressionMethod = getOptionValue(zipWriter, options, PROPERTY_NAME_COMPRESSION_METHOD);
925
- const registeredCodec = passThrough || compressionMethod === UNDEFINED_VALUE ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
926
- if (!passThrough && compressionMethod !== UNDEFINED_VALUE &&
1074
+ const registeredCodec = passThroughCompression || compressionMethod === UNDEFINED_VALUE ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
1075
+ if (!passThroughCompression && compressionMethod !== UNDEFINED_VALUE &&
927
1076
  compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE && !registeredCodec) {
928
1077
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
929
1078
  }
@@ -937,15 +1086,15 @@ function resolveMetadata(zipWriter, name, options) {
937
1086
  level = 0;
938
1087
  }
939
1088
  }
940
- if (passThrough) {
941
- level = UNDEFINED_VALUE;
1089
+ if (passThroughCompression) {
1090
+ level = toNumber(options[OPTION_LEVEL]);
942
1091
  }
943
1092
  let useCompressionStream = getOptionValue(zipWriter, options, OPTION_USE_COMPRESSION_STREAM);
944
1093
  let dataDescriptor = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR);
945
1094
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
946
1095
  dataDescriptor = false;
947
1096
  }
948
- if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThrough)) {
1097
+ if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThroughEncryption)) {
949
1098
  dataDescriptor = true;
950
1099
  }
951
1100
  if (level !== UNDEFINED_VALUE && level != 6) {
@@ -969,7 +1118,8 @@ function resolveMetadata(zipWriter, name, options) {
969
1118
  lastAccessDate,
970
1119
  creationDate,
971
1120
  internalFileAttributes,
972
- passThrough,
1121
+ passThroughCompression,
1122
+ passThroughEncryption,
973
1123
  password,
974
1124
  rawPassword,
975
1125
  encryptionStrength,
@@ -1030,7 +1180,7 @@ function serializeExtraField(extraField) {
1030
1180
  }
1031
1181
 
1032
1182
  async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, options) {
1033
- if (metadata.passThrough && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
1183
+ if (metadata.passThroughCompression && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
1034
1184
  throw new Error(ERR_UNDEFINED_READER);
1035
1185
  }
1036
1186
  let contentSize;
@@ -1046,12 +1196,12 @@ async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, op
1046
1196
  }
1047
1197
 
1048
1198
  function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options) {
1049
- const { passThrough, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
1199
+ const { passThroughCompression, passThroughEncryption, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
1050
1200
  let { dataDescriptor, zip64, level, compressionMethod } = metadata;
1051
1201
  let maximumCompressedSize = 0;
1052
1202
  let uncompressedSize = 0;
1053
1203
  let unknownSize = false;
1054
- if (passThrough && hasContent) {
1204
+ if (passThroughCompression && hasContent) {
1055
1205
  uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
1056
1206
  if (uncompressedSize === UNDEFINED_VALUE) {
1057
1207
  throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
@@ -1062,17 +1212,17 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1062
1212
  }
1063
1213
  const zip64Enabled = zip64 === true;
1064
1214
  const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
1065
- if (hasContent && passThrough && !encrypted && getLength(password, rawPassword)) {
1215
+ if (hasContent && passThroughEncryption && !encrypted && getLength(password, rawPassword)) {
1066
1216
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH);
1067
1217
  }
1068
- const encryptedEntry = hasContent && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
1218
+ const encryptedEntry = hasContent && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThroughEncryption && encrypted));
1069
1219
  if (!hasContent) {
1070
1220
  level = 0;
1071
1221
  compressionMethod = COMPRESSION_METHOD_STORE;
1072
1222
  }
1073
- const encryptionOverhead = encryptedEntry ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
1223
+ const encryptionOverhead = getEncryptionOverhead(encryptedEntry, zipCrypto, encryptionStrength);
1074
1224
  if (hasContent) {
1075
- if (!passThrough) {
1225
+ if (!passThroughCompression) {
1076
1226
  if (contentSize === UNDEFINED_VALUE) {
1077
1227
  dataDescriptor = true;
1078
1228
  if (zip64 || zip64 === UNDEFINED_VALUE) {
@@ -1085,10 +1235,12 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1085
1235
  }
1086
1236
  } else {
1087
1237
  options.uncompressedSize = uncompressedSize;
1088
- maximumCompressedSize = contentSize === UNDEFINED_VALUE ? getMaximumCompressedSize(uncompressedSize) + encryptionOverhead : contentSize;
1238
+ maximumCompressedSize = contentSize === UNDEFINED_VALUE ?
1239
+ getMaximumCompressedSize(uncompressedSize) + encryptionOverhead :
1240
+ contentSize + (passThroughEncryption ? 0 : encryptionOverhead);
1089
1241
  }
1090
1242
  }
1091
- const emptyEntry = !encryptedEntry && (!hasContent || (contentSize === 0 && !passThrough)) && !isCompressed(compressionMethod, level);
1243
+ const emptyEntry = !encryptedEntry && (!hasContent || (contentSize === 0 && !passThroughCompression)) && !isCompressed(compressionMethod, level);
1092
1244
  if (emptyEntry && !zipCrypto && getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR) === UNDEFINED_VALUE) {
1093
1245
  dataDescriptor = false;
1094
1246
  }
@@ -1137,6 +1289,8 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1137
1289
  const initialOffset = writerOptions[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : writerOptions[OPTION_OFFSET];
1138
1290
  let offset = initialOffset;
1139
1291
  let minimumEntrySize = INFINITY_VALUE;
1292
+ const entrySizes = [];
1293
+ const entriesAlreadyZip64 = new Set();
1140
1294
  for (const entry of entries) {
1141
1295
  let { name } = entry;
1142
1296
  const { size } = entry;
@@ -1148,7 +1302,7 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1148
1302
  ({ name } = attributesInfo);
1149
1303
  const { resolvedOptions: metadata } = resolveMetadata(zipWriter, name, options);
1150
1304
  if (metadata.level != 0 && metadata.compressionMethod === UNDEFINED_VALUE &&
1151
- !metadata.passThrough && !(await supportsDeflate(getConfiguration()))) {
1305
+ !metadata.passThroughCompression && !(await supportsDeflate(getConfiguration()))) {
1152
1306
  metadata.level = 0;
1153
1307
  }
1154
1308
  const hasContent = !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY);
@@ -1156,7 +1310,7 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1156
1310
  throw new Error(ERR_UNDETERMINED_SIZE);
1157
1311
  }
1158
1312
  const { maximumCompressedSize, resolvedOptions: sizes } = resolveEntrySizes(zipWriter, hasContent, size, metadata, options);
1159
- if (hasContent && !metadata.passThrough && isCompressed(sizes.compressionMethod, sizes.level)) {
1313
+ if (hasContent && !metadata.passThroughCompression && isCompressed(sizes.compressionMethod, sizes.level)) {
1160
1314
  throw new Error(ERR_UNDETERMINED_SIZE);
1161
1315
  }
1162
1316
  const entryOptions = Object.assign({}, options, attributesInfo.resolvedOptions, metadata, sizes, { [OPTION_USDZ]: usdz });
@@ -1176,10 +1330,15 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1176
1330
  compressedSize
1177
1331
  }));
1178
1332
  const entrySize = entryInfo.metadataSize + compressedSize;
1333
+ entrySizes.push(entrySize);
1334
+ entriesAlreadyZip64.add(Boolean(entryOptions.zip64Enabled ||
1335
+ entryOptions.uncompressedSize >= MAX_32_BITS || compressedSize >= MAX_32_BITS));
1179
1336
  minimumEntrySize = Math.min(minimumEntrySize, entrySize);
1180
1337
  offset += entrySize;
1181
1338
  }
1182
- const layoutDependsOnWriteOrder = files.size > 1 && (usdz || offset - minimumEntrySize >= MAX_32_BITS);
1339
+ const layoutDependsOnWriteOrder = files.size > 1 && (usdz ||
1340
+ (offset - minimumEntrySize >= MAX_32_BITS &&
1341
+ (entriesAlreadyZip64.size > 1 || countEntriesBefore4GB(entrySizes, initialOffset, true) != countEntriesBefore4GB(entrySizes, initialOffset, false))));
1183
1342
  if (layoutDependsOnWriteOrder && !writeOrderGuaranteed) {
1184
1343
  throw new Error(ERR_UNDETERMINED_SIZE);
1185
1344
  }
@@ -1197,6 +1356,20 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1197
1356
  return offset - initialOffset + directoryDataLength + commentLength + (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
1198
1357
  }
1199
1358
 
1359
+ function countEntriesBefore4GB(entrySizes, initialOffset, largestFirst) {
1360
+ const ordered = entrySizes.slice().sort((first, second) => largestFirst ? second - first : first - second);
1361
+ let position = initialOffset;
1362
+ let count = 0;
1363
+ for (const entrySize of ordered) {
1364
+ if (position >= MAX_32_BITS) {
1365
+ break;
1366
+ }
1367
+ position += entrySize;
1368
+ count++;
1369
+ }
1370
+ return count;
1371
+ }
1372
+
1200
1373
  async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1201
1374
  const {
1202
1375
  fileEntries,
@@ -1372,6 +1545,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1372
1545
  localExtraFieldZip64Length,
1373
1546
  rawExtraFieldExtendedTimestamp,
1374
1547
  extraFieldExtendedTimestampFlag,
1548
+ extraFieldExtendedTimestampTime,
1375
1549
  rawExtraFieldNTFS,
1376
1550
  rawExtraFieldUnix,
1377
1551
  rawExtraFieldAES,
@@ -1384,6 +1558,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1384
1558
  password,
1385
1559
  rawPassword,
1386
1560
  level,
1561
+ useUnicodeFileNames,
1387
1562
  zip64,
1388
1563
  zip64Enabled,
1389
1564
  zip64UncompressedSize,
@@ -1418,7 +1593,8 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1418
1593
  msdosAttributesRaw,
1419
1594
  msdosAttributes,
1420
1595
  useCompressionStream,
1421
- passThrough,
1596
+ passThroughCompression,
1597
+ passThroughEncryption,
1422
1598
  format,
1423
1599
  codecURI
1424
1600
  } = options;
@@ -1429,9 +1605,9 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1429
1605
  zip64Enabled,
1430
1606
  directory: Boolean(directory),
1431
1607
  executable: Boolean(executable),
1432
- filenameUTF8: true,
1608
+ filenameUTF8: Boolean(useUnicodeFileNames),
1433
1609
  rawFilename,
1434
- commentUTF8: true,
1610
+ commentUTF8: Boolean(useUnicodeFileNames),
1435
1611
  rawComment,
1436
1612
  rawExtraFieldZip64,
1437
1613
  localExtraFieldZip64Length,
@@ -1462,7 +1638,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1462
1638
  uncompressedSize
1463
1639
  } = options;
1464
1640
  let compressedSize = 0;
1465
- if (!passThrough) {
1641
+ if (!passThroughCompression) {
1466
1642
  uncompressedSize = 0;
1467
1643
  }
1468
1644
  const { writable } = writer;
@@ -1479,9 +1655,9 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1479
1655
  encryptionStrength,
1480
1656
  zipCrypto: encrypted && zipCrypto,
1481
1657
  passwordVerification: encrypted && zipCrypto && (rawLastModDate >> 8) & MAX_8_BITS,
1482
- computeCrc32: !passThrough,
1483
- compressed: compressed && !passThrough,
1484
- encrypted: encrypted && !passThrough,
1658
+ computeCrc32: !passThroughCompression,
1659
+ compressed: compressed && !passThroughCompression,
1660
+ encrypted: encrypted && !passThroughEncryption,
1485
1661
  useWebWorkers,
1486
1662
  useCompressionStream,
1487
1663
  transferStreams,
@@ -1496,7 +1672,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1496
1672
  const result = await runWorker({ readable, writable }, workerOptions);
1497
1673
  compressedSize = result.outputSize;
1498
1674
  writer.size += compressedSize;
1499
- if (!passThrough) {
1675
+ if (!passThroughCompression) {
1500
1676
  uncompressedSize = result.inputSize;
1501
1677
  if (!encrypted || zipCrypto) {
1502
1678
  crc32 = result.crc32;
@@ -1507,7 +1683,10 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1507
1683
  throw new Error(ERR_UNSUPPORTED_FORMAT);
1508
1684
  }
1509
1685
  } catch (error) {
1510
- if (error.outputSize !== UNDEFINED_VALUE) {
1686
+ const { outputSize: failedOutputSize } = workerOptions;
1687
+ if (failedOutputSize !== UNDEFINED_VALUE) {
1688
+ writer.size += failedOutputSize;
1689
+ } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
1511
1690
  writer.size += error.outputSize;
1512
1691
  }
1513
1692
  throw error;
@@ -1539,8 +1718,9 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1539
1718
  headerArray,
1540
1719
  headerView,
1541
1720
  signature: crc32,
1542
- crc32: encrypted && !zipCrypto && !passThrough ? UNDEFINED_VALUE : crc32,
1721
+ crc32: encrypted && !zipCrypto && !passThroughCompression ? UNDEFINED_VALUE : crc32,
1543
1722
  extraFieldExtendedTimestampFlag,
1723
+ extraFieldExtendedTimestampTime,
1544
1724
  zip64UncompressedSize,
1545
1725
  zip64CompressedSize
1546
1726
  });
@@ -1565,7 +1745,7 @@ function getHeaderInfo(options) {
1565
1745
  encryptionStrength,
1566
1746
  extendedTimestamp,
1567
1747
  ntfsTimestamp,
1568
- passThrough,
1748
+ passThroughCompression,
1569
1749
  encrypted,
1570
1750
  zip64UncompressedSize,
1571
1751
  zip64CompressedSize,
@@ -1576,7 +1756,7 @@ function getHeaderInfo(options) {
1576
1756
  let { version, compressionMethod } = options;
1577
1757
  const compressed = !directory && isCompressed(compressionMethod, level);
1578
1758
  let rawLocalExtraFieldZip64;
1579
- const uncompressedFile = passThrough || !compressed;
1759
+ const uncompressedFile = passThroughCompression || !compressed;
1580
1760
  const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || (uncompressedFile && !unknownSize)));
1581
1761
  const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
1582
1762
  if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
@@ -1588,8 +1768,8 @@ function getHeaderInfo(options) {
1588
1768
  if (zip64ExtraFieldComplete) {
1589
1769
  extraFieldZip64.writeUint64(uncompressedSize);
1590
1770
  if (uncompressedFile) {
1591
- const encryptionOverhead = encrypted ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
1592
- extraFieldZip64.writeUint64(passThrough ? 0 : uncompressedSize + encryptionOverhead);
1771
+ const encryptionOverhead = getEncryptionOverhead(encrypted, zipCrypto, encryptionStrength);
1772
+ extraFieldZip64.writeUint64(passThroughCompression ? 0 : uncompressedSize + encryptionOverhead);
1593
1773
  }
1594
1774
  }
1595
1775
  } else {
@@ -1608,6 +1788,7 @@ function getHeaderInfo(options) {
1608
1788
  let rawExtraFieldNTFS;
1609
1789
  let rawExtraFieldExtendedTimestamp;
1610
1790
  let extraFieldExtendedTimestampFlag;
1791
+ let extraFieldExtendedTimestampTime;
1611
1792
  if (extendedTimestamp) {
1612
1793
  const lastModTimeUnix = getTimeUnix(lastModDate);
1613
1794
  const lastModTimeUnixInRange = inUnixTimeRange(lastModTimeUnix);
@@ -1615,6 +1796,7 @@ function getHeaderInfo(options) {
1615
1796
  const extraFieldTimestampLength = 9 + (lastAccessDate ? 4 : 0) + (creationDate ? 4 : 0);
1616
1797
  const extraFieldTimestamp = createRecordWriter(extraFieldTimestampLength);
1617
1798
  extraFieldExtendedTimestampFlag = 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0);
1799
+ extraFieldExtendedTimestampTime = lastModTimeUnix;
1618
1800
  extraFieldTimestamp.writeUint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
1619
1801
  extraFieldTimestamp.writeUint16(extraFieldTimestampLength - 4);
1620
1802
  extraFieldTimestamp.writeUint8(extraFieldExtendedTimestampFlag);
@@ -1698,7 +1880,7 @@ function getHeaderInfo(options) {
1698
1880
  }
1699
1881
  if (encrypted && !zipCrypto) {
1700
1882
  version = version > VERSION_AES ? version : VERSION_AES;
1701
- if (passThrough && crc32 !== UNDEFINED_VALUE) {
1883
+ if (passThroughCompression && crc32 !== UNDEFINED_VALUE) {
1702
1884
  rawExtraFieldAES[EXTRAFIELD_OFFSET_AES_VENDOR_VERSION] = VENDOR_VERSION_AE_1;
1703
1885
  }
1704
1886
  setUint16(getDataView(rawExtraFieldAES), EXTRAFIELD_OFFSET_AES_COMPRESSION_METHOD, compressionMethod);
@@ -1760,12 +1942,14 @@ function getHeaderInfo(options) {
1760
1942
  headerArray,
1761
1943
  headerView,
1762
1944
  lastModDate: storedLastModDate,
1945
+ lastModDateClamped: storedLastModDate === clampedLastModDate && dosLastModDate.getTime() != clampedLastModDate.getTime(),
1763
1946
  rawLastModDate,
1764
1947
  encrypted,
1765
1948
  compressed,
1766
1949
  version,
1767
1950
  compressionMethod,
1768
1951
  extraFieldExtendedTimestampFlag,
1952
+ extraFieldExtendedTimestampTime,
1769
1953
  rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
1770
1954
  localExtraFieldZip64Length,
1771
1955
  rawExtraFieldExtendedTimestamp,
@@ -1868,7 +2052,7 @@ function setEntryInfo({
1868
2052
  }, {
1869
2053
  zip64,
1870
2054
  zipCrypto,
1871
- passThrough,
2055
+ passThroughCompression,
1872
2056
  dataDescriptor
1873
2057
  }) {
1874
2058
  const {
@@ -1879,7 +2063,7 @@ function setEntryInfo({
1879
2063
  dataDescriptorView,
1880
2064
  dataDescriptorOffset
1881
2065
  } = dataDescriptorInfo;
1882
- if ((!encrypted || zipCrypto || passThrough) && crc32 !== UNDEFINED_VALUE) {
2066
+ if ((!encrypted || zipCrypto || passThroughCompression) && crc32 !== UNDEFINED_VALUE) {
1883
2067
  setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
1884
2068
  if (dataDescriptor) {
1885
2069
  setUint32(dataDescriptorView, dataDescriptorOffset, crc32);
@@ -1910,9 +2094,9 @@ function updateLocalHeader({
1910
2094
  uncompressedSize,
1911
2095
  zip64UncompressedSize,
1912
2096
  zip64CompressedSize
1913
- }, localHeaderView, { dataDescriptor, passThrough }) {
2097
+ }, localHeaderView, { dataDescriptor, passThroughCompression }) {
1914
2098
  if (!dataDescriptor) {
1915
- if (!encrypted || (passThrough && crc32 !== UNDEFINED_VALUE)) {
2099
+ if (!encrypted || (passThroughCompression && crc32 !== UNDEFINED_VALUE)) {
1916
2100
  setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE + LOCAL_HEADER_COMMON_OFFSET, crc32);
1917
2101
  }
1918
2102
  if (!zip64CompressedSize) {
@@ -1949,9 +2133,8 @@ function createDirectoryRecords(files) {
1949
2133
  rawExtraFieldUnix,
1950
2134
  rawExtraField,
1951
2135
  rawCentralExtraField,
1952
- extendedTimestamp,
1953
2136
  extraFieldExtendedTimestampFlag,
1954
- lastModDate,
2137
+ extraFieldExtendedTimestampTime,
1955
2138
  zip64Enabled,
1956
2139
  uncompressedSize,
1957
2140
  compressedSize
@@ -1994,16 +2177,15 @@ function createDirectoryRecords(files) {
1994
2177
  fileEntry.zip64Offset = zip64Offset;
1995
2178
  fileEntry.zip64DiskNumberStart = zip64DiskNumberStart;
1996
2179
  let rawExtraFieldTimestamp;
1997
- const lastModTimeUnix = getTimeUnix(lastModDate);
1998
- if (extendedTimestamp && inUnixTimeRange(lastModTimeUnix)) {
2180
+ if (extraFieldExtendedTimestampTime === UNDEFINED_VALUE) {
2181
+ rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
2182
+ } else {
1999
2183
  const extraFieldTimestamp = createRecordWriter(9);
2000
2184
  extraFieldTimestamp.writeUint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
2001
2185
  extraFieldTimestamp.writeUint16(5);
2002
2186
  extraFieldTimestamp.writeUint8(extraFieldExtendedTimestampFlag);
2003
- extraFieldTimestamp.writeUint32(lastModTimeUnix);
2187
+ extraFieldTimestamp.writeUint32(extraFieldExtendedTimestampTime);
2004
2188
  rawExtraFieldTimestamp = extraFieldTimestamp.array;
2005
- } else {
2006
- rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
2007
2189
  }
2008
2190
  fileEntry.rawExtraFieldExtendedTimestamp = rawExtraFieldTimestamp;
2009
2191
  const extraFieldLength = getLength(
@@ -2503,6 +2685,11 @@ function getHeaderArrayData({
2503
2685
  };
2504
2686
  }
2505
2687
 
2688
+ function isPrintableASCIIText(rawText) {
2689
+ return rawText.every(characterCode =>
2690
+ characterCode >= MIN_PRINTABLE_ASCII_CHARACTER_CODE && characterCode <= MAX_PRINTABLE_ASCII_CHARACTER_CODE);
2691
+ }
2692
+
2506
2693
  function getBitFlag(level, useUnicodeFileNames, dataDescriptor, encrypted, compressionMethod) {
2507
2694
  let bitFlag = 0;
2508
2695
  if (useUnicodeFileNames) {