@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
@@ -95,6 +95,7 @@ import {
95
95
  FILE_ATTR_UNIX_STICKY_MASK,
96
96
  NUMBER_TYPE,
97
97
  EMPTY_UINT8_ARRAY,
98
+ SYMBOL_ASYNC_DISPOSE,
98
99
  MIN_DATE
99
100
  } from "./constants.js";
100
101
  import { getConfiguration } from "./configuration.js";
@@ -120,6 +121,7 @@ import {
120
121
  } from "./io.js";
121
122
  import { decodeText } from "./util/decode-text.js";
122
123
  import { getDataView } from "./util/array.js";
124
+ import { addWarning } from "./util/warnings.js";
123
125
  import { toCompatibleReadable, streamToBlob } from "./util/compatible-streams.js";
124
126
  import { Crc32 } from "./streams/codecs/crc32.js";
125
127
  import {
@@ -137,7 +139,8 @@ import {
137
139
  PROPERTY_NAME_RAW_LAST_ACCESS_DATE,
138
140
  PROPERTY_NAME_CREATION_DATE,
139
141
  PROPERTY_NAME_RAW_CREATION_DATE,
140
- Entry
142
+ Entry,
143
+ getEncryptionOverhead
141
144
  } from "./zip-entry.js";
142
145
  import {
143
146
  OPTION_FILENAME_ENCODING,
@@ -154,6 +157,7 @@ import {
154
157
  OPTION_CHECK_OVERLAPPING_ENTRY,
155
158
  OPTION_CHECK_AMBIGUITY,
156
159
  OPTION_CHECK_LOCAL_DIRECTORY,
160
+ OPTION_CHECK_LOCAL_FILENAME,
157
161
  OPTION_CHECK_SIGNATURE,
158
162
  OPTION_CHECK_CRC32,
159
163
  OPTION_CHECK_AUTHENTICATION_CODE,
@@ -175,6 +179,7 @@ import {
175
179
  checkSignalOption,
176
180
  throwIfAborted,
177
181
  checkPasswordOption,
182
+ checkPassThroughOption,
178
183
  toNumber
179
184
  } from "./options.js";
180
185
 
@@ -208,6 +213,8 @@ const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
208
213
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
209
214
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
210
215
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
216
+ const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
217
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = "mismatched local file header (filename)";
211
218
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
212
219
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
213
220
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
@@ -278,7 +285,7 @@ class ZipReader {
278
285
  }
279
286
  }
280
287
  if (rejectAmbiguousEndOfDirectory && endOfDirectoryReachingEndCount > 1) {
281
- throwAmbiguousArchive("multiple end of central directory records");
288
+ throwAmbiguousArchive(WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY);
282
289
  }
283
290
  const endOfDirectoryView = getDataView(endOfDirectoryInfo);
284
291
  let directoryDataLength = getUint32(endOfDirectoryView, 12);
@@ -576,7 +583,8 @@ class ZipReader {
576
583
  const writer = new TransformStream();
577
584
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
578
585
  arrayBufferPromise.catch(() => { });
579
- await fileEntry.getData(writer, entry, zipReader.readRanges, options);
586
+ await fileEntry.getData(writer, entry, zipReader.readRanges,
587
+ Object.assign({}, options, { preventClose: false }));
580
588
  return arrayBufferPromise;
581
589
  };
582
590
  offset = endOffset;
@@ -657,49 +665,101 @@ class ZipReader {
657
665
  await reader.readable.cancel();
658
666
  }
659
667
  }
668
+
669
+ [SYMBOL_ASYNC_DISPOSE]() {
670
+ return this.close();
671
+ }
660
672
  }
661
673
 
