@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
@@ -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,
@@ -173,7 +177,9 @@ import {
173
177
  OPTION_PREVENT_CLOSE,
174
178
  checkFunctionOption,
175
179
  checkSignalOption,
180
+ throwIfAborted,
176
181
  checkPasswordOption,
182
+ checkPassThroughOption,
177
183
  toNumber
178
184
  } from "./options.js";
179
185
 
@@ -207,6 +213,8 @@ const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
207
213
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
208
214
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
209
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)";
210
218
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
211
219
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
212
220
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
@@ -277,7 +285,7 @@ class ZipReader {
277
285
  }
278
286
  }
279
287
  if (rejectAmbiguousEndOfDirectory && endOfDirectoryReachingEndCount > 1) {
280
- throwAmbiguousArchive("multiple end of central directory records");
288
+ throwAmbiguousArchive(WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY);
281
289
  }
282
290
  const endOfDirectoryView = getDataView(endOfDirectoryInfo);
283
291
  let directoryDataLength = getUint32(endOfDirectoryView, 12);
@@ -575,7 +583,8 @@ class ZipReader {
575
583
  const writer = new TransformStream();
576
584
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
577
585
  arrayBufferPromise.catch(() => { });
578
- await fileEntry.getData(writer, entry, zipReader.readRanges, options);
586
+ await fileEntry.getData(writer, entry, zipReader.readRanges,
587
+ Object.assign({}, options, { preventClose: false }));
579
588
  return arrayBufferPromise;
580
589
  };
581
590
  offset = endOffset;
@@ -656,48 +665,101 @@ class ZipReader {
656
665
  await reader.readable.cancel();
657
666
  }
658
667
  }
668
+
669
+ [SYMBOL_ASYNC_DISPOSE]() {
670
+ return this.close();
671
+ }
659
672
  }
660
673
 
661
674
  class ZipReaderStream {
662
675
 
663
676
  constructor(options = {}) {
664
- const { readable, writable } = new TransformStream();
665
- 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();
666
686
  this.readable = new ReadableStream({
667
687
  async pull(controller) {
668
688
  const { done, value } = await gen.next();
669
689
  if (done)
670
690
  return controller.close();
691
+ const entryStream = createEntryStream(value, pendingEntries);
671
692
  const chunk = {
672
693
  ...value,
673
- readable: (function () {
674
- const { readable, writable } = new TransformStream();
675
- if (value.getData) {
676
- getData();
677
- return readable;
678
- }
679
-
680
- async function getData() {
681
- try {
682
- await value.getData(writable);
683
- } catch (error) {
684
- try {
685
- await writable.abort(error);
686
- } catch {
687
- // ignored
688
- }
689
- }
690
- }
691
- })()
694
+ readable: entryStream.readable
692
695
  };
693
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
+ });
694
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()]);
695
715
  }
696
716
  });
697
717
  this.writable = writable;
698
718
  }
699
719
  }
