@zip.js/zip.js 2.18.2 → 2.20.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 (45) hide show
  1. package/BENCHMARKS.md +172 -144
  2. package/deno.json +1 -1
  3. package/dist/zip-core-external.js +192 -120
  4. package/dist/zip-core-external.min.js +1 -1
  5. package/dist/zip-core.js +193 -119
  6. package/dist/zip-core.min.js +1 -1
  7. package/dist/zip-fs-core-external.js +192 -120
  8. package/dist/zip-fs-core-external.min.js +1 -1
  9. package/dist/zip-fs-core.js +193 -119
  10. package/dist/zip-fs-core.min.js +1 -1
  11. package/dist/zip-fs-external.js +192 -120
  12. package/dist/zip-fs-external.min.js +1 -1
  13. package/dist/zip-fs-native.js +195 -121
  14. package/dist/zip-fs-native.min.js +1 -1
  15. package/dist/zip-fs.js +194 -120
  16. package/dist/zip-fs.min.js +1 -1
  17. package/dist/zip-legacy.js +195 -121
  18. package/dist/zip-legacy.min.js +1 -1
  19. package/dist/zip-native.js +195 -121
  20. package/dist/zip-native.min.js +1 -1
  21. package/dist/zip-web-worker-native.js +1 -1
  22. package/dist/zip-web-worker.js +1 -1
  23. package/dist/zip.js +194 -120
  24. package/dist/zip.min.js +1 -1
  25. package/index-native.cjs +195 -121
  26. package/index-native.min.js +1 -1
  27. package/index.cjs +194 -120
  28. package/index.d.cts +95 -26
  29. package/index.d.ts +95 -26
  30. package/index.min.js +1 -1
  31. package/lib/core/codec-pool.js +1 -2
  32. package/lib/core/codec-worker-web.js +11 -39
  33. package/lib/core/codec-worker.js +1 -2
  34. package/lib/core/configuration.js +1 -1
  35. package/lib/core/options.js +0 -2
  36. package/lib/core/streams/codec-stream.js +1 -1
  37. package/lib/core/version.js +1 -1
  38. package/lib/core/web-worker-base.js +2 -3
  39. package/lib/core/web-worker-inline-native.js +1 -1
  40. package/lib/core/web-worker-inline-wasm.js +1 -1
  41. package/lib/core/zip-reader.js +100 -40
  42. package/lib/core/zip-writer.js +75 -34
  43. package/lib/zip-core-reader.js +2 -0
  44. package/package.json +1 -1
  45. package/worker-message-property-names.js +1 -1
@@ -175,7 +175,6 @@ import {
175
175
  STRICTNESS_TOLERANT,
176
176
  OPTION_USE_WEB_WORKERS,
177
177
  OPTION_USE_COMPRESSION_STREAM,
178
- OPTION_TRANSFER_STREAMS,
179
178
  OPTION_PREVENT_CLOSE,
180
179
  checkFunctionOption,
181
180
  checkSignalOption,
@@ -213,6 +212,8 @@ const WARNING_APPENDED_DATA = "appended data";
213
212
  const WARNING_PREPENDED_DATA = "prepended data";
214
213
  const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
215
214
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
215
+ const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = "mismatched central directory offset";
216
+ const WARNING_MISSING_ZIP64_EXTRA_FIELD = "missing zip64 extra field";
216
217
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
217
218
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
218
219
  const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
@@ -375,6 +376,8 @@ class ZipReader {
375
376
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
376
377
  }
377
378
  directoryDataOffset = getDiskOffset(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
379
+ } else if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || diskNumber == MAX_16_BITS) {
380
+ throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
378
381
  }
379
382
  }
380
383
  let declaredDirectoryDataLength = directoryDataLength;
