@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
package/dist/zip-core.js CHANGED
@@ -185,7 +185,6 @@
185
185
  const OPTION_CHECK_AUTHENTICATION_CODE = "checkAuthenticationCode";
186
186
  const OPTION_USE_WEB_WORKERS = "useWebWorkers";
187
187
  const OPTION_USE_COMPRESSION_STREAM = "useCompressionStream";
188
- const OPTION_TRANSFER_STREAMS = "transferStreams";
189
188
  const OPTION_PREVENT_CLOSE = "preventClose";
190
189
  const OPTION_ENCRYPTION_STRENGTH = "encryptionStrength";
191
190
  const OPTION_EXTENDED_TIMESTAMP = "extendedTimestamp";
@@ -303,7 +302,7 @@
303
302
  */
304
303
 
305
304
 
306
- const DEFAULT_CHUNK_SIZE$1 = 64 * 1024;
305
+ const DEFAULT_CHUNK_SIZE$1 = 256 * 1024;
307
306
  const MINIMUM_CHUNK_SIZE = 64;
308
307
  const MINIMUM_PROPERTY_VALUE = 1;
309
308
  const ERR_INVALID_MAX_WORKERS = "Invalid maxWorkers (must be an integer greater than 0)";
@@ -2392,7 +2391,7 @@
2392
2391
  */
2393
2392
 
2394
2393
 
2395
- const DEFAULT_CHUNK_SIZE = 64 * 1024;
2394
+ const DEFAULT_CHUNK_SIZE = 256 * 1024;
2396
2395
  const MESSAGE_EVENT_TYPE = "message";
2397
2396
  const MESSAGE_START = "start";
2398
2397
  const MESSAGE_PULL = "pull";