662
674
  class ZipReaderStream {
663
675
 
664
676
  constructor(options = {}) {
665
- const { readable, writable } = new TransformStream();
666
- const gen = new ZipReader(readable, options).getEntriesGenerator();
677
+ let sourceController;
678
+ const { readable, writable } = new TransformStream({
679
+ start(controller) {
680
+ sourceController = controller;
681
+ }
682
+ });
683
+ const zipReader = new ZipReader(readable, options);
684
+ const gen = zipReader.getEntriesGenerator();
685
+ const pendingEntries = new Set();
667
686
  this.readable = new ReadableStream({
668
687
  async pull(controller) {
669
688
  const { done, value } = await gen.next();
670
689
  if (done)
671
690
  return controller.close();
672
- const entryReadable = (function () {
673
- const { readable, writable } = new TransformStream();
674
- if (value.getData) {
675
- getData();
676
- return readable;
677
- }
678
-
679
- async function getData() {
680
- try {
681
- await value.getData(writable);
682
- } catch (error) {
683
- try {
684
- await writable.abort(error);
685
- } catch {
686
- // ignored
687
- }
688
- }
689
- }
690
- })();
691
+ const entryStream = createEntryStream(value, pendingEntries);
691
692
  const chunk = {
692
693
  ...value,
693
- readable: entryReadable
694
+ readable: entryStream.readable
694
695
  };
695
696
  delete chunk.getData;
697
+ Object.defineProperties(chunk, {
698
+ localDirectory: {
699
+ get: () => value.localDirectory,
700
+ enumerable: true
701
+ },
702
+ warnings: {
703
+ get: () => value.warnings,
704
+ enumerable: true
705
+ }
706
+ });
696
707
  controller.enqueue(chunk);
708
+ },
709
+ async cancel(reason) {
710
+ const entryStreams = Array.from(pendingEntries);
711
+ pendingEntries.clear();
712
+ sourceController.error(reason);
713
+ await Promise.allSettled(entryStreams.map(entryStream => entryStream.cancel(reason)));
714
+ await Promise.allSettled([gen.return(), zipReader.close()]);
697
715
  }
698
716
  });
699
717
  this.writable = writable;
700
718
  }
701
719
  }
702
720
 
