@zip.js/zip.js 2.11.4 → 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 +427 -110
  3. package/dist/zip-core-external.min.js +1 -1
  4. package/dist/zip-core.js +437 -107
  5. package/dist/zip-core.min.js +1 -1
  6. package/dist/zip-fs-core-external.js +492 -230
  7. package/dist/zip-fs-core-external.min.js +1 -1
  8. package/dist/zip-fs-core.js +505 -227
  9. package/dist/zip-fs-core.min.js +1 -1
  10. package/dist/zip-fs-external.js +492 -230
  11. package/dist/zip-fs-external.min.js +1 -1
  12. package/dist/zip-fs-native.js +507 -229
  13. package/dist/zip-fs-native.min.js +1 -1
  14. package/dist/zip-fs.js +508 -230
  15. package/dist/zip-fs.min.js +1 -1
  16. package/dist/zip-legacy.js +439 -109
  17. package/dist/zip-legacy.min.js +1 -1
  18. package/dist/zip-native.js +439 -109
  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 +440 -110
  23. package/dist/zip.min.js +1 -1
  24. package/index-native.cjs +507 -229
  25. package/index-native.min.js +1 -1
  26. package/index.cjs +508 -230
  27. package/index.d.cts +468 -59
  28. package/index.d.ts +468 -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 +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/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 -132
  44. package/lib/core/zip-reader.js +106 -48
  45. package/lib/core/zip-writer.js +236 -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,
@@ -240,9 +244,12 @@ import {
240
244
  checkSignalOption,
241
245
  throwIfAborted,
242
246
  checkPasswordOption,
247
+ checkPassThroughOption,
243
248
  checkInteger,
244
249
  checkIntegerOption,
245
- toNumber
250
+ toNumber,
251
+ OPTION_ENTRY,
252
+ PASS_THROUGH_COMPRESSED
246
253
  } from "./options.js";
247
254
 
248
255
  const ERR_DUPLICATED_NAME = "File already exists";
@@ -256,7 +263,7 @@ const ERR_INVALID_VERSION = "Version exceeds 65535";
256
263
  const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
257
264
  const ERR_UNSUPPORTED_ENCRYPTION_USDZ = "Encryption is not supported in USDZ files";
258
265
  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";
266
+ const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set to true (use 'compressed' instead)";
260
267
  const ERR_INVALID_EXTRAFIELD = "Invalid extra field (must be a Map)";
261
268
  const ERR_INVALID_EXTRAFIELD_TYPE = "Invalid extra field type (must be integer 0..65535)";
262
269
  const ERR_INVALID_EXTRAFIELD_DATA_TYPE = "Invalid extra field data (must be a Uint8Array)";
@@ -268,6 +275,7 @@ const MAX_NTFS_TIME = BigInt("0x7fffffffffffffff");
268
275
  const ERR_UNSUPPORTED_FORMAT = "Zip64 is not supported (set the 'zip64' option to 'true')";
269
276
  const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
270
277
  const ERR_UNDEFINED_COMPRESSION_METHOD = "Undefined compression method";
278
+ const ERR_UNDEFINED_CRC32 = "Undefined CRC32";
271
279
  const ERR_UNDETERMINED_SIZE = "Undetermined size";
272
280
  const ERR_UNDEFINED_READER = "Undefined reader";
273
281
  const ERR_INVALID_READER = "Invalid reader (must be a Reader instance, a ReadableStream instance, or an object with a 'readable' property)";
@@ -281,14 +289,21 @@ const ERR_INVALID_MSDOS_ATTRIBUTES = "Invalid msdosAttributesRaw (must be intege
281
289
  const ERR_INVALID_MSDOS_DATA = "Invalid msdosAttributes (must be an object with boolean flags)";
282
290
  const ERR_INVALID_LEVEL = "Invalid level (must be integer 0..9)";
283
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";
284
296
 
285
297
  const EXTRAFIELD_DATA_AES = new Uint8Array([0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00]);
