@zip.js/zip.js 2.11.3 → 2.12.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 (50) hide show
  1. package/deno.json +1 -1
  2. package/dist/zip-core-external.js +444 -114
  3. package/dist/zip-core-external.min.js +1 -1
  4. package/dist/zip-core.js +454 -111
  5. package/dist/zip-core.min.js +1 -1
  6. package/dist/zip-fs-core-external.js +509 -236
  7. package/dist/zip-fs-core-external.min.js +1 -1
  8. package/dist/zip-fs-core.js +522 -233
  9. package/dist/zip-fs-core.min.js +1 -1
  10. package/dist/zip-fs-external.js +509 -236
  11. package/dist/zip-fs-external.min.js +1 -1
  12. package/dist/zip-fs-native.js +524 -235
  13. package/dist/zip-fs-native.min.js +1 -1
  14. package/dist/zip-fs.js +525 -236
  15. package/dist/zip-fs.min.js +1 -1
  16. package/dist/zip-legacy.js +456 -113
  17. package/dist/zip-legacy.min.js +1 -1
  18. package/dist/zip-native.js +456 -113
  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 +457 -114
  23. package/dist/zip.min.js +1 -1
  24. package/index-native.cjs +524 -235
  25. package/index-native.min.js +1 -1
  26. package/index.cjs +525 -236
  27. package/index.d.cts +480 -59
  28. package/index.d.ts +480 -59
  29. package/index.min.js +1 -1
  30. package/lib/core/codec-worker-web.js +10 -2
  31. package/lib/core/codec-worker.js +4 -3
  32. package/lib/core/configuration.js +20 -5
  33. package/lib/core/constants.js +2 -0
  34. package/lib/core/io.js +20 -10
  35. package/lib/core/options.js +29 -0
  36. package/lib/core/streams/codec-stream.js +6 -0
  37. package/lib/core/util/warnings.js +42 -0
  38. package/lib/core/version.js +1 -1
  39. package/lib/core/web-worker-base.js +4 -3
  40. package/lib/core/web-worker-inline-native.js +1 -1
  41. package/lib/core/web-worker-inline-wasm.js +1 -1
  42. package/lib/core/zip-entry.js +49 -1
  43. package/lib/core/zip-fs.js +43 -134
  44. package/lib/core/zip-reader.js +108 -47
  45. package/lib/core/zip-writer.js +238 -54
  46. package/lib/zip-core-base.js +9 -3
  47. package/lib/zip-core-reader.js +2 -0
  48. package/lib/zip-core-writer.js +6 -1
  49. package/lib/zip-module-wasm-base.js +2 -2
  50. package/package.json +5 -3
@@ -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,6 +176,7 @@ 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";
179
181
  import {
180
182
  PROPERTY_NAME_LAST_MODIFICATION_DATE,
@@ -204,7 +206,9 @@ import {
204
206
  PROPERTY_NAME_STICKY,
205
207
  PROPERTY_NAME_MSDOS_ATTRIBUTES,
206
208
  PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW,
207
- Entry
209
+ Entry,
210
+ getEncryptionOverhead,
211
+ getUserExtraField
208
212
  } from "./zip-entry.js";
209
213
  import {
210
214
  OPTION_PASSWORD,
@@ -238,10 +242,14 @@ import {
238
242
  ERR_INVALID_PASSWORD_TYPE,
239
243
  checkFunctionOption,
240
244
  checkSignalOption,
245
+ throwIfAborted,
241
246
  checkPasswordOption,
247
+ checkPassThroughOption,
242
248
  checkInteger,
243
249
  checkIntegerOption,
244
- toNumber
250
+ toNumber,
251
+ OPTION_ENTRY,
252
+ PASS_THROUGH_COMPRESSED
245
253
  } from "./options.js";
246
254
 
247
255
  const ERR_DUPLICATED_NAME = "File already exists";
@@ -255,7 +263,7 @@ const ERR_INVALID_VERSION = "Version exceeds 65535";
255
263
  const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
256
264
  const ERR_UNSUPPORTED_ENCRYPTION_USDZ = "Encryption is not supported in USDZ files";
257
265
  const ERR_UNSUPPORTED_SPLIT_USDZ = "Split zip files are not supported in USDZ files";
258
- const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set";
266
+ const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set to true (use 'compressed' instead)";
259
267
  const ERR_INVALID_EXTRAFIELD = "Invalid extra field (must be a Map)";
260
268
  const ERR_INVALID_EXTRAFIELD_TYPE = "Invalid extra field type (must be integer 0..65535)";
261
269
  const ERR_INVALID_EXTRAFIELD_DATA_TYPE = "Invalid extra field data (must be a Uint8Array)";
@@ -267,6 +275,7 @@ const MAX_NTFS_TIME = BigInt("0x7fffffffffffffff");
267
275
  const ERR_UNSUPPORTED_FORMAT = "Zip64 is not supported (set the 'zip64' option to 'true')";
268
276
  const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
269
277
  const ERR_UNDEFINED_COMPRESSION_METHOD = "Undefined compression method";
278
+ const ERR_UNDEFINED_CRC32 = "Undefined CRC32";
270
279
  const ERR_UNDETERMINED_SIZE = "Undetermined size";
271
280
  const ERR_UNDEFINED_READER = "Undefined reader";
272
281
  const ERR_INVALID_READER = "Invalid reader (must be a Reader instance, a ReadableStream instance, or an object with a 'readable' property)";
@@ -280,14 +289,21 @@ const ERR_INVALID_MSDOS_ATTRIBUTES = "Invalid msdosAttributesRaw (must be intege
280
289
  const ERR_INVALID_MSDOS_DATA = "Invalid msdosAttributes (must be an object with boolean flags)";
281
290
  const ERR_INVALID_LEVEL = "Invalid level (must be integer 0..9)";
282
291
  const ERR_INVALID_SIGNATURE_DATA = "Signature data exceeds 64KB";
292
+ const ERR_INVALID_ENTRY = "Invalid entry option (must be an entry returned by ZipReader#getEntries())";
293
+ const ERR_ZIP_CRYPTO_LAST_MOD_DATE = "The last modification date of an entry encrypted with ZipCrypto cannot be changed when passThrough is set";
294
+ const WARNING_COMPRESSION_UNAVAILABLE = "compression unavailable";
295
+ const WARNING_CLAMPED_LAST_MODIFICATION_DATE = "clamped last modification date";
283
296
 
284
297
  const EXTRAFIELD_DATA_AES = new Uint8Array([0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00]);
285
298
  const EXTRAFIELD_OFFSET_AES_VENDOR_VERSION = 4;
286
299
  const EXTRAFIELD_OFFSET_AES_COMPRESSION_METHOD = 9;
287
300
  const EXTRAFIELD_USDZ_MAX_LENGTH = 67;
301
+ const MIN_PRINTABLE_ASCII_CHARACTER_CODE = 0x20;
302
+ const MAX_PRINTABLE_ASCII_CHARACTER_CODE = 0x7e;
288
303
  const VENDOR_VERSION_AE_1 = 1;
289
304
  const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
290
305
  const UNIX_EXTRA_FIELD_TYPE = "unix";
306
+ const LEVEL_BY_BITFLAG_LEVEL = [8, 9, 5, 3];
291
307
  const MAX_LEVEL = 9;
292
308
 
293
309
  let workers = 0;
@@ -314,9 +330,11 @@ class ZipWriter {
314
330
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
315
331
  pendingAddFileCalls: new Set(),
316
332
  pendingErrors: [],
333
+ warnings: [],
317
334
  bufferedWrites: 0,
318
335
  directWrites: 0,
319
- lastFileEntry: UNDEFINED_VALUE
336
+ lastFileEntry: UNDEFINED_VALUE,
337
+ archiveClosed: false
320
338
  });
321
339
  }