721
+ function createEntryStream(entry, pendingEntries) {
722
+ const { readable, writable } = new TransformStream();
723
+ let dataReader;
724
+ const entryStream = {
725
+ cancel: async reason => {
726
+ pendingEntries.delete(entryStream);
727
+ await (dataReader ? dataReader.cancel(reason) : readable.cancel(reason));
728
+ }
729
+ };
730
+ entryStream.readable = new ReadableStream({
731
+ async pull(controller) {
732
+ if (!dataReader) {
733
+ dataReader = readable.getReader();
734
+ pendingEntries.add(entryStream);
735
+ getData();
736
+ }
737
+ const { done, value } = await dataReader.read();
738
+ if (done) {
739
+ controller.close();
740
+ } else {
741
+ controller.enqueue(value);
742
+ }
743
+ },
744
+ cancel: reason => entryStream.cancel(reason)
745
+ }, { highWaterMark: 0 });
746
+ return entryStream;
747
+
748
+ async function getData() {
749
+ try {
750
+ await entry.getData(writable, { preventClose: false });
751
+ } catch (error) {
752
+ try {
753
+ await writable.abort(error);
754
+ } catch {
755
+ // ignored
756
+ }
757
+ } finally {
758
+ pendingEntries.delete(entryStream);
759
+ }
760
+ }
761
+ }
762
+
703
763
  async function isZipFile(reader, options = {}) {
704
764
  reader = new GenericReader(reader);
705
765
  await initStream(reader);
@@ -763,6 +823,8 @@ export {
763
823
  WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
764
824
  WARNING_DUPLICATE_FILENAME,
765
825
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
826
+ WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
827
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
766
828
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
767
829
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
768
830
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES
@@ -805,7 +867,9 @@ class ZipEntry {
805
867
  const dataView = getDataView(dataArray);
806
868
  let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
807
869
  let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
808
- const passThrough = getOptionValue(zipEntry, options, OPTION_PASS_THROUGH);
870
+ const passThrough = checkPassThroughOption(getOptionValue(zipEntry, options, OPTION_PASS_THROUGH));
871
+ const passThroughCompression = Boolean(passThrough);
872
+ const passThroughEncryption = passThrough === true;
809
873
  checkPasswordOption(password, rawPassword);
810
874
  password = password && password.length && password;
811
875
  rawPassword = rawPassword && rawPassword.length && rawPassword;
@@ -826,7 +890,9 @@ class ZipEntry {
826
890
  const checkLocalDirectoryOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
827
891
  const entryStrictness = getStrictness(options, zipEntry.options);
828
892
  const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
829
- const checkLocalFilename = getCheckLocalFilename(checkLocalDirectoryOption, entryStrictness);
893
+ const checkLocalFilenameOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_FILENAME);
894
+ const checkLocalFilename = getCheckLocalFilename(
895
+ checkLocalFilenameOption === UNDEFINED_VALUE ? checkLocalDirectoryOption : checkLocalFilenameOption, entryStrictness);
830
896
  let rawLocalFilename = EMPTY_UINT8_ARRAY;
831
897
  if (checkLocalFilename && (filenameLength || extraFieldLength)) {
832
898
  const trailingDataArray = await readUint8Array(reader, localHeaderOffset + HEADER_SIZE, filenameLength + extraFieldLength);
@@ -857,16 +923,17 @@ class ZipEntry {
857
923
  if (gid !== UNDEFINED_VALUE && fileEntry.gid === UNDEFINED_VALUE) {
858
924
  fileEntry.gid = gid;
859
925
  }
860
- const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
926
+ const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
927
+ const encrypted = zipEntry.encrypted && localDirectory.encrypted && (!passThroughEncryption || checkPasswordOnly);
861
928
  const zipCrypto = encrypted && !extraFieldAES;
862
- if (!passThrough) {
929
+ if (!passThroughEncryption) {
863
930
  fileEntry.zipCrypto = zipCrypto;
864
931
  }
865
932
  if (encrypted && (localDirectory.rawBitFlag & BITFLAG_STRONG_ENCRYPTION) == BITFLAG_STRONG_ENCRYPTION) {
866
933
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION);
867
934
  }
868
- const registeredCodec = passThrough ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
869
- if (compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64 && !registeredCodec && !passThrough) {
935
+ const registeredCodec = passThroughCompression ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
936
+ if (compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64 && !registeredCodec && !passThroughCompression) {
870
937
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
871
938
  }
872
939
  if (encrypted) {
@@ -883,15 +950,16 @@ class ZipEntry {
883
950
  const readable = toCompatibleReadable(reader.createReadable({ offset: dataOffset, size }));
884
951
  const signal = checkSignalOption(getOptionValue(zipEntry, options, OPTION_SIGNAL));
885
952
  throwIfAborted(signal);
886
- const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
887
953
  let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
888
954
  const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
889
955
  if (checkOverlappingEntryOnly) {
890
956
  checkOverlappingEntry = true;
891
957
  }
892
958
  const { onstart, onprogress, onend } = options;
893
- const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThrough;
894
- const outputSize = passThrough ? compressedSize : uncompressedSize;
959
+ const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThroughCompression;
960
+ const outputSize = passThroughCompression ?
961
+ compressedSize - getEncryptionOverhead(encrypted, zipCrypto, extraFieldAES && extraFieldAES.strength) :
962
+ uncompressedSize;
895
963
  const deflate64 = compressionMethod == COMPRESSION_METHOD_DEFLATE_64;
896
964
  let useCompressionStream = getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM);
897
965
  if (deflate64) {
@@ -900,7 +968,7 @@ class ZipEntry {
900
968
  const checkCrc32Option = getOptionValue(zipEntry, options, OPTION_CHECK_CRC32);
901
969
  const checkCrc32 = (checkCrc32Option === UNDEFINED_VALUE ?
902
970
  getOptionValue(zipEntry, options, OPTION_CHECK_SIGNATURE) :
903
- checkCrc32Option) && !passThrough &&
971
+ checkCrc32Option) && !passThroughCompression &&
904
972
  (!encrypted || zipCrypto || (extraFieldAES && extraFieldAES.vendorVersion == VENDOR_VERSION_AE_1));
905
973
  const workerOptions = {
906
974
  options: {
@@ -954,10 +1022,10 @@ class ZipEntry {
954
1022
  await initStream(writer, getDecodableOutputSize(outputSize, compressedSize, compressed));
955
1023
  ({ writable } = writer);
956
1024
  const { outputSize: writtenSize } = await runWorker({ readable, writable }, workerOptions);
957
- writer.size += writtenSize;
958
1025
  if (writtenSize != outputSize) {
959
- throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
1026
+ throw Object.assign(new Error(ERR_INVALID_UNCOMPRESSED_SIZE), { outputSize: writtenSize });
960
1027
  }
1028
+ writer.size += writtenSize;
961
1029
  }
962
1030
  } catch (error) {
963
1031
  if (error.outputSize !== UNDEFINED_VALUE) {
@@ -1623,7 +1691,7 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
1623
1691
  if (checkLocalFilename && !maskedLocalDirectory &&
1624
1692
  (rawLocalFilename.length != rawFilename.length ||
1625
1693
  rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
1626
- reportAmbiguity(reject, warnings, "mismatched local file header (filename)");
1694
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME);
1627
1695
  }
1628
1696
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
1629
1697
  reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
@@ -1648,16 +1716,6 @@ function reportAmbiguity(reject, warnings, reason) {
1648
1716
  }
1649
1717
  }
1650
1718
 
1651
- function addWarning(warnings, reason, filename) {
1652
- if (!warnings.some(warning => warning.reason == reason)) {
1653
- const warning = { reason };
1654
- if (filename !== UNDEFINED_VALUE) {
1655
- warning.filename = filename;
1656
- }
1657
- warnings.push(warning);
1658
- }
1659
- }
1660
-
1661
1719
  function throwAmbiguousArchive(reason) {
1662
1720
  const error = new Error(ERR_AMBIGUOUS_ARCHIVE);
1663
1721
  error.reason = reason;