286
298
  const EXTRAFIELD_OFFSET_AES_VENDOR_VERSION = 4;
287
299
  const EXTRAFIELD_OFFSET_AES_COMPRESSION_METHOD = 9;
288
300
  const EXTRAFIELD_USDZ_MAX_LENGTH = 67;
301
+ const MIN_PRINTABLE_ASCII_CHARACTER_CODE = 0x20;
302
+ const MAX_PRINTABLE_ASCII_CHARACTER_CODE = 0x7e;
289
303
  const VENDOR_VERSION_AE_1 = 1;
290
304
  const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
291
305
  const UNIX_EXTRA_FIELD_TYPE = "unix";
306
+ const LEVEL_BY_BITFLAG_LEVEL = [8, 9, 5, 3];
292
307
  const MAX_LEVEL = 9;
293
308
 
294
309
  let workers = 0;
@@ -315,9 +330,11 @@ class ZipWriter {
315
330
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
316
331
  pendingAddFileCalls: new Set(),
317
332
  pendingErrors: [],
333
+ warnings: [],
318
334
  bufferedWrites: 0,
319
335
  directWrites: 0,
320
- lastFileEntry: UNDEFINED_VALUE
336
+ lastFileEntry: UNDEFINED_VALUE,
337
+ archiveClosed: false
321
338
  });
322
339
  }
323
340
 
@@ -482,6 +499,9 @@ class ZipWriter {
482
499
  const zipWriter = this;
483
500
  const { pendingAddFileCalls, writer } = this;
484
501
  const { writable } = writer;
502
+ if (zipWriter.archiveClosed) {
503
+ return getWriterData(writer);
504
+ }
485
505
  if (!(comment instanceof Uint8Array)) {
486
506
  throw new Error(ERR_INVALID_COMMENT_TYPE);
487
507
  }
@@ -505,11 +525,16 @@ class ZipWriter {
505
525
  throw error;
506
526
  }
507
527
  await closeFile(zipWriter, comment, options);
528
+ zipWriter.archiveClosed = true;
508
529
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
509
530
  if (!preventClose) {
510
531
  await writable.getWriter().close();
511
532
  }
512
- return writer.getData ? writer.getData() : writable;
533
+ return getWriterData(writer);
534
+ }
535
+
536
+ [SYMBOL_ASYNC_DISPOSE]() {
537
+ return this.close();
513
538
  }
514
539
  }
515
540
 
@@ -588,6 +613,10 @@ class WatchedPromise extends Promise {
588
613
  }
589
614
  }
590
615
 