322
340
 
@@ -481,6 +499,9 @@ class ZipWriter {
481
499
  const zipWriter = this;
482
500
  const { pendingAddFileCalls, writer } = this;
483
501
  const { writable } = writer;
502
+ if (zipWriter.archiveClosed) {
503
+ return getWriterData(writer);
504
+ }
484
505
  if (!(comment instanceof Uint8Array)) {
485
506
  throw new Error(ERR_INVALID_COMMENT_TYPE);
486
507
  }
@@ -504,11 +525,16 @@ class ZipWriter {
504
525
  throw error;
505
526
  }
506
527
  await closeFile(zipWriter, comment, options);
528
+ zipWriter.archiveClosed = true;
507
529
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
508
530
  if (!preventClose) {
509
531
  await writable.getWriter().close();
510
532
  }
511
- return writer.getData ? writer.getData() : writable;
533
+ return getWriterData(writer);
534
+ }
535
+
536
+ [SYMBOL_ASYNC_DISPOSE]() {
537
+ return this.close();
512
538
  }
513
539
  }
514
540
 
@@ -587,6 +613,10 @@ class WatchedPromise extends Promise {
587
613
  }
588
614
  }
589
615
 
616
+ function getWriterData(writer) {
617
+ return writer.getData ? writer.getData() : writer.writable;
618
+ }
619
+
590
620
  function watchPromiseError(zipWriter, promise) {
591
621
  const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
592
622
  const watcher = {};
@@ -605,6 +635,14 @@ async function prependZipEntries(zipWriter, reader) {
605
635
 
606
636
  async function addFileEntry(zipWriter, name, reader, options) {
607
637
  options = Object.assign({}, options);
638
+ const entry = options[OPTION_ENTRY];
639
+ if (entry !== UNDEFINED_VALUE) {
640
+ const { entryOptions, passThroughOptions } = getSourceEntryOptions(entry,
641
+ checkPassThroughOption(getOptionValue(zipWriter, options, OPTION_PASS_THROUGH)),
642
+ getOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE));
643
+ delete options[OPTION_ENTRY];
644
+ options = Object.assign(entryOptions, passThroughOptions, options);
645
+ }
608
646
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
609
647
  name += DIRECTORY_SIGNATURE;
610
648
  }
@@ -655,6 +693,7 @@ export {
655
693
  ZipWriter,
656
694
  ZipWriterStream,
657
695
  getEntriesSize,
696
+ getSourceEntryOptions,
658
697
  ERR_DUPLICATED_NAME,
659
698
  ERR_INVALID_COMMENT,
660
699
  ERR_INVALID_COMMENT_TYPE,
@@ -674,11 +713,14 @@ export {
674
713
  ERR_UNSUPPORTED_FORMAT,
675
714
  ERR_UNDEFINED_UNCOMPRESSED_SIZE,
676
715
  ERR_UNDEFINED_COMPRESSION_METHOD,
716
+ ERR_UNDEFINED_CRC32,
677
717
  ERR_UNDETERMINED_SIZE,
678
718
  ERR_UNDEFINED_READER,
679
719
  ERR_INVALID_READER,
680
720
  ERR_ZIP_NOT_EMPTY,
681
721
  ERR_INVALID_SIGNATURE_DATA,
722
+ ERR_INVALID_ENTRY,
723
+ ERR_ZIP_CRYPTO_LAST_MOD_DATE,
682
724
  ERR_INVALID_UID,
683
725
  ERR_INVALID_GID,
684
726
  ERR_INVALID_UNIX_MODE,
@@ -688,7 +730,9 @@ export {
688
730
  ERR_INVALID_MSDOS_DATA,
689
731
  ERR_INVALID_LEVEL,
690
732
  ERR_INVALID_PASSWORD_TYPE,
691
- ERR_UNSUPPORTED_CRYPTO_API
733
+ ERR_UNSUPPORTED_CRYPTO_API,
734
+ WARNING_COMPRESSION_UNAVAILABLE,
735
+ WARNING_CLAMPED_LAST_MODIFICATION_DATE
692
736
  };
693
737
 
694
738
  async function addFile(zipWriter, name, reader, options) {
@@ -709,21 +753,33 @@ async function addFile(zipWriter, name, reader, options) {
709
753
  try {
710
754
  const { resolvedOptions } = metadataInfo;
711
755
  if (resolvedOptions.level != 0 && resolvedOptions.compressionMethod === UNDEFINED_VALUE &&
712
- !resolvedOptions.passThrough && !(await supportsDeflate(getConfiguration()))) {
756
+ !resolvedOptions.passThroughCompression && !(await supportsDeflate(getConfiguration()))) {
713
757
  resolvedOptions.level = 0;
758
+ addWarning(zipWriter.warnings, WARNING_COMPRESSION_UNAVAILABLE, name);
714
759
  }
715
760
  const sizesInfo = await resolveSizes(zipWriter, reader, metadataInfo, options);
716
761
  ({ reader } = sizesInfo);
717
762
  const diskOffset = getDiskOffset(zipWriter.writer);
718
763
  const diskNumber = getDiskNumber(zipWriter.writer);
764
+ let crc32 = options.crc32 === UNDEFINED_VALUE ? options[PROPERTY_NAME_SIGNATURE] : options.crc32;
765
+ const storesAE2 = sizesInfo.resolvedOptions.encrypted && !resolvedOptions.zipCrypto;
766
+ if (resolvedOptions.passThroughCompression && !resolvedOptions.passThroughEncryption && storesAE2) {
767
+ crc32 = UNDEFINED_VALUE;
768
+ }
769
+ if (resolvedOptions.passThroughCompression && reader && !storesAE2 && crc32 === UNDEFINED_VALUE) {
770
+ throw new Error(ERR_UNDEFINED_CRC32);
771
+ }
719
772
  options = Object.assign({}, options, attributesInfo.resolvedOptions, metadataInfo.resolvedOptions, sizesInfo.resolvedOptions, {
720
773
  signature: options[PROPERTY_NAME_SIGNATURE],
721
- crc32: options.crc32 === UNDEFINED_VALUE ? options[PROPERTY_NAME_SIGNATURE] : options.crc32,
774
+ crc32,
722
775
  offset: zipWriter.offset - diskOffset,
723
776
  diskNumberStart: diskNumber,
724
777
  [OPTION_USDZ]: zipWriter.options[OPTION_USDZ]
725
778
  });
726
779
  const headerInfo = getHeaderInfo(options);
780
+ if (headerInfo.lastModDateClamped) {
781
+ addWarning(zipWriter.warnings, WARNING_CLAMPED_LAST_MODIFICATION_DATE, name);
782
+ }
727
783
  const dataDescriptorInfo = getDataDescriptorInfo(options);
728
784
  const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
729
785
  fileEntry = await getFileEntry(zipWriter, name, reader, {
@@ -750,6 +806,95 @@ async function addFile(zipWriter, name, reader, options) {
750
806
  return new Entry(fileEntry);
751
807
  }
752
808
 
809
+ function getSourceEntryOptions(entry, passThrough, lastModDateOverride) {
810
+ if (entry === null || typeof entry != OBJECT_TYPE || Array.isArray(entry)) {
811
+ throw new Error(ERR_INVALID_ENTRY);
812
+ }
813
+ const {
814
+ externalFileAttributes,
815
+ versionMadeBy,
816
+ comment,
817
+ lastModDate,
818
+ rawLastModDate,
819
+ creationDate,
820
+ lastAccessDate,
821
+ uncompressedSize,
822
+ encrypted,
823
+ zipCrypto,
824
+ crc32,
825
+ compressionMethod,
826
+ extraFieldAES,
827
+ extraFieldUnix,
828
+ internalFileAttributes,
829
+ extraField,
830
+ bitFlag,
831
+ directory,
832
+ uid,
833
+ gid
834
+ } = entry;
835
+ const entryOptions = {
836
+ externalFileAttributes,
837
+ versionMadeBy,
838
+ comment,
839
+ lastModDate,
840
+ creationDate,
841
+ lastAccessDate,
842
+ internalFileAttributes,
843
+ directory
844
+ };
845
+ if (bitFlag && bitFlag.languageEncodingFlag) {
846
+ entryOptions[OPTION_USE_UNICODE_FILE_NAMES] = true;
847
+ }
848
+ const userExtraField = getUserExtraField(extraField);
849
+ if (userExtraField) {
850
+ entryOptions[PROPERTY_NAME_EXTRA_FIELD] = userExtraField;
851
+ }
852
+ if (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE) {
853
+ Object.assign(entryOptions, {
854
+ uid,
855
+ gid,
856
+ unixExtraFieldType: extraFieldUnix ? UNIX_EXTRA_FIELD_TYPE : INFOZIP_EXTRA_FIELD_TYPE
857
+ });
858
+ }
859
+ const passThroughOptions = {};
860
+ if (passThrough && !directory) {
861
+ Object.assign(passThroughOptions, {
862
+ uncompressedSize,
863
+ crc32,
864
+ compressionMethod
865
+ });
866
+ if (passThrough !== PASS_THROUGH_COMPRESSED) {
867
+ Object.assign(passThroughOptions, {
868
+ encrypted,
869
+ zipCrypto,
870
+ encryptionStrength: extraFieldAES ? extraFieldAES.strength : UNDEFINED_VALUE
871
+ });
872
+ }
873
+ if (bitFlag) {
874
+ passThroughOptions.dataDescriptor = bitFlag.dataDescriptor;
875
+ passThroughOptions[OPTION_LEVEL] = LEVEL_BY_BITFLAG_LEVEL[bitFlag.level];
876
+ }
877
+ if (lastModDateOverride === UNDEFINED_VALUE) {
878
+ passThroughOptions.rawLastModDate = rawLastModDate;
879
+ } else if (passThrough !== PASS_THROUGH_COMPRESSED && zipCrypto && (!bitFlag || bitFlag.dataDescriptor) &&
880
+ lastModDateOverride instanceof Date &&
881
+ getDosTimeHighByte(lastModDateOverride) != ((rawLastModDate >>> 8) & MAX_8_BITS)) {
882
+ throw new Error(ERR_ZIP_CRYPTO_LAST_MOD_DATE);
883
+ }
884
+ }
885
+ return { entryOptions, passThroughOptions };
886
+ }
887
+
888
+ function getDosTimeHighByte(lastModDate) {
889
+ let dosLastModDate = new Date(Math.ceil(Math.floor(lastModDate.getTime() / 1000) / 2) * 2000);
890
+ if (dosLastModDate < MIN_DATE) {
891
+ dosLastModDate = MIN_DATE;
892
+ } else if (dosLastModDate > MAX_DATE) {
893
+ dosLastModDate = MAX_DATE;
894
+ }
895
+ return ((dosLastModDate.getHours() << 3) | (dosLastModDate.getMinutes() >> 3)) & MAX_8_BITS;
896
+ }
897
+
753
898
  function resolveAttributes(zipWriter, name, options) {
754
899
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
755
900
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
@@ -903,7 +1048,9 @@ function resolveMetadata(zipWriter, name, options) {
903
1048
  const lastAccessDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
904
1049
  const creationDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
905
1050
  const internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, 0);
906
- const passThrough = getOptionValue(zipWriter, options, OPTION_PASS_THROUGH);
1051
+ const passThrough = checkPassThroughOption(getOptionValue(zipWriter, options, OPTION_PASS_THROUGH));
1052
+ const passThroughCompression = Boolean(passThrough);
1053
+ const passThroughEncryption = passThrough === true;
907
1054
  const password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
908
1055
  const rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
909
1056
  checkPasswordOption(password, rawPassword);
@@ -918,10 +1065,12 @@ function resolveMetadata(zipWriter, name, options) {
918
1065
  const createTempStream = getFunctionOptionValue(zipWriter, options, OPTION_CREATE_TEMP_STREAM);
919
1066
  const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, true);
920
1067
  const signal = checkSignalOption(getOptionValue(zipWriter, options, OPTION_SIGNAL));
921
- const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES, true);
1068
+ throwIfAborted(signal);
1069
+ const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES,
1070
+ !isPrintableASCIIText(rawFilename) || !isPrintableASCIIText(rawComment));
922
1071
  const compressionMethod = getOptionValue(zipWriter, options, PROPERTY_NAME_COMPRESSION_METHOD);
923
- const registeredCodec = passThrough || compressionMethod === UNDEFINED_VALUE ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
924
- if (!passThrough && compressionMethod !== UNDEFINED_VALUE &&
1072
+ const registeredCodec = passThroughCompression || compressionMethod === UNDEFINED_VALUE ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
1073
+ if (!passThroughCompression && compressionMethod !== UNDEFINED_VALUE &&
925
1074
  compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE && !registeredCodec) {
926
1075
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
927
1076
  }
@@ -935,15 +1084,15 @@ function resolveMetadata(zipWriter, name, options) {
935
1084
  level = 0;
936
1085
  }
937
1086
  }
938
- if (passThrough) {
939
- level = UNDEFINED_VALUE;
1087
+ if (passThroughCompression) {
1088
+ level = toNumber(options[OPTION_LEVEL]);
940
1089
  }
941
1090
  let useCompressionStream = getOptionValue(zipWriter, options, OPTION_USE_COMPRESSION_STREAM);
942
1091
  let dataDescriptor = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR);
943
1092
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
944
1093
  dataDescriptor = false;
945
1094
  }
946
- if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThrough)) {
1095
+ if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThroughEncryption)) {
947
1096
  dataDescriptor = true;
948
1097
  }
949
1098
  if (level !== UNDEFINED_VALUE && level != 6) {
@@ -967,7 +1116,8 @@ function resolveMetadata(zipWriter, name, options) {
967
1116
  lastAccessDate,
968
1117
  creationDate,
969
1118
  internalFileAttributes,
970
- passThrough,
1119
+ passThroughCompression,
1120
+ passThroughEncryption,
971
1121
  password,
972
1122
  rawPassword,
973
1123
  encryptionStrength,
@@ -1028,7 +1178,7 @@ function serializeExtraField(extraField) {
1028
1178
  }
1029
1179
 
1030
1180
  async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, options) {
1031
- if (metadata.passThrough && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
1181
+ if (metadata.passThroughCompression && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
1032
1182
  throw new Error(ERR_UNDEFINED_READER);
1033
1183
  }
1034
1184
  let contentSize;
@@ -1044,12 +1194,12 @@ async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, op
1044
1194
  }
1045
1195
 
1046
1196
  function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options) {
1047
- const { passThrough, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
1197
+ const { passThroughCompression, passThroughEncryption, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
1048
1198
  let { dataDescriptor, zip64, level, compressionMethod } = metadata;
1049
1199
  let maximumCompressedSize = 0;
1050
1200
  let uncompressedSize = 0;
1051
1201
  let unknownSize = false;
1052
- if (passThrough && hasContent) {
1202
+ if (passThroughCompression && hasContent) {
1053
1203
  uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
1054
1204
  if (uncompressedSize === UNDEFINED_VALUE) {
1055
1205
  throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
@@ -1060,17 +1210,17 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1060
1210
  }
1061
1211
  const zip64Enabled = zip64 === true;
1062
1212
  const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
1063
- if (hasContent && passThrough && !encrypted && getLength(password, rawPassword)) {
1213
+ if (hasContent && passThroughEncryption && !encrypted && getLength(password, rawPassword)) {
1064
1214
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH);
1065
1215
  }
1066
- const encryptedEntry = hasContent && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
1216
+ const encryptedEntry = hasContent && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThroughEncryption && encrypted));
1067
1217
  if (!hasContent) {
1068
1218
  level = 0;
1069
1219
  compressionMethod = COMPRESSION_METHOD_STORE;
1070
1220
  }
1071
- const encryptionOverhead = encryptedEntry ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
1221
+ const encryptionOverhead = getEncryptionOverhead(encryptedEntry, zipCrypto, encryptionStrength);
1072
1222
  if (hasContent) {
1073
- if (!passThrough) {
1223
+ if (!passThroughCompression) {
1074
1224
  if (contentSize === UNDEFINED_VALUE) {
1075
1225
  dataDescriptor = true;
1076
1226
  if (zip64 || zip64 === UNDEFINED_VALUE) {
@@ -1083,10 +1233,12 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1083
1233
  }
1084
1234
  } else {
1085
1235
  options.uncompressedSize = uncompressedSize;
1086
- maximumCompressedSize = contentSize === UNDEFINED_VALUE ? getMaximumCompressedSize(uncompressedSize) + encryptionOverhead : contentSize;
1236
+ maximumCompressedSize = contentSize === UNDEFINED_VALUE ?
1237
+ getMaximumCompressedSize(uncompressedSize) + encryptionOverhead :
1238
+ contentSize + (passThroughEncryption ? 0 : encryptionOverhead);
1087
1239
  }
1088
1240
  }
1089
- const emptyEntry = !encryptedEntry && (!hasContent || (contentSize === 0 && !passThrough)) && !isCompressed(compressionMethod, level);
1241
+ const emptyEntry = !encryptedEntry && (!hasContent || (contentSize === 0 && !passThroughCompression)) && !isCompressed(compressionMethod, level);
1090
1242
  if (emptyEntry && !zipCrypto && getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR) === UNDEFINED_VALUE) {
1091
1243
  dataDescriptor = false;
1092
1244
  }
@@ -1135,6 +1287,8 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1135
1287
  const initialOffset = writerOptions[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : writerOptions[OPTION_OFFSET];
1136
1288
  let offset = initialOffset;
1137
1289
  let minimumEntrySize = INFINITY_VALUE;
1290
+ const entrySizes = [];
1291
+ const entriesAlreadyZip64 = new Set();
1138
1292
  for (const entry of entries) {
1139
1293
  let { name } = entry;
1140
1294
  const { size } = entry;
@@ -1146,7 +1300,7 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1146
1300
  ({ name } = attributesInfo);
1147
1301
  const { resolvedOptions: metadata } = resolveMetadata(zipWriter, name, options);
1148
1302
  if (metadata.level != 0 && metadata.compressionMethod === UNDEFINED_VALUE &&
1149
- !metadata.passThrough && !(await supportsDeflate(getConfiguration()))) {
1303
+ !metadata.passThroughCompression && !(await supportsDeflate(getConfiguration()))) {
1150
1304
  metadata.level = 0;
1151
1305
  }
1152
1306
  const hasContent = !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY);
@@ -1154,7 +1308,7 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1154
1308
  throw new Error(ERR_UNDETERMINED_SIZE);
1155
1309
  }
1156
1310
  const { maximumCompressedSize, resolvedOptions: sizes } = resolveEntrySizes(zipWriter, hasContent, size, metadata, options);
1157
- if (hasContent && !metadata.passThrough && isCompressed(sizes.compressionMethod, sizes.level)) {
1311
+ if (hasContent && !metadata.passThroughCompression && isCompressed(sizes.compressionMethod, sizes.level)) {
1158
1312
  throw new Error(ERR_UNDETERMINED_SIZE);
1159
1313
  }
1160
1314
  const entryOptions = Object.assign({}, options, attributesInfo.resolvedOptions, metadata, sizes, { [OPTION_USDZ]: usdz });
@@ -1174,10 +1328,15 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1174
1328
  compressedSize
1175
1329
  }));
1176
1330
  const entrySize = entryInfo.metadataSize + compressedSize;
1331
+ entrySizes.push(entrySize);
1332
+ entriesAlreadyZip64.add(Boolean(entryOptions.zip64Enabled ||
1333
+ entryOptions.uncompressedSize >= MAX_32_BITS || compressedSize >= MAX_32_BITS));
1177
1334
  minimumEntrySize = Math.min(minimumEntrySize, entrySize);
1178
1335
  offset += entrySize;
1179
1336
  }
1180
- const layoutDependsOnWriteOrder = files.size > 1 && (usdz || offset - minimumEntrySize >= MAX_32_BITS);
1337
+ const layoutDependsOnWriteOrder = files.size > 1 && (usdz ||
1338
+ (offset - minimumEntrySize >= MAX_32_BITS &&
1339
+ (entriesAlreadyZip64.size > 1 || countEntriesBefore4GB(entrySizes, initialOffset, true) != countEntriesBefore4GB(entrySizes, initialOffset, false))));
1181
1340
  if (layoutDependsOnWriteOrder && !writeOrderGuaranteed) {
1182
1341
  throw new Error(ERR_UNDETERMINED_SIZE);
1183
1342
  }
@@ -1195,6 +1354,20 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1195
1354
  return offset - initialOffset + directoryDataLength + commentLength + (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
1196
1355
  }
1197
1356
 
1357
+ function countEntriesBefore4GB(entrySizes, initialOffset, largestFirst) {
1358
+ const ordered = entrySizes.slice().sort((first, second) => largestFirst ? second - first : first - second);
1359
+ let position = initialOffset;
1360
+ let count = 0;
1361
+ for (const entrySize of ordered) {
1362
+ if (position >= MAX_32_BITS) {
1363
+ break;
1364
+ }
1365
+ position += entrySize;
1366
+ count++;
1367
+ }
1368
+ return count;
1369
+ }
1370
+
1198
1371
  async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1199
1372
  const {
1200
1373
  fileEntries,
@@ -1370,6 +1543,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1370
1543
  localExtraFieldZip64Length,
1371
1544
  rawExtraFieldExtendedTimestamp,
1372
1545
  extraFieldExtendedTimestampFlag,
1546
+ extraFieldExtendedTimestampTime,
1373
1547
  rawExtraFieldNTFS,
1374
1548
  rawExtraFieldUnix,
1375
1549
  rawExtraFieldAES,
@@ -1382,6 +1556,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1382
1556
  password,
1383
1557
  rawPassword,
1384
1558
  level,
1559
+ useUnicodeFileNames,
1385
1560
  zip64,
1386
1561
  zip64Enabled,
1387
1562
  zip64UncompressedSize,
@@ -1416,7 +1591,8 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1416
1591
  msdosAttributesRaw,
1417
1592
  msdosAttributes,
1418
1593
  useCompressionStream,
1419
- passThrough,
1594
+ passThroughCompression,
1595
+ passThroughEncryption,
1420
1596
  format,
1421
1597
  codecURI
1422
1598
  } = options;
@@ -1427,9 +1603,9 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1427
1603
  zip64Enabled,
1428
1604
  directory: Boolean(directory),
1429
1605
  executable: Boolean(executable),
1430
- filenameUTF8: true,
1606
+ filenameUTF8: Boolean(useUnicodeFileNames),
1431
1607
  rawFilename,
1432
- commentUTF8: true,
1608
+ commentUTF8: Boolean(useUnicodeFileNames),
1433
1609
  rawComment,
1434
1610
  rawExtraFieldZip64,
1435
1611
  localExtraFieldZip64Length,
@@ -1460,7 +1636,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1460
1636
  uncompressedSize
1461
1637
  } = options;
1462
1638
  let compressedSize = 0;
1463
- if (!passThrough) {
1639
+ if (!passThroughCompression) {
1464
1640
  uncompressedSize = 0;
1465
1641
  }
1466
1642
  const { writable } = writer;
@@ -1477,9 +1653,9 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1477
1653
  encryptionStrength,
1478
1654
  zipCrypto: encrypted && zipCrypto,
1479
1655
  passwordVerification: encrypted && zipCrypto && (rawLastModDate >> 8) & MAX_8_BITS,
1480
- computeCrc32: !passThrough,
1481
- compressed: compressed && !passThrough,
1482
- encrypted: encrypted && !passThrough,
1656
+ computeCrc32: !passThroughCompression,
1657
+ compressed: compressed && !passThroughCompression,
1658
+ encrypted: encrypted && !passThroughEncryption,
1483
1659
  useWebWorkers,
1484
1660
  useCompressionStream,
1485
1661
  transferStreams,
@@ -1494,7 +1670,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1494
1670
  const result = await runWorker({ readable, writable }, workerOptions);
1495
1671
  compressedSize = result.outputSize;
1496
1672
  writer.size += compressedSize;
1497
- if (!passThrough) {
1673
+ if (!passThroughCompression) {
1498
1674
  uncompressedSize = result.inputSize;
1499
1675
  if (!encrypted || zipCrypto) {
1500
1676
  crc32 = result.crc32;
@@ -1537,8 +1713,9 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1537
1713
  headerArray,
1538
1714
  headerView,
1539
1715
  signature: crc32,
1540
- crc32: encrypted && !zipCrypto && !passThrough ? UNDEFINED_VALUE : crc32,
1716
+ crc32: encrypted && !zipCrypto && !passThroughCompression ? UNDEFINED_VALUE : crc32,
1541
1717
  extraFieldExtendedTimestampFlag,
1718
+ extraFieldExtendedTimestampTime,
1542
1719
  zip64UncompressedSize,
1543
1720
  zip64CompressedSize
1544
1721
  });
@@ -1563,7 +1740,7 @@ function getHeaderInfo(options) {
1563
1740
  encryptionStrength,
1564
1741
  extendedTimestamp,
1565
1742
  ntfsTimestamp,
1566
- passThrough,
1743
+ passThroughCompression,
1567
1744
  encrypted,
1568
1745
  zip64UncompressedSize,
1569
1746
  zip64CompressedSize,
@@ -1574,7 +1751,7 @@ function getHeaderInfo(options) {
1574
1751
  let { version, compressionMethod } = options;
1575
1752
  const compressed = !directory && isCompressed(compressionMethod, level);
1576
1753
  let rawLocalExtraFieldZip64;
1577
- const uncompressedFile = passThrough || !compressed;
1754
+ const uncompressedFile = passThroughCompression || !compressed;
1578
1755
  const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || (uncompressedFile && !unknownSize)));
1579
1756
  const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
1580
1757
  if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
@@ -1586,8 +1763,8 @@ function getHeaderInfo(options) {
1586
1763
  if (zip64ExtraFieldComplete) {
1587
1764
  extraFieldZip64.writeUint64(uncompressedSize);
1588
1765
  if (uncompressedFile) {
1589
- const encryptionOverhead = encrypted ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
1590
- extraFieldZip64.writeUint64(passThrough ? 0 : uncompressedSize + encryptionOverhead);
1766
+ const encryptionOverhead = getEncryptionOverhead(encrypted, zipCrypto, encryptionStrength);
1767
+ extraFieldZip64.writeUint64(passThroughCompression ? 0 : uncompressedSize + encryptionOverhead);
1591
1768
  }
1592
1769
  }
1593
1770
  } else {
@@ -1606,6 +1783,7 @@ function getHeaderInfo(options) {
1606
1783
  let rawExtraFieldNTFS;
1607
1784
  let rawExtraFieldExtendedTimestamp;
1608
1785
  let extraFieldExtendedTimestampFlag;
1786
+ let extraFieldExtendedTimestampTime;
1609
1787
  if (extendedTimestamp) {
1610
1788
  const lastModTimeUnix = getTimeUnix(lastModDate);
1611
1789
  const lastModTimeUnixInRange = inUnixTimeRange(lastModTimeUnix);
@@ -1613,6 +1791,7 @@ function getHeaderInfo(options) {
1613
1791
  const extraFieldTimestampLength = 9 + (lastAccessDate ? 4 : 0) + (creationDate ? 4 : 0);
1614
1792
  const extraFieldTimestamp = createRecordWriter(extraFieldTimestampLength);
1615
1793
  extraFieldExtendedTimestampFlag = 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0);
1794
+ extraFieldExtendedTimestampTime = lastModTimeUnix;
1616
1795
  extraFieldTimestamp.writeUint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
1617
1796
  extraFieldTimestamp.writeUint16(extraFieldTimestampLength - 4);
1618
1797
  extraFieldTimestamp.writeUint8(extraFieldExtendedTimestampFlag);
@@ -1696,7 +1875,7 @@ function getHeaderInfo(options) {
1696
1875
  }
1697
1876
  if (encrypted && !zipCrypto) {
1698
1877
  version = version > VERSION_AES ? version : VERSION_AES;
1699
- if (passThrough && crc32 !== UNDEFINED_VALUE) {
1878
+ if (passThroughCompression && crc32 !== UNDEFINED_VALUE) {
1700
1879
  rawExtraFieldAES[EXTRAFIELD_OFFSET_AES_VENDOR_VERSION] = VENDOR_VERSION_AE_1;
1701
1880
  }
1702
1881
  setUint16(getDataView(rawExtraFieldAES), EXTRAFIELD_OFFSET_AES_COMPRESSION_METHOD, compressionMethod);
@@ -1758,12 +1937,14 @@ function getHeaderInfo(options) {
1758
1937
  headerArray,
1759
1938
  headerView,
1760
1939
  lastModDate: storedLastModDate,
1940
+ lastModDateClamped: storedLastModDate === clampedLastModDate && dosLastModDate.getTime() != clampedLastModDate.getTime(),
1761
1941
  rawLastModDate,
1762
1942
  encrypted,
1763
1943
  compressed,
1764
1944
  version,
1765
1945
  compressionMethod,
1766
1946
  extraFieldExtendedTimestampFlag,
1947
+ extraFieldExtendedTimestampTime,
1767
1948
  rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
1768
1949
  localExtraFieldZip64Length,
1769
1950
  rawExtraFieldExtendedTimestamp,
@@ -1866,7 +2047,7 @@ function setEntryInfo({
1866
2047
  }, {
1867
2048
  zip64,
1868
2049
  zipCrypto,
1869
- passThrough,
2050
+ passThroughCompression,
1870
2051
  dataDescriptor
1871
2052
  }) {
1872
2053
  const {
@@ -1877,7 +2058,7 @@ function setEntryInfo({
1877
2058
  dataDescriptorView,
1878
2059
  dataDescriptorOffset
1879
2060
  } = dataDescriptorInfo;
1880
- if ((!encrypted || zipCrypto || passThrough) && crc32 !== UNDEFINED_VALUE) {
2061
+ if ((!encrypted || zipCrypto || passThroughCompression) && crc32 !== UNDEFINED_VALUE) {
1881
2062
  setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
1882
2063
  if (dataDescriptor) {
1883
2064
  setUint32(dataDescriptorView, dataDescriptorOffset, crc32);
@@ -1908,9 +2089,9 @@ function updateLocalHeader({
1908
2089
  uncompressedSize,
1909
2090
  zip64UncompressedSize,
1910
2091
  zip64CompressedSize
1911
- }, localHeaderView, { dataDescriptor, passThrough }) {
2092
+ }, localHeaderView, { dataDescriptor, passThroughCompression }) {
1912
2093
  if (!dataDescriptor) {
1913
- if (!encrypted || (passThrough && crc32 !== UNDEFINED_VALUE)) {
2094
+ if (!encrypted || (passThroughCompression && crc32 !== UNDEFINED_VALUE)) {
1914
2095
  setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE + LOCAL_HEADER_COMMON_OFFSET, crc32);
1915
2096
  }
1916
2097
  if (!zip64CompressedSize) {
@@ -1947,9 +2128,8 @@ function createDirectoryRecords(files) {
1947
2128
  rawExtraFieldUnix,
1948
2129
  rawExtraField,
1949
2130
  rawCentralExtraField,
1950
- extendedTimestamp,
1951
2131
  extraFieldExtendedTimestampFlag,
1952
- lastModDate,
2132
+ extraFieldExtendedTimestampTime,
1953
2133
  zip64Enabled,
1954
2134
  uncompressedSize,
1955
2135
  compressedSize
@@ -1992,16 +2172,15 @@ function createDirectoryRecords(files) {
1992
2172
  fileEntry.zip64Offset = zip64Offset;
1993
2173
  fileEntry.zip64DiskNumberStart = zip64DiskNumberStart;
1994
2174
  let rawExtraFieldTimestamp;
1995
- const lastModTimeUnix = getTimeUnix(lastModDate);
1996
- if (extendedTimestamp && inUnixTimeRange(lastModTimeUnix)) {
2175
+ if (extraFieldExtendedTimestampTime === UNDEFINED_VALUE) {
2176
+ rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
2177
+ } else {
1997
2178
  const extraFieldTimestamp = createRecordWriter(9);
1998
2179
  extraFieldTimestamp.writeUint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
1999
2180
  extraFieldTimestamp.writeUint16(5);
2000
2181
  extraFieldTimestamp.writeUint8(extraFieldExtendedTimestampFlag);
2001
- extraFieldTimestamp.writeUint32(lastModTimeUnix);
2182
+ extraFieldTimestamp.writeUint32(extraFieldExtendedTimestampTime);
2002
2183
  rawExtraFieldTimestamp = extraFieldTimestamp.array;
2003
- } else {
2004
- rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
2005
2184
  }
2006
2185
  fileEntry.rawExtraFieldExtendedTimestamp = rawExtraFieldTimestamp;
2007
2186
  const extraFieldLength = getLength(
@@ -2501,6 +2680,11 @@ function getHeaderArrayData({
2501
2680
  };
2502
2681
  }
2503
2682
 
2683
+ function isPrintableASCIIText(rawText) {
2684
+ return rawText.every(characterCode =>
2685
+ characterCode >= MIN_PRINTABLE_ASCII_CHARACTER_CODE && characterCode <= MAX_PRINTABLE_ASCII_CHARACTER_CODE);
2686
+ }
2687
+
2504
2688
  function getBitFlag(level, useUnicodeFileNames, dataDescriptor, encrypted, compressionMethod) {
2505
2689
  let bitFlag = 0;
2506
2690
  if (useUnicodeFileNames) {