700
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
+
701
763
  async function isZipFile(reader, options = {}) {
702
764
  reader = new GenericReader(reader);
703
765
  await initStream(reader);
@@ -761,6 +823,8 @@ export {
761
823
  WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
762
824
  WARNING_DUPLICATE_FILENAME,
763
825
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
826
+ WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
827
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
764
828
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
765
829
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
766
830
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES
@@ -803,7 +867,9 @@ class ZipEntry {
803
867
  const dataView = getDataView(dataArray);
804
868
  let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
805
869
  let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
806
- 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;
807
873
  checkPasswordOption(password, rawPassword);
808
874
  password = password && password.length && password;
809
875
  rawPassword = rawPassword && rawPassword.length && rawPassword;
@@ -824,7 +890,9 @@ class ZipEntry {
824
890
  const checkLocalDirectoryOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
825
891
  const entryStrictness = getStrictness(options, zipEntry.options);
826
892
  const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
827
- 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);
828
896
  let rawLocalFilename = EMPTY_UINT8_ARRAY;
829
897
  if (checkLocalFilename && (filenameLength || extraFieldLength)) {
830
898
  const trailingDataArray = await readUint8Array(reader, localHeaderOffset + HEADER_SIZE, filenameLength + extraFieldLength);
@@ -855,16 +923,17 @@ class ZipEntry {
855
923
  if (gid !== UNDEFINED_VALUE && fileEntry.gid === UNDEFINED_VALUE) {
856
924
  fileEntry.gid = gid;
857
925
  }
858
- 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);
859
928
  const zipCrypto = encrypted && !extraFieldAES;
860
- if (!passThrough) {
929
+ if (!passThroughEncryption) {
861
930
  fileEntry.zipCrypto = zipCrypto;
862
931
  }
863
932
  if (encrypted && (localDirectory.rawBitFlag & BITFLAG_STRONG_ENCRYPTION) == BITFLAG_STRONG_ENCRYPTION) {
864
933
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION);
865
934
  }
866
- const registeredCodec = passThrough ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
867
- 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) {
868
937
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
869
938
  }
870
939
  if (encrypted) {
@@ -880,15 +949,17 @@ class ZipEntry {
880
949
  const size = compressedSize;
881
950
  const readable = toCompatibleReadable(reader.createReadable({ offset: dataOffset, size }));
882
951
  const signal = checkSignalOption(getOptionValue(zipEntry, options, OPTION_SIGNAL));
883
- const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
952
+ throwIfAborted(signal);
884
953
  let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
885
954
  const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
886
955
  if (checkOverlappingEntryOnly) {
887
956
  checkOverlappingEntry = true;
888
957
  }
889
958
  const { onstart, onprogress, onend } = options;
890
- const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThrough;
891
- 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;
892
963
  const deflate64 = compressionMethod == COMPRESSION_METHOD_DEFLATE_64;
893
964
  let useCompressionStream = getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM);
894
965
  if (deflate64) {
@@ -897,7 +968,7 @@ class ZipEntry {
897
968
  const checkCrc32Option = getOptionValue(zipEntry, options, OPTION_CHECK_CRC32);
898
969
  const checkCrc32 = (checkCrc32Option === UNDEFINED_VALUE ?
899
970
  getOptionValue(zipEntry, options, OPTION_CHECK_SIGNATURE) :
900
- checkCrc32Option) && !passThrough &&
971
+ checkCrc32Option) && !passThroughCompression &&
901
972
  (!encrypted || zipCrypto || (extraFieldAES && extraFieldAES.vendorVersion == VENDOR_VERSION_AE_1));
902
973
  const workerOptions = {
903
974
  options: {
@@ -951,10 +1022,10 @@ class ZipEntry {
951
1022
  await initStream(writer, getDecodableOutputSize(outputSize, compressedSize, compressed));
952
1023
  ({ writable } = writer);
953
1024
  const { outputSize: writtenSize } = await runWorker({ readable, writable }, workerOptions);
954
- writer.size += writtenSize;
955
1025
  if (writtenSize != outputSize) {
956
- throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
1026
+ throw Object.assign(new Error(ERR_INVALID_UNCOMPRESSED_SIZE), { outputSize: writtenSize });
957
1027
  }
1028
+ writer.size += writtenSize;
958
1029
  }
959
1030
  } catch (error) {
960
1031
  if (error.outputSize !== UNDEFINED_VALUE) {
@@ -1620,7 +1691,7 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
1620
1691
  if (checkLocalFilename && !maskedLocalDirectory &&
1621
1692
  (rawLocalFilename.length != rawFilename.length ||
1622
1693
  rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
1623
- reportAmbiguity(reject, warnings, "mismatched local file header (filename)");
1694
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME);
1624
1695
  }
1625
1696
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
1626
1697
  reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
@@ -1645,16 +1716,6 @@ function reportAmbiguity(reject, warnings, reason) {
1645
1716
  }
1646
1717
  }
1647
1718
 
1648
- function addWarning(warnings, reason, filename) {
1649
- if (!warnings.some(warning => warning.reason == reason)) {
1650
- const warning = { reason };
1651
- if (filename !== UNDEFINED_VALUE) {
1652
- warning.filename = filename;
1653
- }
1654
- warnings.push(warning);
1655
- }
1656
- }
1657
-
1658
1719
  function throwAmbiguousArchive(reason) {
1659
1720
  const error = new Error(ERR_AMBIGUOUS_ARCHIVE);
1660
1721
  error.reason = reason;