616
+ function getWriterData(writer) {
617
+ return writer.getData ? writer.getData() : writer.writable;
618
+ }
619
+
591
620
  function watchPromiseError(zipWriter, promise) {
592
621
  const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
593
622
  const watcher = {};
@@ -606,6 +635,14 @@ async function prependZipEntries(zipWriter, reader) {
606
635
 
607
636
  async function addFileEntry(zipWriter, name, reader, options) {
608
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
+ }
609
646
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
610
647
  name += DIRECTORY_SIGNATURE;
611
648
  }
@@ -656,6 +693,7 @@ export {
656
693
  ZipWriter,
657
694
  ZipWriterStream,
658
695
  getEntriesSize,
696
+ getSourceEntryOptions,
659
697
  ERR_DUPLICATED_NAME,
660
698
  ERR_INVALID_COMMENT,
661
699
  ERR_INVALID_COMMENT_TYPE,
@@ -675,11 +713,14 @@ export {
675
713
  ERR_UNSUPPORTED_FORMAT,
676
714
  ERR_UNDEFINED_UNCOMPRESSED_SIZE,
677
715
  ERR_UNDEFINED_COMPRESSION_METHOD,
716
+ ERR_UNDEFINED_CRC32,
678
717
  ERR_UNDETERMINED_SIZE,
679
718
  ERR_UNDEFINED_READER,
680
719
  ERR_INVALID_READER,
681
720
  ERR_ZIP_NOT_EMPTY,
682
721
  ERR_INVALID_SIGNATURE_DATA,
722
+ ERR_INVALID_ENTRY,
723
+ ERR_ZIP_CRYPTO_LAST_MOD_DATE,
683
724
  ERR_INVALID_UID,
684
725
  ERR_INVALID_GID,
685
726
  ERR_INVALID_UNIX_MODE,
@@ -689,7 +730,9 @@ export {
689
730
  ERR_INVALID_MSDOS_DATA,
690
731
  ERR_INVALID_LEVEL,
691
732
  ERR_INVALID_PASSWORD_TYPE,
692
- ERR_UNSUPPORTED_CRYPTO_API
733
+ ERR_UNSUPPORTED_CRYPTO_API,
734
+ WARNING_COMPRESSION_UNAVAILABLE,
735
+ WARNING_CLAMPED_LAST_MODIFICATION_DATE
693
736
  };
694
737
 
695
738
  async function addFile(zipWriter, name, reader, options) {
@@ -710,21 +753,33 @@ async function addFile(zipWriter, name, reader, options) {
710
753
  try {
711
754
  const { resolvedOptions } = metadataInfo;
712
755
  if (resolvedOptions.level != 0 && resolvedOptions.compressionMethod === UNDEFINED_VALUE &&
713
- !resolvedOptions.passThrough && !(await supportsDeflate(getConfiguration()))) {
756
+ !resolvedOptions.passThroughCompression && !(await supportsDeflate(getConfiguration()))) {
714
757
  resolvedOptions.level = 0;
758
+ addWarning(zipWriter.warnings, WARNING_COMPRESSION_UNAVAILABLE, name);
715
759
  }
716
760
  const sizesInfo = await resolveSizes(zipWriter, reader, metadataInfo, options);
717
761
  ({ reader } = sizesInfo);
718
762
  const diskOffset = getDiskOffset(zipWriter.writer);
719
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
+ }
720
772
  options = Object.assign({}, options, attributesInfo.resolvedOptions, metadataInfo.resolvedOptions, sizesInfo.resolvedOptions, {
721
773
  signature: options[PROPERTY_NAME_SIGNATURE],
722
- crc32: options.crc32 === UNDEFINED_VALUE ? options[PROPERTY_NAME_SIGNATURE] : options.crc32,
774
+ crc32,
723
775
  offset: zipWriter.offset - diskOffset,
724
776
  diskNumberStart: diskNumber,
725
777
  [OPTION_USDZ]: zipWriter.options[OPTION_USDZ]
726
778
  });
727
779
  const headerInfo = getHeaderInfo(options);
780
+ if (headerInfo.lastModDateClamped) {
781
+ addWarning(zipWriter.warnings, WARNING_CLAMPED_LAST_MODIFICATION_DATE, name);
782
+ }
728
783
  const dataDescriptorInfo = getDataDescriptorInfo(options);
729
784
  const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
730
785
  fileEntry = await getFileEntry(zipWriter, name, reader, {
@@ -751,6 +806,95 @@ async function addFile(zipWriter, name, reader, options) {
751
806
  return new Entry(fileEntry);
752
807
  }
753
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
+
754
898
  function resolveAttributes(zipWriter, name, options) {
755
899
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
756
900
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
@@ -904,7 +1048,9 @@ function resolveMetadata(zipWriter, name, options) {
904
1048
  const lastAccessDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
905
1049
  const creationDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
906
1050
  const internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, 0);
907
- 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;
908
1054
  const password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
909
1055
  const rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
910
1056
  checkPasswordOption(password, rawPassword);
@@ -920,10 +1066,11 @@ function resolveMetadata(zipWriter, name, options) {
920
1066
  const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, true);
921
1067
  const signal = checkSignalOption(getOptionValue(zipWriter, options, OPTION_SIGNAL));
922
1068
  throwIfAborted(signal);
923
- const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES, true);
1069
+ const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES,
1070
+ !isPrintableASCIIText(rawFilename) || !isPrintableASCIIText(rawComment));
924
1071
  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 &&
1072
+ const registeredCodec = passThroughCompression || compressionMethod === UNDEFINED_VALUE ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
1073
+ if (!passThroughCompression && compressionMethod !== UNDEFINED_VALUE &&
927
1074
  compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE && !registeredCodec) {
928
1075
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
929
1076
  }
@@ -937,15 +1084,15 @@ function resolveMetadata(zipWriter, name, options) {
937
1084
  level = 0;
938
1085
  }
939
1086
  }
940
- if (passThrough) {
941
- level = UNDEFINED_VALUE;
1087
+ if (passThroughCompression) {
1088
+ level = toNumber(options[OPTION_LEVEL]);
942
1089
  }
943
1090
  let useCompressionStream = getOptionValue(zipWriter, options, OPTION_USE_COMPRESSION_STREAM);
944
1091
  let dataDescriptor = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR);
945
1092
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
946
1093
  dataDescriptor = false;
947
1094
  }
948
- if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThrough)) {
1095
+ if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThroughEncryption)) {
949
1096
  dataDescriptor = true;
950
1097
  }
951
1098
  if (level !== UNDEFINED_VALUE && level != 6) {
@@ -969,7 +1116,8 @@ function resolveMetadata(zipWriter, name, options) {
969
1116
  lastAccessDate,
970
1117
  creationDate,
971
1118
  internalFileAttributes,
972
- passThrough,
1119
+ passThroughCompression,
1120
+ passThroughEncryption,
973
1121
  password,
974
1122
  rawPassword,
975
1123
  encryptionStrength,
@@ -1030,7 +1178,7 @@ function serializeExtraField(extraField) {
1030
1178
  }
1031
1179
 
1032
1180
  async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, options) {
1033
- if (metadata.passThrough && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
1181
+ if (metadata.passThroughCompression && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
1034
1182
  throw new Error(ERR_UNDEFINED_READER);
1035
1183
  }
1036
1184
  let contentSize;
@@ -1046,12 +1194,12 @@ async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, op
1046
1194
  }
1047
1195
 
1048
1196
  function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options) {
1049
- const { passThrough, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
1197
+ const { passThroughCompression, passThroughEncryption, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
1050
1198
  let { dataDescriptor, zip64, level, compressionMethod } = metadata;
1051
1199
  let maximumCompressedSize = 0;
1052
1200
  let uncompressedSize = 0;
1053
1201
  let unknownSize = false;
1054
- if (passThrough && hasContent) {
1202
+ if (passThroughCompression && hasContent) {
1055
1203
  uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
1056
1204
  if (uncompressedSize === UNDEFINED_VALUE) {
1057
1205
  throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
@@ -1062,17 +1210,17 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1062
1210
  }
1063
1211
  const zip64Enabled = zip64 === true;
1064
1212
  const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
1065
- if (hasContent && passThrough && !encrypted && getLength(password, rawPassword)) {
1213
+ if (hasContent && passThroughEncryption && !encrypted && getLength(password, rawPassword)) {
1066
1214
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH);
1067
1215
  }
1068
- 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));
1069
1217
  if (!hasContent) {
1070
1218
  level = 0;
1071
1219
  compressionMethod = COMPRESSION_METHOD_STORE;
1072
1220
  }
1073
- const encryptionOverhead = encryptedEntry ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
1221
+ const encryptionOverhead = getEncryptionOverhead(encryptedEntry, zipCrypto, encryptionStrength);
1074
1222
  if (hasContent) {
1075
- if (!passThrough) {
1223
+ if (!passThroughCompression) {
1076
1224
  if (contentSize === UNDEFINED_VALUE) {
1077
1225
  dataDescriptor = true;
1078
1226
  if (zip64 || zip64 === UNDEFINED_VALUE) {
@@ -1085,10 +1233,12 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1085
1233
  }
1086
1234
  } else {
1087
1235
  options.uncompressedSize = uncompressedSize;
1088
- maximumCompressedSize = contentSize === UNDEFINED_VALUE ? getMaximumCompressedSize(uncompressedSize) + encryptionOverhead : contentSize;
1236
+ maximumCompressedSize = contentSize === UNDEFINED_VALUE ?
1237
+ getMaximumCompressedSize(uncompressedSize) + encryptionOverhead :
1238
+ contentSize + (passThroughEncryption ? 0 : encryptionOverhead);
1089
1239
  }
1090
1240
  }
1091
- const emptyEntry = !encryptedEntry && (!hasContent || (contentSize === 0 && !passThrough)) && !isCompressed(compressionMethod, level);
1241
+ const emptyEntry = !encryptedEntry && (!hasContent || (contentSize === 0 && !passThroughCompression)) && !isCompressed(compressionMethod, level);
1092
1242
  if (emptyEntry && !zipCrypto && getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR) === UNDEFINED_VALUE) {
1093
1243
  dataDescriptor = false;
1094
1244
  }
@@ -1137,6 +1287,8 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1137
1287
  const initialOffset = writerOptions[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : writerOptions[OPTION_OFFSET];
1138
1288
  let offset = initialOffset;
1139
1289
  let minimumEntrySize = INFINITY_VALUE;
1290
+ const entrySizes = [];
1291
+ const entriesAlreadyZip64 = new Set();
1140
1292
  for (const entry of entries) {
1141
1293
  let { name } = entry;
1142
1294
  const { size } = entry;
@@ -1148,7 +1300,7 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1148
1300
  ({ name } = attributesInfo);
1149
1301
  const { resolvedOptions: metadata } = resolveMetadata(zipWriter, name, options);
1150
1302
  if (metadata.level != 0 && metadata.compressionMethod === UNDEFINED_VALUE &&
1151
- !metadata.passThrough && !(await supportsDeflate(getConfiguration()))) {
1303
+ !metadata.passThroughCompression && !(await supportsDeflate(getConfiguration()))) {
1152
1304
  metadata.level = 0;
1153
1305
  }
1154
1306
  const hasContent = !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY);
@@ -1156,7 +1308,7 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1156
1308
  throw new Error(ERR_UNDETERMINED_SIZE);
1157
1309
  }
1158
1310
  const { maximumCompressedSize, resolvedOptions: sizes } = resolveEntrySizes(zipWriter, hasContent, size, metadata, options);
1159
- if (hasContent && !metadata.passThrough && isCompressed(sizes.compressionMethod, sizes.level)) {
1311
+ if (hasContent && !metadata.passThroughCompression && isCompressed(sizes.compressionMethod, sizes.level)) {
1160
1312
  throw new Error(ERR_UNDETERMINED_SIZE);
1161
1313
  }
1162
1314
  const entryOptions = Object.assign({}, options, attributesInfo.resolvedOptions, metadata, sizes, { [OPTION_USDZ]: usdz });
@@ -1176,10 +1328,15 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1176
1328
  compressedSize
1177
1329
  }));
1178
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));
1179
1334
  minimumEntrySize = Math.min(minimumEntrySize, entrySize);
1180
1335
  offset += entrySize;
1181
1336
  }
1182
- 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))));
1183
1340
  if (layoutDependsOnWriteOrder && !writeOrderGuaranteed) {
1184
1341
  throw new Error(ERR_UNDETERMINED_SIZE);
1185
1342
  }
