@zip.js/zip.js 2.8.51 → 2.8.52

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.
@@ -120,6 +120,8 @@ import {
120
120
  FILE_ATTR_MSDOS_SYSTEM_MASK,
121
121
  FILE_ATTR_MSDOS_ARCHIVE_MASK,
122
122
  FILE_ATTR_UNIX_TYPE_DIR,
123
+ FILE_ATTR_UNIX_TYPE_FILE,
124
+ FILE_ATTR_UNIX_TYPE_MASK,
123
125
  FILE_ATTR_UNIX_EXECUTABLE_MASK,
124
126
  FILE_ATTR_UNIX_DEFAULT_MASK,
125
127
  FILE_ATTR_UNIX_SETUID_MASK,
@@ -154,7 +156,8 @@ import {
154
156
  import {
155
157
  initStream,
156
158
  GenericWriter,
157
- GenericReader
159
+ GenericReader,
160
+ ownsWritable
158
161
  } from "./io.js";
159
162
  import { encodeText } from "./util/encode-text.js";
160
163
  import { getDataView } from "./util/array.js";
@@ -165,6 +168,8 @@ import {
165
168
  PROPERTY_NAME_CREATION_DATE,
166
169
  PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES,
167
170
  PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES,
171
+ PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES,
172
+ PROPERTY_NAME_DEPRECATED_EXTERNAL_FILE_ATTRIBUTES,
168
173
  PROPERTY_NAME_MS_DOS_COMPATIBLE,
169
174
  PROPERTY_NAME_ZIP64,
170
175
  PROPERTY_NAME_ENCRYPTED,
@@ -225,6 +230,7 @@ const ERR_INVALID_ENTRY_NAME = "File entry name exceeds 64KB";
225
230
  const ERR_INVALID_VERSION = "Version exceeds 65535";
226
231
  const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
227
232
  const ERR_UNSUPPORTED_ENCRYPTION_USDZ = "Encryption is not supported in USDZ files";
233
+ const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set";
228
234
  const ERR_INVALID_EXTRAFIELD_TYPE = "Extra field type exceeds 65535";
229
235
  const ERR_INVALID_EXTRAFIELD_DATA = "Extra field data exceeds 64KB";
230
236
  const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
@@ -234,6 +240,7 @@ const MIN_NTFS_TIME = BigInt(0);
234
240
  const MAX_NTFS_TIME = BigInt("0x7fffffffffffffff");
235
241
  const ERR_UNSUPPORTED_FORMAT = "Zip64 is not supported (set the 'zip64' option to 'true')";
236
242
  const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
243
+ const ERR_UNDETERMINED_SIZE = "Undetermined size";
237
244
  const ERR_UNDEFINED_READER = "Undefined reader";
238
245
  const ERR_ZIP_NOT_EMPTY = "Zip file not empty";
239
246
  const ERR_INVALID_UID = "Invalid uid (must be integer 0..2^32-1)";
@@ -441,7 +448,7 @@ class ZipWriter {
441
448
  await Promise.allSettled(Array.from(pendingAddFileCalls));
442
449
  }
443
450
  await closeFile(zipWriter, comment, options);
444
- const preventClose = getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
451
+ const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
445
452
  if (!preventClose) {
446
453
  await writable.getWriter().close();
447
454
  }
@@ -514,6 +521,7 @@ function watchAddFileCall(zipWriterStream, promiseAddFile, onerror) {
514
521
  export {
515
522
  ZipWriter,
516
523
  ZipWriterStream,
524
+ getEntriesSize,
517
525
  ERR_DUPLICATED_NAME,
518
526
  ERR_INVALID_COMMENT,
519
527
  ERR_INVALID_ENTRY_NAME,
@@ -523,8 +531,10 @@ export {
523
531
  ERR_INVALID_EXTRAFIELD_DATA,
524
532
  ERR_INVALID_ENCRYPTION_STRENGTH,
525
533
  ERR_UNSUPPORTED_ENCRYPTION_USDZ,
534
+ ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH,
526
535
  ERR_UNSUPPORTED_FORMAT,
527
536
  ERR_UNDEFINED_UNCOMPRESSED_SIZE,
537
+ ERR_UNDETERMINED_SIZE,
528
538
  ERR_UNDEFINED_READER,
529
539
  ERR_ZIP_NOT_EMPTY,
530
540
  ERR_INVALID_SIGNATURE_DATA,
@@ -629,7 +639,7 @@ function resolveAttributes(zipWriter, name, options) {
629
639
  if (versionMadeBy > MAX_16_BITS) {
630
640
  throw new Error(ERR_INVALID_VERSION);
631
641
  }
632
- let externalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES);
642
+ let externalFileAttributes = getAliasedOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_DEPRECATED_EXTERNAL_FILE_ATTRIBUTES);
633
643
  const externalFileAttributesProvided = externalFileAttributes !== UNDEFINED_VALUE;
634
644
  if (!externalFileAttributesProvided) {
635
645
  externalFileAttributes = 0;
@@ -677,7 +687,9 @@ function resolveAttributes(zipWriter, name, options) {
677
687
  }
678
688
  if (!externalFileAttributesProvided || unixModeProvided) {
679
689
  if (directory) {
680
- unixMode |= FILE_ATTR_UNIX_TYPE_DIR;
690
+ unixMode = (unixMode & ~FILE_ATTR_UNIX_TYPE_MASK) | FILE_ATTR_UNIX_TYPE_DIR;
691
+ } else if (!(unixMode & FILE_ATTR_UNIX_TYPE_MASK)) {
692
+ unixMode |= FILE_ATTR_UNIX_TYPE_FILE;
681
693
  }
682
694
  externalFileAttributes = ((unixMode & MAX_16_BITS) << 16) | (externalFileAttributes & MAX_16_BITS);
683
695
  }
@@ -730,15 +742,12 @@ function resolveMetadata(zipWriter, name, options) {
730
742
  const lastModDate = getOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE, new Date());
731
743
  const lastAccessDate = getOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
732
744
  const creationDate = getOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
733
- const internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, 0);
745
+ const internalFileAttributes = getAliasedOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES, 0);
734
746
  const passThrough = getOptionValue(zipWriter, options, OPTION_PASS_THROUGH);
735
- let password, rawPassword;
736
- if (!passThrough) {
737
- password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
738
- rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
739
- if ((password && typeof password != STRING_TYPE) || (rawPassword && !(rawPassword instanceof Uint8Array))) {
740
- throw new Error(ERR_INVALID_PASSWORD_TYPE);
741
- }
747
+ const password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
748
+ const rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
749
+ if ((password && typeof password != STRING_TYPE) || (rawPassword && !(rawPassword instanceof Uint8Array))) {
750
+ throw new Error(ERR_INVALID_PASSWORD_TYPE);
742
751
  }
743
752
  const encryptionStrength = getNumberOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
744
753
  const zipCrypto = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIPCRYPTO);
@@ -851,14 +860,24 @@ function serializeExtraField(extraField) {
851
860
  }
852
861
 
853
862
  async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, options) {
863
+ if (metadata.passThrough && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
864
+ throw new Error(ERR_UNDEFINED_READER);
865
+ }
866
+ let contentSize;
867
+ if (reader) {
868
+ reader = new GenericReader(reader);
869
+ await initStream(reader);
870
+ ({ size: contentSize } = reader);
871
+ }
872
+ return Object.assign({ reader }, resolveEntrySizes(zipWriter, Boolean(reader), contentSize, metadata, options));
873
+ }
874
+
875
+ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options) {
854
876
  const { passThrough, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
855
877
  let { dataDescriptor, zip64, level, compressionMethod } = metadata;
856
878
  let maximumCompressedSize = 0;
857
879
  let uncompressedSize = 0;
858
- if (passThrough) {
859
- if (!reader) {
860
- throw new Error(ERR_UNDEFINED_READER);
861
- }
880
+ if (passThrough && hasContent) {
862
881
  uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
863
882
  if (uncompressedSize === UNDEFINED_VALUE) {
864
883
  throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
@@ -866,29 +885,30 @@ async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, op
866
885
  }
867
886
  const zip64Enabled = zip64 === true;
868
887
  const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
869
- const encryptedEntry = Boolean(reader) && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
870
- if (!reader) {
888
+ if (hasContent && passThrough && !encrypted && getLength(password, rawPassword)) {
889
+ throw new Error(ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH);
890
+ }
891
+ const encryptedEntry = hasContent && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
892
+ if (!hasContent) {
871
893
  level = 0;
872
894
  compressionMethod = COMPRESSION_METHOD_STORE;
873
895
  }
874
896
  const encryptionOverhead = encryptedEntry ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
875
- if (reader) {
876
- reader = new GenericReader(reader);
877
- await initStream(reader);
897
+ if (hasContent) {
878
898
  if (!passThrough) {
879
- if (reader.size === UNDEFINED_VALUE) {
899
+ if (contentSize === UNDEFINED_VALUE) {
880
900
  dataDescriptor = true;
881
901
  if (zip64 || zip64 === UNDEFINED_VALUE) {
882
902
  zip64 = true;
883
903
  uncompressedSize = maximumCompressedSize = MAX_32_BITS + 1;
884
904
  }
885
905
  } else {
886
- options.uncompressedSize = uncompressedSize = reader.size;
887
- maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
906
+ options.uncompressedSize = uncompressedSize = contentSize;
907
+ maximumCompressedSize = (isCompressed(compressionMethod, level) ? getMaximumCompressedSize(uncompressedSize) : uncompressedSize) + encryptionOverhead;
888
908
  }
889
909
  } else {
890
910
  options.uncompressedSize = uncompressedSize;
891
- maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
911
+ maximumCompressedSize = contentSize === UNDEFINED_VALUE ? getMaximumCompressedSize(uncompressedSize) + encryptionOverhead : contentSize;
892
912
  }
893
913
  }
894
914
  const zip64UncompressedSize = zip64Enabled || uncompressedSize >= MAX_32_BITS;
@@ -902,7 +922,7 @@ async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, op
902
922
  }
903
923
  zip64 = zip64 || false;
904
924
  return {
905
- reader,
925
+ maximumCompressedSize,
906
926
  resolvedOptions: {
907
927
  dataDescriptor,
908
928
  zip64,
@@ -916,6 +936,78 @@ async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, op
916
936
  };
917
937
  }
918
938
 
939
+ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comment) {
940
+ const zipWriter = { options: writerOptions, config: getConfiguration() };
941
+ if (writerOptions[OPTION_SIGN_CENTRAL_DIRECTORY]) {
942
+ throw new Error(ERR_UNDETERMINED_SIZE);
943
+ }
944
+ const commentLength = getLength(comment);
945
+ if (commentLength > MAX_16_BITS) {
946
+ throw new Error(ERR_INVALID_COMMENT);
947
+ }
948
+ const usdz = writerOptions[OPTION_USDZ];
949
+ const files = new Map();
950
+ let layoutDependsOnWriteOrder = Boolean(usdz);
951
+ let offset = 0;
952
+ for (const entry of entries) {
953
+ let { name } = entry;
954
+ const { size } = entry;
955
+ const options = Object.assign({}, entry.options);
956
+ name = name.trim();
957
+ if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
958
+ name += DIRECTORY_SIGNATURE;
959
+ }
960
+ const attributesInfo = resolveAttributes(zipWriter, name, options);
961
+ ({ name } = attributesInfo);
962
+ const { resolvedOptions: metadata } = resolveMetadata(zipWriter, name, options);
963
+ if (metadata.level != 0 && metadata.compressionMethod === UNDEFINED_VALUE &&
964
+ !metadata.passThrough && !(await supportsDeflate(zipWriter.config))) {
965
+ metadata.level = 0;
966
+ }
967
+ const hasContent = !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY);
968
+ if (hasContent && size === UNDEFINED_VALUE) {
969
+ throw new Error(ERR_UNDETERMINED_SIZE);
970
+ }
971
+ const { maximumCompressedSize, resolvedOptions: sizes } = resolveEntrySizes(zipWriter, hasContent, size, metadata, options);
972
+ if (hasContent && !metadata.passThrough && isCompressed(sizes.compressionMethod, sizes.level)) {
973
+ throw new Error(ERR_UNDETERMINED_SIZE);
974
+ }
975
+ const entryOptions = Object.assign({}, options, attributesInfo.resolvedOptions, metadata, sizes);
976
+ const headerInfo = getHeaderInfo(entryOptions);
977
+ const dataDescriptorInfo = getDataDescriptorInfo(entryOptions);
978
+ const entryInfo = {
979
+ headerInfo,
980
+ metadataSize: getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray)
981
+ };
982
+ if (usdz) {
983
+ appendExtraFieldUSDZ(entryInfo, offset);
984
+ }
985
+ const compressedSize = hasContent ? maximumCompressedSize : 0;
986
+ if (offset >= MAX_32_BITS) {
987
+ layoutDependsOnWriteOrder = true;
988
+ }
989
+ files.set(name, Object.assign({}, entryOptions, headerInfo, {
990
+ offset,
991
+ diskNumberStart: 0,
992
+ compressedSize
993
+ }));
994
+ offset += entryInfo.metadataSize + compressedSize;
995
+ }
996
+ if (layoutDependsOnWriteOrder && !writeOrderGuaranteed) {
997
+ throw new Error(ERR_UNDETERMINED_SIZE);
998
+ }
999
+ const directoryDataLength = createDirectoryRecords(files);
1000
+ let zip64 = getOptionValue(zipWriter, writerOptions, PROPERTY_NAME_ZIP64);
1001
+ if (offset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || files.size >= MAX_16_BITS) {
1002
+ if (zip64 === false) {
1003
+ throw new Error(ERR_UNSUPPORTED_FORMAT);
1004
+ } else {
1005
+ zip64 = true;
1006
+ }
1007
+ }
1008
+ return offset + directoryDataLength + commentLength + (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
1009
+ }
1010
+
919
1011
  async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
920
1012
  const {
921
1013
  files,
@@ -1287,9 +1379,7 @@ function getHeaderInfo(options) {
1287
1379
  crc32
1288
1380
  } = options;
1289
1381
  let { version, compressionMethod } = options;
1290
- const compressed = !directory && (compressionMethod === UNDEFINED_VALUE
1291
- ? (level === UNDEFINED_VALUE || level > 0)
1292
- : compressionMethod !== COMPRESSION_METHOD_STORE);
1382
+ const compressed = !directory && isCompressed(compressionMethod, level);
1293
1383
  let rawLocalExtraFieldZip64;
1294
1384
  const uncompressedFile = passThrough || !compressed;
1295
1385
  const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || uncompressedFile));
@@ -1976,6 +2066,16 @@ function getOptionValue(zipWriter, options, name, defaultValue) {
1976
2066
  return result === UNDEFINED_VALUE ? defaultValue : result;
1977
2067
  }
1978
2068
 
2069
+ function getAliasedOptionValue(zipWriter, options, name, deprecatedName, defaultValue) {
2070
+ const value = getAliasedValue(options, name, deprecatedName);
2071
+ const result = value === UNDEFINED_VALUE ? getAliasedValue(zipWriter.options, name, deprecatedName) : value;
2072
+ return result === UNDEFINED_VALUE ? defaultValue : result;
2073
+ }
2074
+
2075
+ function getAliasedValue(options, name, deprecatedName) {
2076
+ return options[name] === UNDEFINED_VALUE ? options[deprecatedName] : options[name];
2077
+ }
2078
+
1979
2079
  function getNumberOptionValue(zipWriter, options, name, defaultValue) {
1980
2080
  return toNumber(getOptionValue(zipWriter, options, name, defaultValue));
1981
2081
  }
@@ -1988,6 +2088,12 @@ function getMaximumCompressedSize(uncompressedSize) {
1988
2088
  return uncompressedSize + (5 * (Math.floor(uncompressedSize / 16383) + 1));
1989
2089
  }
1990
2090
 
2091
+ function isCompressed(compressionMethod, level) {
2092
+ return compressionMethod === UNDEFINED_VALUE
2093
+ ? (level === UNDEFINED_VALUE || level > 0)
2094
+ : compressionMethod !== COMPRESSION_METHOD_STORE;
2095
+ }
2096
+
1991
2097
  function setUint8(view, offset, value) {
1992
2098
  view.setUint8(offset, value);
1993
2099
  }
@@ -40,8 +40,10 @@ export {
40
40
  ERR_INVALID_EXTRAFIELD_DATA,
41
41
  ERR_INVALID_ENCRYPTION_STRENGTH,
42
42
  ERR_UNSUPPORTED_ENCRYPTION_USDZ,
43
+ ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH,
43
44
  ERR_UNSUPPORTED_FORMAT,
44
45
  ERR_UNDEFINED_UNCOMPRESSED_SIZE,
46
+ ERR_UNDETERMINED_SIZE,
45
47
  ERR_UNDEFINED_READER,
46
48
  ERR_ZIP_NOT_EMPTY,
47
49
  ERR_INVALID_SIGNATURE_DATA,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "A JavaScript library to zip and unzip files in the browser, Deno and Node.js",
4
4
  "author": "Gildas Lormeau",
5
5
  "license": "BSD-3-Clause",
6
- "version": "2.8.51",
6
+ "version": "2.8.52",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "zip",
@@ -36,13 +36,13 @@
36
36
  "scripts": {
37
37
  "lint": "npx eslint . && deno lint",
38
38
  "test": "npm run test-node && npm run test-deno && npm run test-bun && npm run test-node-dist && npm run test-deno-dist && npm run test-bun-dist && npm run test-web-streams-polyfill && npm run test-types",
39
- "test-browsers": "npm run test-chrome && npm run test-firefox && npm run test-webkit",
39
+ "test-browsers": "npm run test-chrome && npm run test-firefox && npm run test-safari",
40
40
  "serve-tests": "http-server -p 8888 -c-1 .",
41
41
  "test-all": "npm run lint && npm run test && npm run test-browsers",
42
42
  "build": "npx rollup -c",
43
43
  "build-dev": "npx rollup -c --environment DEV",
44
44
  "build-doc-api": "npx typedoc",
45
- "build-doc-markdown": "npx typedoc --plugin typedoc-plugin-markdown --out ./docs",
45
+ "build-doc-markdown": "npx typedoc --plugin typedoc-plugin-markdown --disableSources --out ./docs",
46
46
  "test-deno": "cd ./tests && deno test --allow-read ./deno-runner.js",
47
47
  "test-web-streams-polyfill": "cd ./tests && deno run --allow-read ./web-streams-polyfill-runner.js",
48
48
  "test-node": "cd ./tests && node ./node-runner.js",
@@ -53,7 +53,7 @@
53
53
  "test-types": "tsc -p ./tests/types/tsconfig.json",
54
54
  "test-firefox": "node ./tests/browser-runner.js firefox",
55
55
  "test-chrome": "node ./tests/browser-runner.js chrome",
56
- "test-webkit": "node ./tests/browser-runner.js webkit"
56
+ "test-safari": "node ./tests/browser-runner.js safari"
57
57
  },
58
58
  "main": "./index.cjs",
59
59
  "module": "./index.js",
@@ -260,8 +260,8 @@
260
260
  "@rollup/plugin-terser": "^1.0.0",
261
261
  "eslint": "^10.8.1",
262
262
  "http-server": "^14.1.1",
263
- "playwright": "^1.62.1",
264
263
  "rollup": "^4.62.4",
264
+ "selenium-webdriver": "^4.47.0",
265
265
  "typedoc": "^0.28.20",
266
266
  "typedoc-plugin-markdown": "^4.12.0",
267
267
  "web-streams-polyfill": "^4.3.0",