@@ -397,25 +400,44 @@ class ZipReader {
397
400
  if (directoryArray.length < 4) {
398
401
  throw new Error(ERR_BAD_FORMAT);
399
402
  }
400
- const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
403
+ let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
401
404
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
402
405
  const storedPointsAtDirectory = getUint32(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
403
406
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
404
407
  detectEncryptedCentralDirectory(directoryView);
405
408
  let reconcile = !storedPointsAtDirectory;
406
- if (!reconcile && expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
407
- const expectedSignatureArray = await readUint8Array(reader, expectedDirectoryDataOffset, 4);
408
- reconcile = getUint32(getDataView(expectedSignatureArray), 0) == CENTRAL_FILE_HEADER_SIGNATURE;
409
+ if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
410
+ const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
411
+ if (storedPointsAtDirectory) {
412
+ reconcile = expectedPointsAtDirectory;
413
+ } else if (!expectedPointsAtDirectory) {
414
+ const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
415
+ const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
416
+ if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
417
+ await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
418
+ expectedDirectoryDataOffset = signedDirectoryDataOffset;
419
+ }
420
+ }
409
421
  }
410
422
  if (reconcile) {
411
423
  const originalDirectoryDataOffset = directoryDataOffset;
412
424
  directoryDataOffset = expectedDirectoryDataOffset;
425
+ directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
426
+ directoryView = getDataView(directoryArray);
413
427
  if (directoryDataOffset > originalDirectoryDataOffset) {
414
428
  prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
415
429
  prependedCentralDirectory = storedPointsAtDirectory;
430
+ } else {
431
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
432
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
433
+ if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
434
+ const localHeaderOffset = getUint32(directoryView, 42);
435
+ if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
436
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
437
+ prependedDataLength += offsetDelta;
438
+ }
439
+ }
416
440
  }
417
- directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
418
- directoryView = getDataView(directoryArray);
419
441
  }
420
442
  }
421
443
  }