@@ -1197,6 +1354,20 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1197
1354
  return offset - initialOffset + directoryDataLength + commentLength + (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
1198
1355
  }
1199
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
+
1200
1371
  async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1201
1372
  const {
1202
1373
  fileEntries,
@@ -1372,6 +1543,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1372
1543
  localExtraFieldZip64Length,
1373
1544
  rawExtraFieldExtendedTimestamp,
1374
1545
  extraFieldExtendedTimestampFlag,
1546
+ extraFieldExtendedTimestampTime,
1375
1547
  rawExtraFieldNTFS,
1376
1548
  rawExtraFieldUnix,
1377
1549
  rawExtraFieldAES,
@@ -1384,6 +1556,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1384
1556
  password,
1385
1557
  rawPassword,
1386
1558
  level,
1559
+ useUnicodeFileNames,
1387
1560
  zip64,
1388
1561
  zip64Enabled,
1389
1562
  zip64UncompressedSize,
@@ -1418,7 +1591,8 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1418
1591
  msdosAttributesRaw,
1419
1592
  msdosAttributes,
1420
1593
  useCompressionStream,
1421
- passThrough,
1594
+ passThroughCompression,
1595
+ passThroughEncryption,
1422
1596
  format,
1423
1597
  codecURI
1424
1598
  } = options;