@@ -2598,7 +2597,7 @@
2598
2597
  class CodecWorker {
2599
2598
 
2600
2599
  constructor(workerData, { readable, writable }, workerOptions, onTaskFinished) {
2601
- const { options, config, streamOptions, useWebWorkers, transferStreams, workerURI } = workerOptions;
2600
+ const { options, config, streamOptions, useWebWorkers, workerURI } = workerOptions;
2602
2601
  let { createWorker } = workerOptions;
2603
2602
  const { signal } = streamOptions;
2604
2603
  if (createWorkerFailed) {
@@ -2615,7 +2614,6 @@
2615
2614
  workerOptions,
2616
2615
  workerURI,
2617
2616
  createWorker,
2618
- transferStreams,
2619
2617
  terminate() {
2620
2618
  return new Promise(resolve => {
2621
2619
  const { worker, busy } = workerData;
@@ -2793,12 +2791,6 @@
2793
2791
  const ABORT_EVENT_TYPE = "abort";
2794
2792
 
2795
2793
  let webWorkerSource, webWorkerURI, webWorkerOptions;
2796
- let transferStreamsSupported = true;
2797
- try {
2798
- transferStreamsSupported = typeof structuredClone == FUNCTION_TYPE && structuredClone(new DOMException("", "AbortError")).code !== UNDEFINED_VALUE;
2799
- } catch {
2800
- // ignored
2801
- }
2802
2794
 
2803
2795
  setWebWorkerBackend(createWebWorkerInterface);
2804
2796
 
@@ -2885,14 +2877,11 @@
2885
2877
  });
2886
2878
  const { readable, options } = workerData;
2887
2879
  const { writable, closed, abortPipe } = watchClosedStream(workerData.writable, workerData);
2888
- let streamsTransferred;
2889
2880
  try {
2890
- streamsTransferred = sendMessage({
2881
+ sendMessage({
2891
2882
  type: MESSAGE_START,
2892
2883
  options,
2893
- config,
2894
- readable,
2895
- writable
2884
+ config
2896
2885
  }, workerData);
2897
2886
  } catch (error) {
2898
2887
  abortPipe();
@@ -2904,12 +2893,10 @@
2904
2893
  workerData.onTaskFinished();
2905
2894
  throw error;
2906
2895
  }
2907
- if (!streamsTransferred) {
2908
- Object.assign(workerData, {
2909
- reader: readable.getReader(),
2910
- writer: writable.getWriter()
2911
- });
2912
- }
2896
+ Object.assign(workerData, {
2897
+ reader: readable.getReader(),
2898
+ writer: writable.getWriter()
2899
+ });
2913
2900
  const { workerStartupTimeout } = config;
2914
2901
  if (!workerData.workerAlive && Number.isFinite(workerStartupTimeout) && workerStartupTimeout >= 0) {
2915
2902
  workerData.startupTimeout = setTimeout(() => onStartupTimeout(workerData), workerStartupTimeout);
@@ -2942,7 +2929,7 @@
2942
2929
  }
2943
2930
 
2944
2931
  async function closeWritable() {
2945
- if (!streamsTransferred && !writable.locked) {
2932
+ if (!writable.locked) {
2946
2933
  try {
2947
2934
  await writable.getWriter().close();
2948
2935
  } catch {
@@ -3133,31 +3120,14 @@
3133
3120
  }
3134
3121
  }
3135
3122
 
3136
- function sendMessage(message, { worker, writer, transferStreams, workerAlive }) {
3123
+ function sendMessage(message, { worker, writer }) {
3137
3124
  try {
3138
- const { value, readable, writable } = message;
3139
- const transferables = [];
3125
+ const { value } = message;
3140
3126
  if (value) {
3141
3127
  message.value = toExactUint8Array(value);
3142
- transferables.push(message.value.buffer);
3143
- }
3144
- if (transferStreams && transferStreamsSupported && workerAlive) {
3145
- if (readable) {
3146
- transferables.push(readable);
3147
- }
3148
- if (writable) {
3149
- transferables.push(writable);
3150
- }
3151
- } else {
3152
- message.readable = message.writable = null;
3153
- }
3154
- if (transferables.length) {
3155
3128
  try {
3156
- worker.postMessage(message, transferables);
3157
- return true;
3129
+ worker.postMessage(message, [message.value.buffer]);
3158
3130
  } catch {
3159
- transferStreamsSupported = false;
3160
- message.readable = message.writable = null;
3161
3131
  worker.postMessage(message);
3162
3132
  }
3163
3133
  } else {
@@ -3306,7 +3276,7 @@
3306
3276
 
3307
3277
  async function runWorker(stream, workerOptions) {
3308
3278
  const { options, config } = workerOptions;
3309
- const { transferStreams, useWebWorkers, useCompressionStream, compressed, checkCrc32, computeCrc32, encrypted, format, codecURI } = options;
3279
+ const { useWebWorkers, useCompressionStream, compressed, checkCrc32, computeCrc32, encrypted, format, codecURI } = options;
3310
3280
  const { workerURI, createWorker, maxWorkers } = config;
3311
3281
  if (format) {
3312
3282
  if (codecURI) {
@@ -3314,7 +3284,6 @@
3314
3284
  }
3315
3285
  await ensureCodecStreams(format, options.codecURI);
3316
3286
  }
3317
- workerOptions.transferStreams = !format && (transferStreams || (transferStreams === UNDEFINED_VALUE && config.transferStreams));
3318
3287
  const streamCopy = !compressed && !checkCrc32 && !computeCrc32 && !encrypted;
3319
3288
  const workerSupported = format === UNDEFINED_VALUE || Boolean(options.codecURI);
3320
3289
  workerOptions.useWebWorkers = !streamCopy && workerSupported && (useWebWorkers || (useWebWorkers === UNDEFINED_VALUE && config.useWebWorkers));
@@ -4810,6 +4779,8 @@
4810
4779
  const WARNING_PREPENDED_DATA = "prepended data";
4811
4780
  const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
4812
4781
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
4782
+ const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = "mismatched central directory offset";
4783
+ const WARNING_MISSING_ZIP64_EXTRA_FIELD = "missing zip64 extra field";
4813
4784
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
4814
4785
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
4815
4786
  const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
@@ -4972,6 +4943,8 @@
4972
4943
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
4973
4944
  }
4974
4945
  directoryDataOffset = getDiskOffset$1(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
4946
+ } else if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || diskNumber == MAX_16_BITS) {
4947
+ throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
4975
4948
  }
4976
4949
  }
4977
4950
  let declaredDirectoryDataLength = directoryDataLength;
@@ -4994,25 +4967,44 @@
4994
4967
  if (directoryArray.length < 4) {
4995
4968
  throw new Error(ERR_BAD_FORMAT);
4996
4969
  }
4997
- const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4970
+ let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4998
4971
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4999
4972
  const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
5000
4973
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
5001
4974
  detectEncryptedCentralDirectory(directoryView);
5002
4975
  let reconcile = !storedPointsAtDirectory;
5003
- if (!reconcile && expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
5004
- const expectedSignatureArray = await readUint8Array(reader, expectedDirectoryDataOffset, 4);
5005
- reconcile = getUint32$1(getDataView(expectedSignatureArray), 0) == CENTRAL_FILE_HEADER_SIGNATURE;
4976
+ if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4977
+ const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
4978
+ if (storedPointsAtDirectory) {
4979
+ reconcile = expectedPointsAtDirectory;
4980
+ } else if (!expectedPointsAtDirectory) {
4981
+ const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
4982
+ const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
4983
+ if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
4984
+ await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
4985
+ expectedDirectoryDataOffset = signedDirectoryDataOffset;
4986
+ }
4987
+ }
5006
4988
  }
5007
4989
  if (reconcile) {
5008
4990
  const originalDirectoryDataOffset = directoryDataOffset;
5009
4991
  directoryDataOffset = expectedDirectoryDataOffset;
4992
+ directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4993
+ directoryView = getDataView(directoryArray);
5010
4994
  if (directoryDataOffset > originalDirectoryDataOffset) {
5011
4995
  prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5012
4996
  prependedCentralDirectory = storedPointsAtDirectory;
4997
+ } else {
4998
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
4999
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5000
+ if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
5001
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5002
+ if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
5003
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
5004
+ prependedDataLength += offsetDelta;
5005
+ }
5006
+ }
5013
5007
  }
5014
- directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5015
- directoryView = getDataView(directoryArray);
5016
5008
  }
5017
5009
  }
5018
5010
  }
@@ -5096,17 +5088,6 @@
5096
5088
  if (filename === UNDEFINED_VALUE) {
5097
5089
  filename = decodeText(rawFilename, rawFilenameEncoding);
5098
5090
  }
5099
- if (normalizeFilename) {
5100
- const normalizedFilename = normalizeFilename(filename);
5101
- if (normalizedFilename !== UNDEFINED_VALUE) {
5102
- filename = normalizedFilename;
5103
- }
5104
- }
5105
- if (isUnsafeFilename(filename, filenameValidation)) {
5106
- const error = new Error(ERR_UNSAFE_FILENAME);
5107
- error.filename = filename;
5108
- throw error;
5109
- }
5110
5091
  let comment = decode(rawComment, rawCommentEncoding, TEXT_TYPE_COMMENT);
5111
5092
  if (comment === UNDEFINED_VALUE) {
5112
5093
  comment = decodeText(rawComment, rawCommentEncoding);
@@ -5134,7 +5115,29 @@
5134
5115
  filename,
5135
5116
  comment
5136
5117
  });
5137
- if (readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6)) {
5118
+ let malformedExtraField;
5119
+ try {
5120
+ malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
5121
+ } catch (error) {
5122
+ if (checkAmbiguity || error.message != ERR_EXTRAFIELD_ZIP64_NOT_FOUND) {
5123
+ throw error;
5124
+ }
5125
+ fileEntry.zip64ExtraFieldMissing = true;
5126
+ addWarning(warnings, WARNING_MISSING_ZIP64_EXTRA_FIELD, fileEntry.filename);
5127
+ }
5128
+ filename = fileEntry.filename;
5129
+ if (normalizeFilename) {
5130
+ const normalizedFilename = normalizeFilename(filename);
5131
+ if (normalizedFilename !== UNDEFINED_VALUE) {
5132
+ filename = fileEntry.filename = normalizedFilename;
5133
+ }
5134
+ }
5135
+ if (isUnsafeFilename(filename, filenameValidation)) {
5136
+ const error = new Error(ERR_UNSAFE_FILENAME);
5137
+ error.filename = filename;
5138
+ throw error;
5139
+ }
5140
+ if (malformedExtraField) {
5138
5141
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5139
5142
  }
5140
5143
  fileEntry.offset += prependedDataLength;
@@ -5163,11 +5166,12 @@
5163
5166
  const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
5164
5167
  const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
5165
5168
  const symlink = (unixType & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK;
5166
- const executable = !symlink && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5167
- ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5168
- : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5169
5169
  const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5170
5170
  const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
5171
+ const directory = modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE);
5172
+ const executable = !symlink && !directory && ((fileEntry.unixMode !== UNDEFINED_VALUE)
5173
+ ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
5174
+ : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
5171
5175
  Object.assign(fileEntry, {
5172
5176
  setuid,
5173
5177
  setgid,
@@ -5175,7 +5179,7 @@
5175
5179
  symlink,
5176
5180
  unixExternalUpper,
5177
5181
  executable,
5178
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
5182
+ directory,
5179
5183
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5180
5184
  });
5181
5185
  const entry = new Entry(fileEntry);
@@ -5231,10 +5235,10 @@
5231
5235
  const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
5232
5236
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
5233
5237
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
5234
- if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
5238
+ if (checkAmbiguity && (prependedDataLength > 0 || (filesLength && startOffset > splitZipSignatureLength))) {
5235
5239
  throwAmbiguousArchive(WARNING_PREPENDED_DATA);
5236
5240
  }
5237
- if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5241
+ if (prependedDataLength > 0 || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
5238
5242
  addWarning(warnings, WARNING_PREPENDED_DATA);
5239
5243
  }
5240
5244
  if (prependedCentralDirectory) {
@@ -5394,6 +5398,9 @@
5394
5398
 
5395
5399
  async getData(writer, fileEntry, readRanges, options = {}) {
5396
5400
  const zipEntry = this;
5401
+ if (zipEntry.zip64ExtraFieldMissing) {
5402
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5403
+ }
5397
5404
  const config = getConfiguration();
5398
5405
  const {
5399
5406
  reader,
@@ -5538,7 +5545,6 @@
5538
5545
  encrypted,
5539
5546
  useWebWorkers: getOptionValue$1(zipEntry, options, OPTION_USE_WEB_WORKERS),
5540
5547
  useCompressionStream,
5541
- transferStreams: getOptionValue$1(zipEntry, options, OPTION_TRANSFER_STREAMS),
5542
5548
  deflate64,
5543
5549
  format: registeredCodec ? registeredCodec.format : UNDEFINED_VALUE,
5544
5550
  codecURI: registeredCodec ? registeredCodec.codecURI : UNDEFINED_VALUE,
@@ -5632,6 +5638,30 @@
5632
5638
  return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
5633
5639
  }
5634
5640
 
5641
+ function pointsAtDirectory(reader, offset) {
5642
+ return startsWithSignature(reader, offset, CENTRAL_FILE_HEADER_SIGNATURE);
5643
+ }
5644
+
5645
+ async function startsWithSignature(reader, offset, signature) {
5646
+ if (offset < 0 || offset + 4 > reader.size) {
5647
+ return false;
5648
+ }
5649
+ const signatureArray = await readUint8Array(reader, offset, 4);
5650
+ return getUint32$1(getDataView(signatureArray), 0) == signature;
5651
+ }
5652
+
5653
+ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5654
+ const startOffset = Math.max(0, endOffset - (6 + MAX_16_BITS));
5655
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5656
+ const tailView = getDataView(tailArray);
5657
+ for (let indexByte = tailArray.length - 6; indexByte >= 0; indexByte--) {
5658
+ if (getUint32$1(tailView, indexByte) == DIGITAL_SIGNATURE_RECORD_SIGNATURE &&
5659
+ indexByte + 6 + getUint16$1(tailView, indexByte + 4) == tailArray.length) {
5660
+ return startOffset + indexByte;
5661
+ }
5662
+ }
5663
+ }
5664
+
5635
5665
  function readDigitalSignature(signatureRecordArray) {
5636
5666
  if (signatureRecordArray.length >= 6) {
5637
5667
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5763,29 +5793,27 @@
5763
5793
  readExtraFieldUnixDates(extraFieldUnixType1, directory);
5764
5794
  directory.extraFieldUnixType1 = extraFieldUnixType1;
5765
5795
  }
5796
+ const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5797
+ if (extraFieldExtendedTimestamp) {
5798
+ readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5799
+ directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5800
+ }
5766
5801
  const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS);
5767
5802
  if (extraFieldNTFS) {
5768
5803
  readExtraFieldNTFS(extraFieldNTFS, directory);
5769
5804
  directory.extraFieldNTFS = extraFieldNTFS;
5770
5805
  }
5771
- const extraFieldUnix = extraField.get(EXTRAFIELD_TYPE_UNIX);
5806
+ const extraFieldInfoZip = extraField.get(EXTRAFIELD_TYPE_INFOZIP);
5772
5807
  let unixIdsRead;
5808
+ if (extraFieldInfoZip) {
5809
+ unixIdsRead = readExtraFieldUnix(extraFieldInfoZip, directory, true);
5810
+ directory.extraFieldInfoZip = extraFieldInfoZip;
5811
+ }
5812
+ const extraFieldUnix = extraField.get(EXTRAFIELD_TYPE_UNIX);
5773
5813
  if (extraFieldUnix) {
5774
- unixIdsRead = readExtraFieldUnix(extraFieldUnix, directory, false);
5814
+ readExtraFieldUnix(extraFieldUnix, unixIdsRead ? {} : directory, false);
5775
5815
  directory.extraFieldUnix = extraFieldUnix;
5776
5816
  }
5777
- if (!unixIdsRead) {
5778
- const extraFieldInfoZip = extraField.get(EXTRAFIELD_TYPE_INFOZIP);
5779
- if (extraFieldInfoZip) {
5780
- readExtraFieldUnix(extraFieldInfoZip, directory, true);
5781
- directory.extraFieldInfoZip = extraFieldInfoZip;
5782
- }
5783
- }
5784
- const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
5785
- if (extraFieldExtendedTimestamp) {
5786
- readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
5787
- directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
5788
- }
5789
5817
  const extraFieldUSDZ = extraField.get(EXTRAFIELD_TYPE_USDZ);
5790
5818
  if (extraFieldUSDZ) {
5791
5819
  directory.extraFieldUSDZ = extraFieldUSDZ;
@@ -6424,11 +6452,13 @@
6424
6452
  WARNING_COMPRESSED_PATCHED_DATA: WARNING_COMPRESSED_PATCHED_DATA,
6425
6453
  WARNING_DUPLICATE_FILENAME: WARNING_DUPLICATE_FILENAME,
6426
6454
  WARNING_MALFORMED_EXTRA_FIELD: WARNING_MALFORMED_EXTRA_FIELD,
6455
+ WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET: WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET,
6427
6456
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG: WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
6428
6457
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD: WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
6429
6458
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES: WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES,
6430
6459
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME: WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
6431
6460
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY: WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
6461
+ WARNING_MISSING_ZIP64_EXTRA_FIELD: WARNING_MISSING_ZIP64_EXTRA_FIELD,
6432
6462
  WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY: WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
6433
6463
  WARNING_PREPENDED_CENTRAL_DIRECTORY: WARNING_PREPENDED_CENTRAL_DIRECTORY,
6434
6464
  WARNING_PREPENDED_DATA: WARNING_PREPENDED_DATA,
@@ -6561,13 +6591,14 @@
6561
6591
  return watchPromiseError(this, prependZipEntries(this, reader));
6562
6592
  }
6563
6593
 
6564
- appendZip(reader) {
6565
- return watchPromiseError(this, this.appendZipEntries(reader));
6594
+ appendZip(reader, options = {}) {
6595
+ return watchPromiseError(this, this.appendZipEntries(reader, options));
6566
6596
  }
6567
6597
 
6568
- async appendZipEntries(reader) {
6598
+ async appendZipEntries(reader, options = {}) {
6569
6599
  const zipWriter = this;
6570
6600
  const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6601
+ const filter = checkFunctionOption(options.filter);
6571
6602
  while (pendingAddFileCalls.size) {
6572
6603
  await Promise.allSettled(Array.from(pendingAddFileCalls));
6573
6604
  }
@@ -6586,10 +6617,16 @@
6586
6617
  const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6587
6618
  const zipReader$1 = new ZipReader(reader);
6588
6619
  const entries = await zipReader$1.getEntries();
6620
+ const keptEntries = [];
6621
+ for (const entry of entries) {
6622
+ if (!filter || await filter(entry)) {
6623
+ keptEntries.push(entry);
6624
+ }
6625
+ }
6589
6626
  await zipReader$1.close();
6590
6627
  await initStream(zipWriter.writer);
6591
6628
  const { directoryOffset } = zipReader$1;
6592
- entries.forEach(({ filename }) => {
6629
+ keptEntries.forEach(({ filename }) => {
6593
6630
  if (filenames.has(filename)) {
6594
6631
  throw new Error(ERR_DUPLICATED_NAME);
6595
6632
  }
@@ -6610,8 +6647,8 @@
6610
6647
  zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6611
6648
  }
6612
6649
  }
6613
- const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
6614
- entries.forEach(entry => {
6650
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
6651
+ keptEntries.forEach(entry => {
6615
6652
  const {
6616
6653
  version,
6617
6654
  rawLastModDate,
@@ -6667,6 +6704,7 @@
6667
6704
  rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6668
6705
  rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6669
6706
  rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6707
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6670
6708
  rawExtraField,
6671
6709
  rawCentralExtraField: EMPTY_UINT8_ARRAY,
6672
6710
  headerArray,
@@ -7235,7 +7273,6 @@
7235
7273
  const ntfsTimestamp = getOptionValue(zipWriter, options, OPTION_NTFS_TIMESTAMP);
7236
7274
  const keepOrder = getOptionValue(zipWriter, options, OPTION_KEEP_ORDER, true);
7237
7275
  const useWebWorkers = getOptionValue(zipWriter, options, OPTION_USE_WEB_WORKERS);
7238
- const transferStreams = getOptionValue(zipWriter, options, OPTION_TRANSFER_STREAMS);
7239
7276
  const bufferedWrite = getOptionValue(zipWriter, options, OPTION_BUFFERED_WRITE);
7240
7277
  const createTempStream = getFunctionOptionValue(zipWriter, options, OPTION_CREATE_TEMP_STREAM);
7241
7278
  const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, true);
@@ -7301,7 +7338,6 @@
7301
7338
  ntfsTimestamp,
7302
7339
  keepOrder,
7303
7340
  useWebWorkers,
7304
- transferStreams,
7305
7341
  bufferedWrite,
7306
7342
  createTempStream,
7307
7343
  dataDescriptorSignature,
@@ -7360,7 +7396,7 @@
7360
7396
  if (reader) {
7361
7397
  reader = new GenericReader(reader);
7362
7398
  await initStream(reader);
7363
- if (!reader.readable && !reader.readUint8Array) {
7399
+ if (!reader.readUint8Array && !reader.readable) {
7364
7400
  throw new Error(ERR_INVALID_READER);
7365
7401
  }
7366
7402
  ({ size: contentSize } = reader);
@@ -7623,6 +7659,7 @@
7623
7659
  extraFieldExtendedTimestampTime,
7624
7660
  rawExtraFieldNTFS,
7625
7661
  rawExtraFieldUnix,
7662
+ rawCentralExtraFieldUnix,
7626
7663
  rawExtraFieldAES,
7627
7664
  } = headerInfo;
7628
7665
  const { dataDescriptorArray } = dataDescriptorInfo;
@@ -7647,7 +7684,6 @@
7647
7684
  rawExtraField,
7648
7685
  rawCentralExtraField,
7649
7686
  useWebWorkers,
7650
- transferStreams,
7651
7687
  onstart,
7652
7688
  onprogress,
7653
7689
  onend,
@@ -7689,6 +7725,7 @@
7689
7725
  rawExtraFieldExtendedTimestamp,
7690
7726
  rawExtraFieldNTFS,
7691
7727
  rawExtraFieldUnix,
7728
+ rawCentralExtraFieldUnix,
7692
7729
  rawExtraFieldAES,
7693
7730
  rawExtraField,
7694
7731
  rawCentralExtraField,
@@ -7735,7 +7772,6 @@
7735
7772
  encrypted: encrypted && !passThroughEncryption,
7736
7773
  useWebWorkers,
7737
7774
  useCompressionStream,
7738
- transferStreams,
7739
7775
  format,
7740
7776
  codecURI,
7741
7777
  compressionMethod
@@ -7913,6 +7949,7 @@
7913
7949
  rawExtraFieldNTFS = rawExtraFieldExtendedTimestamp = EMPTY_UINT8_ARRAY;
7914
7950
  }
7915
7951
  let rawExtraFieldUnix;
7952
+ let rawCentralExtraFieldUnix;
7916
7953
  try {
7917
7954
  const { uid, gid, unixExtraFieldType } = options;
7918
7955
  if (unixExtraFieldType == INFOZIP_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
@@ -7927,7 +7964,7 @@
7927
7964
  extraFieldUnix.writeBytes(uidBytes);
7928
7965
  extraFieldUnix.writeUint8(gidBytes.length);
7929
7966
  extraFieldUnix.writeBytes(gidBytes);
7930
- rawExtraFieldUnix = extraFieldUnix.array;
7967
+ rawExtraFieldUnix = rawCentralExtraFieldUnix = extraFieldUnix.array;
7931
7968
  } else if (unixExtraFieldType == UNIX_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
7932
7969
  const extraFieldUnix = createRecordWriter(8);
7933
7970
  extraFieldUnix.writeUint16(EXTRAFIELD_TYPE_UNIX);
@@ -7935,11 +7972,15 @@
7935
7972
  extraFieldUnix.writeUint16((uid === UNDEFINED_VALUE ? 0 : uid) & MAX_16_BITS);
7936
7973
  extraFieldUnix.writeUint16((gid === UNDEFINED_VALUE ? 0 : gid) & MAX_16_BITS);
7937
7974
  rawExtraFieldUnix = extraFieldUnix.array;
7975
+ const centralExtraFieldUnix = createRecordWriter(4);
7976
+ centralExtraFieldUnix.writeUint16(EXTRAFIELD_TYPE_UNIX);
7977
+ centralExtraFieldUnix.writeUint16(0);
7978
+ rawCentralExtraFieldUnix = centralExtraFieldUnix.array;
7938
7979
  } else {
7939
- rawExtraFieldUnix = EMPTY_UINT8_ARRAY;
7980
+ rawExtraFieldUnix = rawCentralExtraFieldUnix = EMPTY_UINT8_ARRAY;
7940
7981
  }
7941
7982
  } catch {
7942
- rawExtraFieldUnix = EMPTY_UINT8_ARRAY;
7983
+ rawExtraFieldUnix = rawCentralExtraFieldUnix = EMPTY_UINT8_ARRAY;
7943
7984
  }
7944
7985
  if (compressionMethod === UNDEFINED_VALUE) {
7945
7986
  compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
@@ -8031,6 +8072,7 @@
8031
8072
  rawExtraFieldExtendedTimestamp,
8032
8073
  rawExtraFieldNTFS,
8033
8074
  rawExtraFieldUnix,
8075
+ rawCentralExtraFieldUnix,
8034
8076
  rawExtraFieldAES,
8035
8077
  extraFieldLength
8036
8078
  };
@@ -8206,7 +8248,7 @@
8206
8248
  rawExtraFieldAES,
8207
8249
  rawComment,
8208
8250
  rawExtraFieldNTFS,
8209
- rawExtraFieldUnix,
8251
+ rawCentralExtraFieldUnix,
8210
8252
  rawExtraField,
8211
8253
  rawCentralExtraField,
8212
8254
  extraFieldExtendedTimestampFlag,
@@ -8268,7 +8310,7 @@
8268
8310
  rawExtraFieldZip64,
8269
8311
  rawExtraFieldAES,
8270
8312
  rawExtraFieldNTFS,
8271
- rawExtraFieldUnix,
8313
+ rawCentralExtraFieldUnix,
8272
8314
  rawExtraFieldTimestamp,
8273
8315
  rawExtraField,
8274
8316
  rawCentralExtraField);
@@ -8297,7 +8339,7 @@
8297
8339
  rawExtraFieldAES,
8298
8340
  rawExtraFieldExtendedTimestamp,
8299
8341
  rawExtraFieldNTFS,
8300
- rawExtraFieldUnix,
8342
+ rawCentralExtraFieldUnix,
8301
8343
  rawExtraField,
8302
8344
  rawCentralExtraField,
8303
8345
  rawComment,
@@ -8314,7 +8356,7 @@
8314
8356
  uncompressedSize,
8315
8357
  compressedSize
8316
8358
  } = fileEntry;
8317
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField, rawCentralExtraField);
8359
+ const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawCentralExtraFieldUnix, rawExtraField, rawCentralExtraField);
8318
8360
  const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
8319
8361
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
8320
8362
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
@@ -8350,7 +8392,7 @@
8350
8392
  directoryRecord.writeBytes(rawExtraFieldAES);
8351
8393
  directoryRecord.writeBytes(rawExtraFieldExtendedTimestamp);
8352
8394
  directoryRecord.writeBytes(rawExtraFieldNTFS);
8353
- directoryRecord.writeBytes(rawExtraFieldUnix);
8395
+ directoryRecord.writeBytes(rawCentralExtraFieldUnix);
8354
8396
  directoryRecord.writeBytes(rawExtraField);
8355
8397
  directoryRecord.writeBytes(rawCentralExtraField);
8356
8398
  directoryRecord.writeBytes(rawComment);
@@ -8519,26 +8561,42 @@
8519
8561
  return rawExtraField;
8520
8562
  }
8521
8563
 
8522
- async function copyZipData(zipWriter, reader, entries, directoryOffset) {
8564
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
8523
8565
  const { writer } = zipWriter;
8524
8566
  const entryPositions = new Map();
8525
- if (writer.closeDisk) {
8567
+ if (writer.closeDisk || compact) {
8526
8568
  const sortedEntries = Array.from(entries).sort((firstEntry, secondEntry) =>
8527
8569
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8528
- let copiedLength = 0;
8529
- for (const entry of sortedEntries) {
8530
- const sourceOffset = getSourceOffset(reader, entry);
8531
- await copyData(zipWriter, reader, copiedLength, sourceOffset - copiedLength);
8532
- if (exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8533
- await writer.closeDisk();
8534
- }
8535
- entryPositions.set(entry, {
8536
- offset: getSegmentOffset(zipWriter, writer),
8537
- diskNumberStart: getDiskNumber(writer)
8538
- });
8539
- copiedLength = sourceOffset;
8570
+ const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8571
+ const keptEntrySet = new Set(keptEntries);
8572
+ let copiedOffset = 0;
8573
+ let lastPosition;
8574
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8575
+ const entry = sortedEntries[indexEntry];
8576
+ if (keptEntrySet.has(entry)) {
8577
+ const sourceOffset = getSourceOffset(reader, entry);
8578
+ if (sourceOffset < copiedOffset) {
8579
+ entryPositions.set(entry, lastPosition);
8580
+ } else {
8581
+ if (!compact) {
8582
+ await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8583
+ }
8584
+ if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8585
+ await writer.closeDisk();
8586
+ }
8587
+ lastPosition = {
8588
+ offset: getSegmentOffset(zipWriter, writer),
8589
+ diskNumberStart: getDiskNumber(writer)
8590
+ };
8591
+ entryPositions.set(entry, lastPosition);
8592
+ await copyData(zipWriter, reader, sourceOffset, regionEnds[indexEntry] - sourceOffset);
8593
+ copiedOffset = regionEnds[indexEntry];
8594
+ }
8595
+ }
8596
+ }
8597
+ if (!compact) {
8598
+ await copyData(zipWriter, reader, copiedOffset, directoryOffset - copiedOffset);
8540
8599
  }
8541
- await copyData(zipWriter, reader, copiedLength, directoryOffset - copiedLength);
8542
8600
  } else {
8543
8601
  const baseOffset = zipWriter.offset;
8544
8602
  await copyData(zipWriter, reader, 0, directoryOffset);
@@ -8550,6 +8608,20 @@
8550
8608
  return entryPositions;
8551
8609
  }
8552
8610
 
8611
+ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8612
+ const regionEnds = new Array(sortedEntries.length);
8613
+ let regionEnd = directoryOffset;
8614
+ for (let indexEntry = sortedEntries.length - 1; indexEntry >= 0; indexEntry--) {
8615
+ const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8616
+ const nextEntry = sortedEntries[indexEntry + 1];
8617
+ if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8618
+ regionEnd = getSourceOffset(reader, nextEntry);
8619
+ }
8620
+ regionEnds[indexEntry] = regionEnd;
8621
+ }
8622
+ return regionEnds;
8623
+ }
8624
+
8553
8625
  async function copyData(zipWriter, reader, offset, size) {
8554
8626
  if (size > 0) {
8555
8627
  const { writer } = zipWriter;
@@ -8927,7 +8999,7 @@
8927
8999
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8928
9000
  */
8929
9001
 
8930
- const VERSION = "2.18.2";
9002
+ const VERSION = "2.20.0";
8931
9003
 
8932
9004
  /*
8933
9005
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -9476,11 +9548,13 @@
9476
9548
  exports.WARNING_COMPRESSION_UNAVAILABLE = WARNING_COMPRESSION_UNAVAILABLE;
9477
9549
  exports.WARNING_DUPLICATE_FILENAME = WARNING_DUPLICATE_FILENAME;
9478
9550
  exports.WARNING_MALFORMED_EXTRA_FIELD = WARNING_MALFORMED_EXTRA_FIELD;
9551
+ exports.WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET;
9479
9552
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG;
9480
9553
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD;
9481
9554
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES;
9482
9555
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME;
9483
9556
  exports.WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY;
9557
+ exports.WARNING_MISSING_ZIP64_EXTRA_FIELD = WARNING_MISSING_ZIP64_EXTRA_FIELD;
9484
9558
  exports.WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY;
9485
9559
  exports.WARNING_PREPENDED_CENTRAL_DIRECTORY = WARNING_PREPENDED_CENTRAL_DIRECTORY;
9486
9560
  exports.WARNING_PREPENDED_DATA = WARNING_PREPENDED_DATA;