@@ -499,17 +521,6 @@ class ZipReader {
499
521
  if (filename === UNDEFINED_VALUE) {
500
522
  filename = decodeText(rawFilename, rawFilenameEncoding);
501
523
  }
502
- if (normalizeFilename) {
503
- const normalizedFilename = normalizeFilename(filename);
504
- if (normalizedFilename !== UNDEFINED_VALUE) {
505
- filename = normalizedFilename;
506
- }
507
- }
508
- if (isUnsafeFilename(filename, filenameValidation)) {
509
- const error = new Error(ERR_UNSAFE_FILENAME);
510
- error.filename = filename;
511
- throw error;
512
- }
513
524
  let comment = decode(rawComment, rawCommentEncoding, TEXT_TYPE_COMMENT);
514
525
  if (comment === UNDEFINED_VALUE) {
515
526
  comment = decodeText(rawComment, rawCommentEncoding);
@@ -537,7 +548,29 @@ class ZipReader {
537
548
  filename,
538
549
  comment
539
550
  });
540
- if (readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6)) {
551
+ let malformedExtraField;
552
+ try {
553
+ malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
554
+ } catch (error) {
555
+ if (checkAmbiguity || error.message != ERR_EXTRAFIELD_ZIP64_NOT_FOUND) {
556
+ throw error;
557
+ }
558
+ fileEntry.zip64ExtraFieldMissing = true;
559
+ addWarning(warnings, WARNING_MISSING_ZIP64_EXTRA_FIELD, fileEntry.filename);
560
+ }
561
+ filename = fileEntry.filename;
562
+ if (normalizeFilename) {
563
+ const normalizedFilename = normalizeFilename(filename);
564
+ if (normalizedFilename !== UNDEFINED_VALUE) {
565
+ filename = fileEntry.filename = normalizedFilename;
566
+ }
567
+ }
568
+ if (isUnsafeFilename(filename, filenameValidation)) {
569
+ const error = new Error(ERR_UNSAFE_FILENAME);
570
+ error.filename = filename;
571
+ throw error;
572
+ }
573
+ if (malformedExtraField) {
541
574
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
542
575
  }
543
576
  fileEntry.offset += prependedDataLength;
@@ -566,11 +599,12 @@ class ZipReader {
566
599
  const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
567
600
  const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
568
601
  const symlink = (unixType & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK;
569
- const executable = !symlink && ((fileEntry.unixMode !== UNDEFINED_VALUE)
570
- ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
571
- : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
572
602
  const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
573
603
  const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
604
+ const directory = modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE);
605
+ const executable = !symlink && !directory && ((fileEntry.unixMode !== UNDEFINED_VALUE)
606
+ ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
607
+ : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
574
608
  Object.assign(fileEntry, {
575
609
  setuid,
576
610
  setgid,
@@ -578,7 +612,7 @@ class ZipReader {
578
612
  symlink,
579
613
  unixExternalUpper,
580
614
  executable,
581
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
615
+ directory,
582
616
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
583
617
  });
584
618
  const entry = new Entry(fileEntry);
@@ -634,10 +668,10 @@ class ZipReader {
634
668
  const extractAppendedData = getOptionValue(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
635
669
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
636
670
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
637
- if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
671
+ if (checkAmbiguity && (prependedDataLength > 0 || (filesLength && startOffset > splitZipSignatureLength))) {
638
672
  throwAmbiguousArchive(WARNING_PREPENDED_DATA);
639
673
  }
640
- if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
674
+ if (prependedDataLength > 0 || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
641
675
  addWarning(warnings, WARNING_PREPENDED_DATA);
642
676
  }
643
677
  if (prependedCentralDirectory) {
@@ -826,6 +860,8 @@ export {
826
860
  WARNING_PREPENDED_DATA,
827
861
  WARNING_PREPENDED_CENTRAL_DIRECTORY,
828
862
  WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
863
+ WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET,
864
+ WARNING_MISSING_ZIP64_EXTRA_FIELD,
829
865
  WARNING_DUPLICATE_FILENAME,
830
866
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
831
867
  WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
@@ -846,6 +882,9 @@ class ZipEntry {
846
882
 
847
883
  async getData(writer, fileEntry, readRanges, options = {}) {
848
884
  const zipEntry = this;
885
+ if (zipEntry.zip64ExtraFieldMissing) {
886
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
887
+ }
849
888
  const config = getConfiguration();
850
889
  const {
851
890
  reader,
@@ -990,7 +1029,6 @@ class ZipEntry {
990
1029
  encrypted,
991
1030
  useWebWorkers: getOptionValue(zipEntry, options, OPTION_USE_WEB_WORKERS),
992
1031
  useCompressionStream,
993
- transferStreams: getOptionValue(zipEntry, options, OPTION_TRANSFER_STREAMS),
994
1032
  deflate64,
995
1033
  format: registeredCodec ? registeredCodec.format : UNDEFINED_VALUE,
996
1034
  codecURI: registeredCodec ? registeredCodec.codecURI : UNDEFINED_VALUE,
@@ -1084,6 +1122,30 @@ function getWrappedFilesLength(directoryView, directoryArray, offset) {
1084
1122
  return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
1085
1123
  }
1086
1124
 
1125
+ function pointsAtDirectory(reader, offset) {
1126
+ return startsWithSignature(reader, offset, CENTRAL_FILE_HEADER_SIGNATURE);
1127
+ }
1128
+
1129
+ async function startsWithSignature(reader, offset, signature) {
1130
+ if (offset < 0 || offset + 4 > reader.size) {
1131
+ return false;
1132
+ }
1133
+ const signatureArray = await readUint8Array(reader, offset, 4);
1134
+ return getUint32(getDataView(signatureArray), 0) == signature;
1135
+ }
1136
+
1137
+ async function findDigitalSignatureRecordOffset(reader, endOffset) {
1138
+ const startOffset = Math.max(0, endOffset - (6 + MAX_16_BITS));
1139
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
1140
+ const tailView = getDataView(tailArray);
1141
+ for (let indexByte = tailArray.length - 6; indexByte >= 0; indexByte--) {
1142
+ if (getUint32(tailView, indexByte) == DIGITAL_SIGNATURE_RECORD_SIGNATURE &&
1143
+ indexByte + 6 + getUint16(tailView, indexByte + 4) == tailArray.length) {
1144
+ return startOffset + indexByte;
1145
+ }
1146
+ }
1147
+ }
1148
+
1087
1149
  function readDigitalSignature(signatureRecordArray) {
1088
1150
  if (signatureRecordArray.length >= 6) {
1089
1151
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -1215,29 +1277,27 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
1215
1277
  readExtraFieldUnixDates(extraFieldUnixType1, directory);
1216
1278
  directory.extraFieldUnixType1 = extraFieldUnixType1;
1217
1279
  }
1280
+ const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
1281
+ if (extraFieldExtendedTimestamp) {
1282
+ readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
1283
+ directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
1284
+ }
1218
1285
  const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS);
1219
1286
  if (extraFieldNTFS) {
1220
1287
  readExtraFieldNTFS(extraFieldNTFS, directory);
1221
1288
  directory.extraFieldNTFS = extraFieldNTFS;
1222
1289
  }
1223
- const extraFieldUnix = extraField.get(EXTRAFIELD_TYPE_UNIX);
1290
+ const extraFieldInfoZip = extraField.get(EXTRAFIELD_TYPE_INFOZIP);
1224
1291
  let unixIdsRead;
1292
+ if (extraFieldInfoZip) {
1293
+ unixIdsRead = readExtraFieldUnix(extraFieldInfoZip, directory, true);
1294
+ directory.extraFieldInfoZip = extraFieldInfoZip;
1295
+ }
1296
+ const extraFieldUnix = extraField.get(EXTRAFIELD_TYPE_UNIX);
1225
1297
  if (extraFieldUnix) {
1226
- unixIdsRead = readExtraFieldUnix(extraFieldUnix, directory, false);
1298
+ readExtraFieldUnix(extraFieldUnix, unixIdsRead ? {} : directory, false);
1227
1299
  directory.extraFieldUnix = extraFieldUnix;
1228
1300
  }
1229
- if (!unixIdsRead) {
1230
- const extraFieldInfoZip = extraField.get(EXTRAFIELD_TYPE_INFOZIP);
1231
- if (extraFieldInfoZip) {
1232
- readExtraFieldUnix(extraFieldInfoZip, directory, true);
1233
- directory.extraFieldInfoZip = extraFieldInfoZip;
1234
- }
1235
- }
1236
- const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
1237
- if (extraFieldExtendedTimestamp) {
1238
- readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
1239
- directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
1240
- }
1241
1301
  const extraFieldUSDZ = extraField.get(EXTRAFIELD_TYPE_USDZ);
1242
1302
  if (extraFieldUSDZ) {
1243
1303
  directory.extraFieldUSDZ = extraFieldUSDZ;
@@ -218,7 +218,6 @@ import {
218
218
  OPTION_SIGNAL,
219
219
  OPTION_USE_WEB_WORKERS,
220
220
  OPTION_USE_COMPRESSION_STREAM,
221
- OPTION_TRANSFER_STREAMS,
222
221
  OPTION_PREVENT_CLOSE,
223
222
  OPTION_ENCRYPTION_STRENGTH,
224
223
  OPTION_EXTENDED_TIMESTAMP,
@@ -343,13 +342,14 @@ class ZipWriter {
343
342
  return watchPromiseError(this, prependZipEntries(this, reader));
344
343
  }
345
344
 
346
- appendZip(reader) {
347
- return watchPromiseError(this, this.appendZipEntries(reader));
345
+ appendZip(reader, options = {}) {
346
+ return watchPromiseError(this, this.appendZipEntries(reader, options));
348
347
  }
349
348
 
350
- async appendZipEntries(reader) {
349
+ async appendZipEntries(reader, options = {}) {
351
350
  const zipWriter = this;
352
351
  const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
352
+ const filter = checkFunctionOption(options.filter);
353
353
  while (pendingAddFileCalls.size) {
354
354
  await Promise.allSettled(Array.from(pendingAddFileCalls));
355
355
  }
@@ -368,10 +368,16 @@ class ZipWriter {
368
368
  const { ZipReader } = await import("./zip-reader.js");
369
369
  const zipReader = new ZipReader(reader);
370
370
  const entries = await zipReader.getEntries();
371
+ const keptEntries = [];
372
+ for (const entry of entries) {
373
+ if (!filter || await filter(entry)) {
374
+ keptEntries.push(entry);
375
+ }
376
+ }
371
377
  await zipReader.close();
372
378
  await initStream(zipWriter.writer);
373
379
  const { directoryOffset } = zipReader;
374
- entries.forEach(({ filename }) => {
380
+ keptEntries.forEach(({ filename }) => {
375
381
  if (filenames.has(filename)) {
376
382
  throw new Error(ERR_DUPLICATED_NAME);
377
383
  }
@@ -392,8 +398,8 @@ class ZipWriter {
392
398
  zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
393
399
  }
394
400
  }
395
- const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
396
- entries.forEach(entry => {
401
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
402
+ keptEntries.forEach(entry => {
397
403
  const {
398
404
  version,
399
405
  rawLastModDate,
@@ -449,6 +455,7 @@ class ZipWriter {
449
455
  rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
450
456
  rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
451
457
  rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
458
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
452
459
  rawExtraField,
453
460
  rawCentralExtraField: EMPTY_UINT8_ARRAY,
454
461
  headerArray,
@@ -1063,7 +1070,6 @@ function resolveMetadata(zipWriter, name, options) {
1063
1070
  const ntfsTimestamp = getOptionValue(zipWriter, options, OPTION_NTFS_TIMESTAMP);
1064
1071
  const keepOrder = getOptionValue(zipWriter, options, OPTION_KEEP_ORDER, true);
1065
1072
  const useWebWorkers = getOptionValue(zipWriter, options, OPTION_USE_WEB_WORKERS);
1066
- const transferStreams = getOptionValue(zipWriter, options, OPTION_TRANSFER_STREAMS);
1067
1073
  const bufferedWrite = getOptionValue(zipWriter, options, OPTION_BUFFERED_WRITE);
1068
1074
  const createTempStream = getFunctionOptionValue(zipWriter, options, OPTION_CREATE_TEMP_STREAM);
1069
1075
  const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, true);
@@ -1129,7 +1135,6 @@ function resolveMetadata(zipWriter, name, options) {
1129
1135
  ntfsTimestamp,
1130
1136
  keepOrder,
1131
1137
  useWebWorkers,
1132
- transferStreams,
1133
1138
  bufferedWrite,
1134
1139
  createTempStream,
1135
1140
  dataDescriptorSignature,
@@ -1188,7 +1193,7 @@ async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, op
1188
1193
  if (reader) {
1189
1194
  reader = new GenericReader(reader);
1190
1195
  await initStream(reader);
1191
- if (!reader.readable && !reader.readUint8Array) {
1196
+ if (!reader.readUint8Array && !reader.readable) {
1192
1197
  throw new Error(ERR_INVALID_READER);
1193
1198
  }
1194
1199
  ({ size: contentSize } = reader);
@@ -1549,6 +1554,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1549
1554
  extraFieldExtendedTimestampTime,
1550
1555
  rawExtraFieldNTFS,
1551
1556
  rawExtraFieldUnix,
1557
+ rawCentralExtraFieldUnix,
1552
1558
  rawExtraFieldAES,
1553
1559
  } = headerInfo;
1554
1560
  const { dataDescriptorArray } = dataDescriptorInfo;
@@ -1573,7 +1579,6 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1573
1579
  rawExtraField,
1574
1580
  rawCentralExtraField,
1575
1581
  useWebWorkers,
1576
- transferStreams,
1577
1582
  onstart,
1578
1583
  onprogress,
1579
1584
  onend,
@@ -1615,6 +1620,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1615
1620
  rawExtraFieldExtendedTimestamp,
1616
1621
  rawExtraFieldNTFS,
1617
1622
  rawExtraFieldUnix,
1623
+ rawCentralExtraFieldUnix,
1618
1624
  rawExtraFieldAES,
1619
1625
  rawExtraField,
1620
1626
  rawCentralExtraField,
@@ -1661,7 +1667,6 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1661
1667
  encrypted: encrypted && !passThroughEncryption,
1662
1668
  useWebWorkers,
1663
1669
  useCompressionStream,
1664
- transferStreams,
1665
1670
  format,
1666
1671
  codecURI,
1667
1672
  compressionMethod
@@ -1839,6 +1844,7 @@ function getHeaderInfo(options) {
1839
1844
  rawExtraFieldNTFS = rawExtraFieldExtendedTimestamp = EMPTY_UINT8_ARRAY;
1840
1845
  }
1841
1846
  let rawExtraFieldUnix;
1847
+ let rawCentralExtraFieldUnix;
1842
1848
  try {
1843
1849
  const { uid, gid, unixExtraFieldType } = options;
1844
1850
  if (unixExtraFieldType == INFOZIP_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
@@ -1853,7 +1859,7 @@ function getHeaderInfo(options) {
1853
1859
  extraFieldUnix.writeBytes(uidBytes);
1854
1860
  extraFieldUnix.writeUint8(gidBytes.length);
1855
1861
  extraFieldUnix.writeBytes(gidBytes);
1856
- rawExtraFieldUnix = extraFieldUnix.array;
1862
+ rawExtraFieldUnix = rawCentralExtraFieldUnix = extraFieldUnix.array;
1857
1863
  } else if (unixExtraFieldType == UNIX_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
1858
1864
  const extraFieldUnix = createRecordWriter(8);
1859
1865
  extraFieldUnix.writeUint16(EXTRAFIELD_TYPE_UNIX);
@@ -1861,11 +1867,15 @@ function getHeaderInfo(options) {
1861
1867
  extraFieldUnix.writeUint16((uid === UNDEFINED_VALUE ? 0 : uid) & MAX_16_BITS);
1862
1868
  extraFieldUnix.writeUint16((gid === UNDEFINED_VALUE ? 0 : gid) & MAX_16_BITS);
1863
1869
  rawExtraFieldUnix = extraFieldUnix.array;
1870
+ const centralExtraFieldUnix = createRecordWriter(4);
1871
+ centralExtraFieldUnix.writeUint16(EXTRAFIELD_TYPE_UNIX);
1872
+ centralExtraFieldUnix.writeUint16(0);
1873
+ rawCentralExtraFieldUnix = centralExtraFieldUnix.array;
1864
1874
  } else {
1865
- rawExtraFieldUnix = EMPTY_UINT8_ARRAY;
1875
+ rawExtraFieldUnix = rawCentralExtraFieldUnix = EMPTY_UINT8_ARRAY;
1866
1876
  }
1867
1877
  } catch {
1868
- rawExtraFieldUnix = EMPTY_UINT8_ARRAY;
1878
+ rawExtraFieldUnix = rawCentralExtraFieldUnix = EMPTY_UINT8_ARRAY;
1869
1879
  }
1870
1880
  if (compressionMethod === UNDEFINED_VALUE) {
1871
1881
  compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
@@ -1957,6 +1967,7 @@ function getHeaderInfo(options) {
1957
1967
  rawExtraFieldExtendedTimestamp,
1958
1968
  rawExtraFieldNTFS,
1959
1969
  rawExtraFieldUnix,
1970
+ rawCentralExtraFieldUnix,
1960
1971
  rawExtraFieldAES,
1961
1972
  extraFieldLength
1962
1973
  };
@@ -2132,7 +2143,7 @@ function createDirectoryRecords(files) {
2132
2143
  rawExtraFieldAES,
2133
2144
  rawComment,
2134
2145
  rawExtraFieldNTFS,
2135
- rawExtraFieldUnix,
2146
+ rawCentralExtraFieldUnix,
2136
2147
  rawExtraField,
2137
2148
  rawCentralExtraField,
2138
2149
  extraFieldExtendedTimestampFlag,
@@ -2194,7 +2205,7 @@ function createDirectoryRecords(files) {
2194
2205
  rawExtraFieldZip64,
2195
2206
  rawExtraFieldAES,
2196
2207
  rawExtraFieldNTFS,
2197
- rawExtraFieldUnix,
2208
+ rawCentralExtraFieldUnix,
2198
2209
  rawExtraFieldTimestamp,
2199
2210
  rawExtraField,
2200
2211
  rawCentralExtraField);
@@ -2223,7 +2234,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
2223
2234
  rawExtraFieldAES,
2224
2235
  rawExtraFieldExtendedTimestamp,
2225
2236
  rawExtraFieldNTFS,
2226
- rawExtraFieldUnix,
2237
+ rawCentralExtraFieldUnix,
2227
2238
  rawExtraField,
2228
2239
  rawCentralExtraField,
2229
2240
  rawComment,
@@ -2240,7 +2251,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
2240
2251
  uncompressedSize,
2241
2252
  compressedSize
2242
2253
  } = fileEntry;
2243
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField, rawCentralExtraField);
2254
+ const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawCentralExtraFieldUnix, rawExtraField, rawCentralExtraField);
2244
2255
  const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
2245
2256
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
2246
2257
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
@@ -2276,7 +2287,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
2276
2287
  directoryRecord.writeBytes(rawExtraFieldAES);
2277
2288
  directoryRecord.writeBytes(rawExtraFieldExtendedTimestamp);
2278
2289
  directoryRecord.writeBytes(rawExtraFieldNTFS);
2279
- directoryRecord.writeBytes(rawExtraFieldUnix);
2290
+ directoryRecord.writeBytes(rawCentralExtraFieldUnix);
2280
2291
  directoryRecord.writeBytes(rawExtraField);
2281
2292
  directoryRecord.writeBytes(rawCentralExtraField);
2282
2293
  directoryRecord.writeBytes(rawComment);
@@ -2445,26 +2456,42 @@ function removeExtraFieldZip64(rawExtraField) {
2445
2456
  return rawExtraField;
2446
2457
  }
2447
2458
 
2448
- async function copyZipData(zipWriter, reader, entries, directoryOffset) {
2459
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
2449
2460
  const { writer } = zipWriter;
2450
2461
  const entryPositions = new Map();
2451
- if (writer.closeDisk) {
2462
+ if (writer.closeDisk || compact) {
2452
2463
  const sortedEntries = Array.from(entries).sort((firstEntry, secondEntry) =>
2453
2464
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
2454
- let copiedLength = 0;
2455
- for (const entry of sortedEntries) {
2456
- const sourceOffset = getSourceOffset(reader, entry);
2457
- await copyData(zipWriter, reader, copiedLength, sourceOffset - copiedLength);
2458
- if (exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
2459
- await writer.closeDisk();
2465
+ const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
2466
+ const keptEntrySet = new Set(keptEntries);
2467
+ let copiedOffset = 0;
2468
+ let lastPosition;
2469
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
2470
+ const entry = sortedEntries[indexEntry];
2471
+ if (keptEntrySet.has(entry)) {
2472
+ const sourceOffset = getSourceOffset(reader, entry);
2473
+ if (sourceOffset < copiedOffset) {
2474
+ entryPositions.set(entry, lastPosition);
2475
+ } else {
2476
+ if (!compact) {
2477
+ await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
2478
+ }
2479
+ if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
2480
+ await writer.closeDisk();
2481
+ }
2482
+ lastPosition = {
2483
+ offset: getSegmentOffset(zipWriter, writer),
2484
+ diskNumberStart: getDiskNumber(writer)
2485
+ };
2486
+ entryPositions.set(entry, lastPosition);
2487
+ await copyData(zipWriter, reader, sourceOffset, regionEnds[indexEntry] - sourceOffset);
2488
+ copiedOffset = regionEnds[indexEntry];
2489
+ }
2460
2490
  }
2461
- entryPositions.set(entry, {
2462
- offset: getSegmentOffset(zipWriter, writer),
2463
- diskNumberStart: getDiskNumber(writer)
2464
- });
2465
- copiedLength = sourceOffset;
2466
2491
  }
2467
- await copyData(zipWriter, reader, copiedLength, directoryOffset - copiedLength);
2492
+ if (!compact) {
2493
+ await copyData(zipWriter, reader, copiedOffset, directoryOffset - copiedOffset);
2494
+ }
2468
2495
  } else {
2469
2496
  const baseOffset = zipWriter.offset;
2470
2497
  await copyData(zipWriter, reader, 0, directoryOffset);
@@ -2476,6 +2503,20 @@ async function copyZipData(zipWriter, reader, entries, directoryOffset) {
2476
2503
  return entryPositions;
2477
2504
  }
2478
2505
 
2506
+ function getRegionEnds(reader, sortedEntries, directoryOffset) {
2507
+ const regionEnds = new Array(sortedEntries.length);
2508
+ let regionEnd = directoryOffset;
2509
+ for (let indexEntry = sortedEntries.length - 1; indexEntry >= 0; indexEntry--) {
2510
+ const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
2511
+ const nextEntry = sortedEntries[indexEntry + 1];
2512
+ if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
2513
+ regionEnd = getSourceOffset(reader, nextEntry);
2514
+ }
2515
+ regionEnds[indexEntry] = regionEnd;
2516
+ }
2517
+ return regionEnds;
2518
+ }
2519
+
2479
2520
  async function copyData(zipWriter, reader, offset, size) {
2480
2521
  if (size > 0) {
2481
2522
  const { writer } = zipWriter;
@@ -68,6 +68,8 @@ export {
68
68
  WARNING_PREPENDED_DATA,
69
69
  WARNING_PREPENDED_CENTRAL_DIRECTORY,
70
70
  WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
71
+ WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET,
72
+ WARNING_MISSING_ZIP64_EXTRA_FIELD,
71
73
  WARNING_DUPLICATE_FILENAME,
72
74
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
73
75
  WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
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.18.2",
6
+ "version": "2.20.0",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "zip",
@@ -1,3 +1,3 @@
1
- const WORKER_MESSAGE_PROPERTY_NAMES = ["baseURI", "cause", "checkAuthenticationCode", "checkCrc32", "checkPasswordOnly", "chunkSize", "code", "codecImportFailed", "codecType", "codecURI", "compressed", "compressionMethod", "computeCrc32", "config", "crc32", "deflate64", "done", "encrypted", "encryptionStrength", "error", "errorValue", "format", "inputSize", "level", "message", "messageId", "name", "options", "outputSize", "password", "passwordVerification", "rawBitFlag", "rawPassword", "readable", "result", "stack", "transferStreams", "type", "useCompressionStream", "useWebWorkers", "value", "wasmURI", "workerStartupTimeout", "writable", "zipCrypto"];
1
+ const WORKER_MESSAGE_PROPERTY_NAMES = ["baseURI", "cause", "checkAuthenticationCode", "checkCrc32", "checkPasswordOnly", "chunkSize", "code", "codecImportFailed", "codecType", "codecURI", "compressed", "compressionMethod", "computeCrc32", "config", "crc32", "deflate64", "done", "encrypted", "encryptionStrength", "error", "errorValue", "format", "inputSize", "level", "message", "messageId", "name", "options", "outputSize", "password", "passwordVerification", "rawBitFlag", "rawPassword", "result", "stack", "type", "useCompressionStream", "useWebWorkers", "value", "wasmURI", "workerStartupTimeout", "zipCrypto"];
2
2
 
3
3
  export { WORKER_MESSAGE_PROPERTY_NAMES };