@@ -1429,9 +1603,9 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1429
1603
  zip64Enabled,
1430
1604
  directory: Boolean(directory),
1431
1605
  executable: Boolean(executable),
1432
- filenameUTF8: true,
1606
+ filenameUTF8: Boolean(useUnicodeFileNames),
1433
1607
  rawFilename,
1434
- commentUTF8: true,
1608
+ commentUTF8: Boolean(useUnicodeFileNames),
1435
1609
  rawComment,
1436
1610
  rawExtraFieldZip64,
1437
1611
  localExtraFieldZip64Length,
@@ -1462,7 +1636,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1462
1636
  uncompressedSize
1463
1637
  } = options;
1464
1638
  let compressedSize = 0;
1465
- if (!passThrough) {
1639
+ if (!passThroughCompression) {
1466
1640
  uncompressedSize = 0;
1467
1641
  }
1468
1642
  const { writable } = writer;
@@ -1479,9 +1653,9 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1479
1653
  encryptionStrength,
1480
1654
  zipCrypto: encrypted && zipCrypto,
1481
1655
  passwordVerification: encrypted && zipCrypto && (rawLastModDate >> 8) & MAX_8_BITS,
1482
- computeCrc32: !passThrough,
1483
- compressed: compressed && !passThrough,
1484
- encrypted: encrypted && !passThrough,
1656
+ computeCrc32: !passThroughCompression,
1657
+ compressed: compressed && !passThroughCompression,
1658
+ encrypted: encrypted && !passThroughEncryption,
1485
1659
  useWebWorkers,
1486
1660
  useCompressionStream,
1487
1661
  transferStreams,
@@ -1496,7 +1670,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1496
1670
  const result = await runWorker({ readable, writable }, workerOptions);
1497
1671
  compressedSize = result.outputSize;
1498
1672
  writer.size += compressedSize;
1499
- if (!passThrough) {
1673
+ if (!passThroughCompression) {
1500
1674
  uncompressedSize = result.inputSize;
1501
1675
  if (!encrypted || zipCrypto) {
1502
1676
  crc32 = result.crc32;
@@ -1539,8 +1713,9 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1539
1713
  headerArray,
1540
1714
  headerView,
1541
1715
  signature: crc32,
1542
- crc32: encrypted && !zipCrypto && !passThrough ? UNDEFINED_VALUE : crc32,
1716
+ crc32: encrypted && !zipCrypto && !passThroughCompression ? UNDEFINED_VALUE : crc32,
1543
1717
  extraFieldExtendedTimestampFlag,
1718
+ extraFieldExtendedTimestampTime,
1544
1719
  zip64UncompressedSize,
1545
1720
  zip64CompressedSize
1546
1721
  });
@@ -1565,7 +1740,7 @@ function getHeaderInfo(options) {
1565
1740
  encryptionStrength,
1566
1741
  extendedTimestamp,
1567
1742
  ntfsTimestamp,
1568
- passThrough,
1743
+ passThroughCompression,
1569
1744
  encrypted,
1570
1745
  zip64UncompressedSize,
1571
1746
  zip64CompressedSize,
@@ -1576,7 +1751,7 @@ function getHeaderInfo(options) {
1576
1751
  let { version, compressionMethod } = options;
1577
1752
  const compressed = !directory && isCompressed(compressionMethod, level);
1578
1753
  let rawLocalExtraFieldZip64;
1579
- const uncompressedFile = passThrough || !compressed;
1754
+ const uncompressedFile = passThroughCompression || !compressed;
1580
1755
  const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || (uncompressedFile && !unknownSize)));
1581
1756
  const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
1582
1757
  if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
@@ -1588,8 +1763,8 @@ function getHeaderInfo(options) {
1588
1763
  if (zip64ExtraFieldComplete) {
1589
1764
  extraFieldZip64.writeUint64(uncompressedSize);
1590
1765
  if (uncompressedFile) {
1591
- const encryptionOverhead = encrypted ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
1592
- extraFieldZip64.writeUint64(passThrough ? 0 : uncompressedSize + encryptionOverhead);
1766
+ const encryptionOverhead = getEncryptionOverhead(encrypted, zipCrypto, encryptionStrength);
1767
+ extraFieldZip64.writeUint64(passThroughCompression ? 0 : uncompressedSize + encryptionOverhead);
1593
1768
  }
1594
1769
  }
1595
1770
  } else {
@@ -1608,6 +1783,7 @@ function getHeaderInfo(options) {
1608
1783
  let rawExtraFieldNTFS;
1609
1784
  let rawExtraFieldExtendedTimestamp;
1610
1785
  let extraFieldExtendedTimestampFlag;
1786
+ let extraFieldExtendedTimestampTime;
1611
1787
  if (extendedTimestamp) {
1612
1788
  const lastModTimeUnix = getTimeUnix(lastModDate);
1613
1789
  const lastModTimeUnixInRange = inUnixTimeRange(lastModTimeUnix);
@@ -1615,6 +1791,7 @@ function getHeaderInfo(options) {
1615
1791
  const extraFieldTimestampLength = 9 + (lastAccessDate ? 4 : 0) + (creationDate ? 4 : 0);
1616
1792
  const extraFieldTimestamp = createRecordWriter(extraFieldTimestampLength);
1617
1793
  extraFieldExtendedTimestampFlag = 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0);
1794
+ extraFieldExtendedTimestampTime = lastModTimeUnix;
1618
1795
  extraFieldTimestamp.writeUint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
1619
1796
  extraFieldTimestamp.writeUint16(extraFieldTimestampLength - 4);
1620
1797
  extraFieldTimestamp.writeUint8(extraFieldExtendedTimestampFlag);
@@ -1698,7 +1875,7 @@ function getHeaderInfo(options) {
1698
1875
  }
1699
1876
  if (encrypted && !zipCrypto) {
1700
1877
  version = version > VERSION_AES ? version : VERSION_AES;
1701
- if (passThrough && crc32 !== UNDEFINED_VALUE) {
1878
+ if (passThroughCompression && crc32 !== UNDEFINED_VALUE) {
1702
1879
  rawExtraFieldAES[EXTRAFIELD_OFFSET_AES_VENDOR_VERSION] = VENDOR_VERSION_AE_1;
1703
1880
  }
1704
1881
  setUint16(getDataView(rawExtraFieldAES), EXTRAFIELD_OFFSET_AES_COMPRESSION_METHOD, compressionMethod);
@@ -1760,12 +1937,14 @@ function getHeaderInfo(options) {
1760
1937
  headerArray,
1761
1938
  headerView,
1762
1939
  lastModDate: storedLastModDate,
1940
+ lastModDateClamped: storedLastModDate === clampedLastModDate && dosLastModDate.getTime() != clampedLastModDate.getTime(),
1763
1941
  rawLastModDate,
1764
1942
  encrypted,
1765
1943
  compressed,
1766
1944
  version,
1767
1945
  compressionMethod,
1768
1946
  extraFieldExtendedTimestampFlag,
1947
+ extraFieldExtendedTimestampTime,
1769
1948
  rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
1770
1949
  localExtraFieldZip64Length,
1771
1950
  rawExtraFieldExtendedTimestamp,
@@ -1868,7 +2047,7 @@ function setEntryInfo({
1868
2047
  }, {
1869
2048
  zip64,
1870
2049
  zipCrypto,
1871
- passThrough,
2050
+ passThroughCompression,
1872
2051
  dataDescriptor
1873
2052
  }) {
1874
2053
  const {
@@ -1879,7 +2058,7 @@ function setEntryInfo({
1879
2058
  dataDescriptorView,
1880
2059
  dataDescriptorOffset
1881
2060
  } = dataDescriptorInfo;
1882
- if ((!encrypted || zipCrypto || passThrough) && crc32 !== UNDEFINED_VALUE) {
2061
+ if ((!encrypted || zipCrypto || passThroughCompression) && crc32 !== UNDEFINED_VALUE) {
1883
2062
  setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
1884
2063
  if (dataDescriptor) {
1885
2064
  setUint32(dataDescriptorView, dataDescriptorOffset, crc32);
@@ -1910,9 +2089,9 @@ function updateLocalHeader({
1910
2089
  uncompressedSize,
1911
2090
  zip64UncompressedSize,
1912
2091
  zip64CompressedSize
1913
- }, localHeaderView, { dataDescriptor, passThrough }) {
2092
+ }, localHeaderView, { dataDescriptor, passThroughCompression }) {
1914
2093
  if (!dataDescriptor) {
1915
- if (!encrypted || (passThrough && crc32 !== UNDEFINED_VALUE)) {
2094
+ if (!encrypted || (passThroughCompression && crc32 !== UNDEFINED_VALUE)) {
1916
2095
  setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE + LOCAL_HEADER_COMMON_OFFSET, crc32);
1917
2096
  }
1918
2097
  if (!zip64CompressedSize) {
@@ -1949,9 +2128,8 @@ function createDirectoryRecords(files) {
1949
2128
  rawExtraFieldUnix,
1950
2129
  rawExtraField,
1951
2130
  rawCentralExtraField,
1952
- extendedTimestamp,
1953
2131
  extraFieldExtendedTimestampFlag,
1954
- lastModDate,
2132
+ extraFieldExtendedTimestampTime,
1955
2133
  zip64Enabled,
1956
2134
  uncompressedSize,
1957
2135
  compressedSize
@@ -1994,16 +2172,15 @@ function createDirectoryRecords(files) {
1994
2172
  fileEntry.zip64Offset = zip64Offset;
1995
2173
  fileEntry.zip64DiskNumberStart = zip64DiskNumberStart;
1996
2174
  let rawExtraFieldTimestamp;
1997
- const lastModTimeUnix = getTimeUnix(lastModDate);
1998
- if (extendedTimestamp && inUnixTimeRange(lastModTimeUnix)) {
2175
+ if (extraFieldExtendedTimestampTime === UNDEFINED_VALUE) {
2176
+ rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
2177
+ } else {
1999
2178
  const extraFieldTimestamp = createRecordWriter(9);
2000
2179
  extraFieldTimestamp.writeUint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
2001
2180
  extraFieldTimestamp.writeUint16(5);
2002
2181
  extraFieldTimestamp.writeUint8(extraFieldExtendedTimestampFlag);
2003
- extraFieldTimestamp.writeUint32(lastModTimeUnix);
2182
+ extraFieldTimestamp.writeUint32(extraFieldExtendedTimestampTime);
2004
2183
  rawExtraFieldTimestamp = extraFieldTimestamp.array;
2005
- } else {
2006
- rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
2007
2184
  }
2008
2185
  fileEntry.rawExtraFieldExtendedTimestamp = rawExtraFieldTimestamp;
2009
2186
  const extraFieldLength = getLength(
@@ -2503,6 +2680,11 @@ function getHeaderArrayData({
2503
2680
  };
2504
2681
  }
2505
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
+
2506
2688
  function getBitFlag(level, useUnicodeFileNames, dataDescriptor, encrypted, compressionMethod) {
2507
2689
  let bitFlag = 0;
2508
2690
  if (useUnicodeFileNames) {