@zip.js/zip.js 2.11.1 → 2.11.3

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.
package/lib/core/io.js CHANGED
@@ -91,11 +91,10 @@ class Reader extends Stream {
91
91
  const data = await readUint8Array(reader, offset + chunkOffset, dataSize);
92
92
  if (data.length) {
93
93
  controller.enqueue(data);
94
+ chunkOffset += data.length;
94
95
  }
95
- if ((chunkOffset + chunkSize >= size) || (!data.length && dataSize)) {
96
+ if ((size !== UNDEFINED_VALUE && chunkOffset >= size) || (!data.length && dataSize)) {
96
97
  controller.close();
97
- } else {
98
- chunkOffset += chunkSize;
99
98
  }
100
99
  }
101
100
  });
@@ -26,6 +26,6 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- const VERSION = "2.11.1";
29
+ const VERSION = "2.11.3";
30
30
 
31
31
  export { VERSION };
@@ -587,12 +587,14 @@ class ZipDirectoryEntry extends ZipEntry {
587
587
  if (options.bufferedWrite === UNDEFINED_VALUE) {
588
588
  options.bufferedWrite = true;
589
589
  }
590
- const entries = zipEntry.getChildren({ recursive: true }).filter(child => !isImplicitDirectory(child)).map(child => {
590
+ const children = zipEntry.getChildren({ recursive: true });
591
+ const entries = children.filter(child => !isImplicitDirectory(child)).map(child => {
591
592
  const { name, entryOptions } = getChildEntryOptions(child, zipEntry, options);
592
593
  return { name, size: child.directory ? 0 : getDeterminedSize(child, isPassThrough(child, options)), options: entryOptions };
593
594
  });
594
595
  const writeOrderGuaranteed = !options.bufferedWrite ||
595
- (entries.every(entry => entry.options.keepOrder !== false) && zipEntry.children.every(child => !child.children.length));
596
+ (entries.every(entry => entry.options.keepOrder !== false) &&
597
+ children.every(child => isImplicitDirectory(child) || !child.children.length));
596
598
  return getEntriesSize(options, entries, writeOrderGuaranteed, options.globalComment);
597
599
  }
598
600
 
@@ -63,6 +63,10 @@
63
63
  * failing before they reach getFileEntry().
64
64
  * - `zipWriter.lockWriter`/`writerLocked` is the mutex over the writer; only the holder may write
65
65
  * into it or read the position variables above.
66
+ * - At most one entry is written directly into the writer at a time, every other one is buffered.
67
+ * `bufferedWrites`/`directWrites` record the choice in the same synchronous block that makes it,
68
+ * because `writerLocked` is only raised an await later: concurrent add() calls all decide before
69
+ * that, and would all pick the direct path and then serialize on the previous entry's lock.
66
70
  * - Both write paths await the lock of the previous entry before requesting the writer lock.
67
71
  * Requesting it first would deadlock an entry written directly into the writer (i.e. without
68
72
  * being buffered) against a buffered predecessor, which needs that same lock to be flushed.
@@ -250,6 +254,7 @@ const ERR_INVALID_ENTRY_NAME = "File entry name exceeds 64KB";
250
254
  const ERR_INVALID_VERSION = "Version exceeds 65535";
251
255
  const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
252
256
  const ERR_UNSUPPORTED_ENCRYPTION_USDZ = "Encryption is not supported in USDZ files";
257
+ const ERR_UNSUPPORTED_SPLIT_USDZ = "Split zip files are not supported in USDZ files";
253
258
  const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set";
254
259
  const ERR_INVALID_EXTRAFIELD = "Invalid extra field (must be a Map)";
255
260
  const ERR_INVALID_EXTRAFIELD_TYPE = "Invalid extra field type (must be integer 0..65535)";
@@ -264,6 +269,7 @@ const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
264
269
  const ERR_UNDEFINED_COMPRESSION_METHOD = "Undefined compression method";
265
270
  const ERR_UNDETERMINED_SIZE = "Undetermined size";
266
271
  const ERR_UNDEFINED_READER = "Undefined reader";
272
+ const ERR_INVALID_READER = "Invalid reader (must be a Reader instance, a ReadableStream instance, or an object with a 'readable' property)";
267
273
  const ERR_ZIP_NOT_EMPTY = "Zip file not empty";
268
274
  const ERR_INVALID_UID = "Invalid uid (must be integer 0..2^32-1)";
269
275
  const ERR_INVALID_GID = "Invalid gid (must be integer 0..2^32-1)";
@@ -295,6 +301,9 @@ class ZipWriter {
295
301
  const addSplitZipSignature =
296
302
  availableSize > 0 && availableSize !== INFINITY_VALUE &&
297
303
  maxSize > 0 && maxSize !== INFINITY_VALUE;
304
+ if (addSplitZipSignature && options[OPTION_USDZ]) {
305
+ throw new Error(ERR_UNSUPPORTED_SPLIT_USDZ);
306
+ }
298
307
  Object.assign(this, {
299
308
  writer,
300
309
  addSplitZipSignature,
@@ -306,6 +315,7 @@ class ZipWriter {
306
315
  pendingAddFileCalls: new Set(),
307
316
  pendingErrors: [],
308
317
  bufferedWrites: 0,
318
+ directWrites: 0,
309
319
  lastFileEntry: UNDEFINED_VALUE
310
320
  });
311
321
  }
@@ -409,6 +419,7 @@ class ZipWriter {
409
419
  }
410
420
  const { offset, diskNumberStart } = entryPositions.get(entry);
411
421
  Object.assign(entry, {
422
+ zip64Enabled: true,
412
423
  zip64UncompressedSize,
413
424
  zip64CompressedSize,
414
425
  offset,
@@ -658,12 +669,14 @@ export {
658
669
  ERR_INVALID_EXTRAFIELD_DATA,
659
670
  ERR_INVALID_ENCRYPTION_STRENGTH,
660
671
  ERR_UNSUPPORTED_ENCRYPTION_USDZ,
672
+ ERR_UNSUPPORTED_SPLIT_USDZ,
661
673
  ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH,
662
674
  ERR_UNSUPPORTED_FORMAT,
663
675
  ERR_UNDEFINED_UNCOMPRESSED_SIZE,
664
676
  ERR_UNDEFINED_COMPRESSION_METHOD,
665
677
  ERR_UNDETERMINED_SIZE,
666
678
  ERR_UNDEFINED_READER,
679
+ ERR_INVALID_READER,
667
680
  ERR_ZIP_NOT_EMPTY,
668
681
  ERR_INVALID_SIGNATURE_DATA,
669
682
  ERR_INVALID_UID,
@@ -1022,6 +1035,9 @@ async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, op
1022
1035
  if (reader) {
1023
1036
  reader = new GenericReader(reader);
1024
1037
  await initStream(reader);
1038
+ if (!reader.readable && !reader.readUint8Array) {
1039
+ throw new Error(ERR_INVALID_READER);
1040
+ }
1025
1041
  ({ size: contentSize } = reader);
1026
1042
  }
1027
1043
  return Object.assign({ reader }, resolveEntrySizes(zipWriter, Boolean(reader), contentSize, metadata, options));
@@ -1032,6 +1048,7 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1032
1048
  let { dataDescriptor, zip64, level, compressionMethod } = metadata;
1033
1049
  let maximumCompressedSize = 0;
1034
1050
  let uncompressedSize = 0;
1051
+ let unknownSize = false;
1035
1052
  if (passThrough && hasContent) {
1036
1053
  uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
1037
1054
  if (uncompressedSize === UNDEFINED_VALUE) {
@@ -1057,8 +1074,8 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1057
1074
  if (contentSize === UNDEFINED_VALUE) {
1058
1075
  dataDescriptor = true;
1059
1076
  if (zip64 || zip64 === UNDEFINED_VALUE) {
1060
- zip64 = true;
1061
- uncompressedSize = maximumCompressedSize = MAX_32_BITS + 1;
1077
+ zip64 = unknownSize = true;
1078
+ maximumCompressedSize = MAX_32_BITS + 1;
1062
1079
  }
1063
1080
  } else {
1064
1081
  options.uncompressedSize = uncompressedSize = contentSize;
@@ -1073,7 +1090,7 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1073
1090
  if (emptyEntry && !zipCrypto && getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR) === UNDEFINED_VALUE) {
1074
1091
  dataDescriptor = false;
1075
1092
  }
1076
- const zip64UncompressedSize = zip64Enabled || uncompressedSize >= MAX_32_BITS;
1093
+ const zip64UncompressedSize = zip64Enabled || unknownSize || uncompressedSize >= MAX_32_BITS;
1077
1094
  const zip64CompressedSize = zip64Enabled || maximumCompressedSize >= MAX_32_BITS;
1078
1095
  if (zip64UncompressedSize || zip64CompressedSize) {
1079
1096
  if (zip64 === false) {
@@ -1089,6 +1106,8 @@ function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options
1089
1106
  dataDescriptor,
1090
1107
  emptyEntry,
1091
1108
  zip64,
1109
+ zip64Enabled,
1110
+ unknownSize,
1092
1111
  zip64UncompressedSize,
1093
1112
  zip64CompressedSize,
1094
1113
  uncompressedSize,
@@ -1162,7 +1181,7 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1162
1181
  if (layoutDependsOnWriteOrder && !writeOrderGuaranteed) {
1163
1182
  throw new Error(ERR_UNDETERMINED_SIZE);
1164
1183
  }
1165
- const directoryDataLength = createDirectoryRecords(files);
1184
+ const { directoryDataLength, zip64Entries } = createDirectoryRecords(files);
1166
1185
  let zip64 = getOptionValue(zipWriter, writerOptions, PROPERTY_NAME_ZIP64);
1167
1186
  if (offset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || files.size >= MAX_16_BITS) {
1168
1187
  if (zip64 === false) {
@@ -1170,6 +1189,8 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1170
1189
  } else {
1171
1190
  zip64 = true;
1172
1191
  }
1192
+ } else if (zip64 === UNDEFINED_VALUE && zip64Entries) {
1193
+ zip64 = true;
1173
1194
  }
1174
1195
  return offset - initialOffset + directoryDataLength + commentLength + (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
1175
1196
  }
@@ -1194,6 +1215,7 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1194
1215
  const usdz = zipWriter.options[OPTION_USDZ];
1195
1216
  let fileEntry = pendingFileEntry;
1196
1217
  let bufferedWrite;
1218
+ let directWrite;
1197
1219
  let releaseLockWriter;
1198
1220
  let writingBufferedEntryData;
1199
1221
  let writingEntryData;
@@ -1203,7 +1225,7 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1203
1225
  const lockPreviousFileEntry = keepOrder && previousFileEntry ? previousFileEntry.lockFileEntry : UNDEFINED_VALUE;
1204
1226
  fileEntries.set(name, fileEntry);
1205
1227
  try {
1206
- if (options.bufferedWrite || !keepOrder || zipWriter.writerLocked || zipWriter.bufferedWrites || (!dataDescriptor && !emptyEntry)) {
1228
+ if (options.bufferedWrite || !keepOrder || zipWriter.writerLocked || zipWriter.bufferedWrites || zipWriter.directWrites || (!dataDescriptor && !emptyEntry)) {
1207
1229
  bufferedWrite = true;
1208
1230
  zipWriter.bufferedWrites++;
1209
1231
  if (options.createTempStream) {
@@ -1214,6 +1236,8 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1214
1236
  fileWriter.size = 0;
1215
1237
  await initStream(writer);
1216
1238
  } else {
1239
+ directWrite = true;
1240
+ zipWriter.directWrites++;
1217
1241
  fileWriter = writer;
1218
1242
  await lockPreviousFileEntry;
1219
1243
  await requestLockWriter();
@@ -1292,6 +1316,9 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1292
1316
  if (bufferedWrite) {
1293
1317
  zipWriter.bufferedWrites--;
1294
1318
  }
1319
+ if (directWrite) {
1320
+ zipWriter.directWrites--;
1321
+ }
1295
1322
  if (releaseLockFileEntry) {
1296
1323
  releaseLockFileEntry(lockPreviousFileEntry);
1297
1324
  }
@@ -1356,6 +1383,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1356
1383
  rawPassword,
1357
1384
  level,
1358
1385
  zip64,
1386
+ zip64Enabled,
1359
1387
  zip64UncompressedSize,
1360
1388
  zip64CompressedSize,
1361
1389
  zipCrypto,
@@ -1396,6 +1424,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1396
1424
  lockFileEntry,
1397
1425
  versionMadeBy,
1398
1426
  zip64,
1427
+ zip64Enabled,
1399
1428
  directory: Boolean(directory),
1400
1429
  executable: Boolean(executable),
1401
1430
  filenameUTF8: true,
@@ -1436,8 +1465,8 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1436
1465
  }
1437
1466
  const { writable } = writer;
1438
1467
  if (reader) {
1439
- const readable = toCompatibleReadable(createReadable(reader));
1440
1468
  const size = reader.size;
1469
+ const readable = toCompatibleReadable(createReadable(reader, { size }));
1441
1470
  const workerOptions = {
1442
1471
  options: {
1443
1472
  codecType: CODEC_DEFLATE,
@@ -1539,13 +1568,14 @@ function getHeaderInfo(options) {
1539
1568
  zip64UncompressedSize,
1540
1569
  zip64CompressedSize,
1541
1570
  uncompressedSize,
1571
+ unknownSize,
1542
1572
  crc32
1543
1573
  } = options;
1544
1574
  let { version, compressionMethod } = options;
1545
1575
  const compressed = !directory && isCompressed(compressionMethod, level);
1546
1576
  let rawLocalExtraFieldZip64;
1547
1577
  const uncompressedFile = passThrough || !compressed;
1548
- const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || uncompressedFile));
1578
+ const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || (uncompressedFile && !unknownSize)));
1549
1579
  const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
1550
1580
  if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
1551
1581
  const length = 4 + 16;
@@ -1679,6 +1709,10 @@ function getHeaderInfo(options) {
1679
1709
  throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
1680
1710
  }
1681
1711
  const dosLastModDate = new Date(Math.ceil(Math.floor(lastModDate.getTime() / 1000) / 2) * 2000);
1712
+ const clampedLastModDate = dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate;
1713
+ const storedLastModDate = getLength(rawExtraFieldExtendedTimestamp) ?
1714
+ new Date(getTimeUnix(lastModDate) * 1000) :
1715
+ getLength(rawExtraFieldNTFS) ? lastModDate : clampedLastModDate;
1682
1716
  const {
1683
1717
  headerArray,
1684
1718
  headerView,
@@ -1688,7 +1722,7 @@ function getHeaderInfo(options) {
1688
1722
  bitFlag: getBitFlag(level, useUnicodeFileNames, dataDescriptor, encrypted, compressionMethod),
1689
1723
  compressionMethod,
1690
1724
  uncompressedSize,
1691
- lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
1725
+ lastModDate: clampedLastModDate,
1692
1726
  rawLastModDate: rawLastModDateOption,
1693
1727
  rawFilename,
1694
1728
  zip64CompressedSize,
@@ -1723,7 +1757,7 @@ function getHeaderInfo(options) {
1723
1757
  localHeaderView,
1724
1758
  headerArray,
1725
1759
  headerView,
1726
- lastModDate,
1760
+ lastModDate: storedLastModDate,
1727
1761
  rawLastModDate,
1728
1762
  encrypted,
1729
1763
  compressed,
@@ -1895,14 +1929,15 @@ function updateLocalHeader({
1895
1929
 
1896
1930
 
1897
1931
  async function closeFile(zipWriter, comment, options) {
1898
- const directoryDataLength = createDirectoryRecords(zipWriter.fileEntries);
1932
+ const { directoryDataLength, zip64Entries } = createDirectoryRecords(zipWriter.fileEntries);
1899
1933
  const { directoryStart, directoryEnd, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
1900
1934
  const signatureLength = await writeDigitalSignatureRecord(zipWriter, directoryArray, options);
1901
- await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength });
1935
+ await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength, zip64Entries });
1902
1936
  }
1903
1937
 
1904
1938
  function createDirectoryRecords(files) {
1905
1939
  let directoryDataLength = 0;
1940
+ let zip64Entries = false;
1906
1941
  for (const [, fileEntry] of files) {
1907
1942
  const {
1908
1943
  rawFilename,
@@ -1915,11 +1950,20 @@ function createDirectoryRecords(files) {
1915
1950
  extendedTimestamp,
1916
1951
  extraFieldExtendedTimestampFlag,
1917
1952
  lastModDate,
1918
- zip64UncompressedSize,
1919
- zip64CompressedSize,
1953
+ zip64Enabled,
1920
1954
  uncompressedSize,
1921
1955
  compressedSize
1922
1956
  } = fileEntry;
1957
+ let { zip64UncompressedSize, zip64CompressedSize } = fileEntry;
1958
+ if (!zip64Enabled) {
1959
+ if (zip64UncompressedSize && uncompressedSize < MAX_32_BITS) {
1960
+ zip64UncompressedSize = fileEntry.zip64UncompressedSize = false;
1961
+ }
1962
+ if (zip64CompressedSize && compressedSize < MAX_32_BITS) {
1963
+ zip64CompressedSize = fileEntry.zip64CompressedSize = false;
1964
+ }
1965
+ }
1966
+ zip64Entries = zip64Entries || zip64UncompressedSize || zip64CompressedSize;
1923
1967
  const zip64Offset = fileEntry.offset >= MAX_32_BITS;
1924
1968
  const zip64DiskNumberStart = fileEntry.diskNumberStart >= MAX_16_BITS;
1925
1969
  let rawExtraFieldZip64;
@@ -1973,7 +2017,7 @@ function createDirectoryRecords(files) {
1973
2017
  }
1974
2018
  directoryDataLength += CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
1975
2019
  }
1976
- return directoryDataLength;
2020
+ return { directoryDataLength, zip64Entries };
1977
2021
  }
1978
2022
 
1979
2023
  async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
@@ -2093,7 +2137,7 @@ async function writeDigitalSignatureRecord(zipWriter, directoryArray, options) {
2093
2137
 
2094
2138
  async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2095
2139
  const { writer } = zipWriter;
2096
- const { directoryStart, directoryEnd, signatureLength } = cdInfo;
2140
+ const { directoryStart, directoryEnd, signatureLength, zip64Entries } = cdInfo;
2097
2141
  let { directoryDataLength } = cdInfo;
2098
2142
  let fileEntriesLength = zipWriter.fileEntries.size;
2099
2143
  let diskNumber = directoryStart.diskNumber;
@@ -2113,6 +2157,8 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
2113
2157
  } else {
2114
2158
  zip64 = true;
2115
2159
  }
2160
+ } else if (zip64 === UNDEFINED_VALUE && zip64Entries) {
2161
+ zip64 = true;
2116
2162
  }
2117
2163
  const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
2118
2164
  if (exceedsAvailableSize(writer, getLength(endOfdirectoryRecord.array) + commentLength)) {
@@ -45,12 +45,14 @@ export {
45
45
  ERR_INVALID_EXTRAFIELD_DATA,
46
46
  ERR_INVALID_ENCRYPTION_STRENGTH,
47
47
  ERR_UNSUPPORTED_ENCRYPTION_USDZ,
48
+ ERR_UNSUPPORTED_SPLIT_USDZ,
48
49
  ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH,
49
50
  ERR_UNSUPPORTED_FORMAT,
50
51
  ERR_UNDEFINED_UNCOMPRESSED_SIZE,
51
52
  ERR_UNDEFINED_COMPRESSION_METHOD,
52
53
  ERR_UNDETERMINED_SIZE,
53
54
  ERR_UNDEFINED_READER,
55
+ ERR_INVALID_READER,
54
56
  ERR_ZIP_NOT_EMPTY,
55
57
  ERR_INVALID_SIGNATURE_DATA,
56
58
  ERR_INVALID_UID,
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.11.1",
6
+ "version": "2.11.3",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "zip",