@zip.js/zip.js 2.21.0 → 2.22.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.
package/index.cjs CHANGED
@@ -219,6 +219,7 @@ const ERR_INVALID_FUNCTION_OPTION = "Invalid option (must be a function)";
219
219
  const ERR_INVALID_SIGNAL = "Invalid signal (must be an AbortSignal instance)";
220
220
  const ERR_INVALID_PASSWORD_TYPE = "Invalid password (password must be a string, rawPassword must be a Uint8Array)";
221
221
  const ERR_INVALID_PASS_THROUGH_VALUE = "Invalid passThrough option (must be a boolean or 'compressed')";
222
+ const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
222
223
  const ERR_ABORTED = "The operation was aborted";
223
224
  const ABORT_ERROR_NAME$1 = "AbortError";
224
225
 
@@ -229,6 +230,13 @@ function checkFunctionOption(value) {
229
230
  return value;
230
231
  }
231
232
 
233
+ function checkReaderOptionsObject(readerOptions) {
234
+ if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
235
+ throw new Error(ERR_INVALID_READER_OPTIONS);
236
+ }
237
+ return readerOptions;
238
+ }
239
+
232
240
  function checkSignalOption(signal) {
233
241
  if (signal && (typeof signal.addEventListener != FUNCTION_TYPE || typeof signal.aborted != BOOLEAN_TYPE)) {
234
242
  throw new Error(ERR_INVALID_SIGNAL);
@@ -324,7 +332,6 @@ const DEFAULT_CONFIGURATION = {
324
332
  workerStartupTimeout: 5000,
325
333
  useWebWorkers: true,
326
334
  useCompressionStream: true,
327
- transferStreams: true,
328
335
  CompressionStream: typeof CompressionStream != UNDEFINED_TYPE && CompressionStream,
329
336
  DecompressionStream: typeof DecompressionStream != UNDEFINED_TYPE && DecompressionStream
330
337
  };
@@ -338,8 +345,7 @@ const URI_PROPERTY_NAMES = [
338
345
  ];
339
346
  const BOOLEAN_PROPERTY_NAMES = [
340
347
  "useCompressionStream",
341
- "useWebWorkers",
342
- "transferStreams"
348
+ "useWebWorkers"
343
349
  ];
344
350
  const NUMBER_PROPERTY_NAMES = [
345
351
  "chunkSize",
@@ -4849,7 +4855,7 @@ class ZipReader {
4849
4855
  constructor(reader, options = {}) {
4850
4856
  Object.assign(this, {
4851
4857
  reader: new GenericReader(reader),
4852
- options,
4858
+ options: options || {},
4853
4859
  readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4854
4860
  entriesMissingZip64ExtraField: new WeakSet()
4855
4861
  });
@@ -6869,6 +6875,7 @@ function watchPromiseError(zipWriter, promise) {
6869
6875
  async function appendZipEntries(zipWriter, reader, options = {}) {
6870
6876
  const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6871
6877
  const filter = checkFunctionOption(options.filter);
6878
+ const readerOptions = checkReaderOptionsObject(options.readerOptions);
6872
6879
  while (pendingAddFileCalls.size) {
6873
6880
  await Promise.allSettled(Array.from(pendingAddFileCalls));
6874
6881
  }
@@ -6885,15 +6892,19 @@ async function appendZipEntries(zipWriter, reader, options = {}) {
6885
6892
  await initStream(reader);
6886
6893
  }
6887
6894
  const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6888
- const zipReader$1 = new ZipReader(reader);
6889
- const entries = await zipReader$1.getEntries();
6895
+ const zipReader$1 = new ZipReader(reader, readerOptions);
6890
6896
  const keptEntries = [];
6891
- for (const entry of entries) {
6892
- if (!filter || await filter(entry)) {
6893
- keptEntries.push(entry);
6897
+ let entries;
6898
+ try {
6899
+ entries = await zipReader$1.getEntries();
6900
+ for (const entry of entries) {
6901
+ if (!filter || await filter(entry, getExistingEntry(fileEntries, entry.filename))) {
6902
+ keptEntries.push(entry);
6903
+ }
6894
6904
  }
6905
+ } finally {
6906
+ await zipReader$1.close();
6895
6907
  }
6896
- await zipReader$1.close();
6897
6908
  await initStream(zipWriter.writer);
6898
6909
  const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6899
6910
  keptEntries.forEach(entry => {
@@ -6963,7 +6974,7 @@ async function appendZipEntries(zipWriter, reader, options = {}) {
6963
6974
  setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6964
6975
  }
6965
6976
  const { offset, diskNumberStart } = entryPositions.get(entry);
6966
- Object.assign(entry, {
6977
+ fileEntries.set(entry.filename, Object.assign({}, entry, {
6967
6978
  zip64Enabled: true,
6968
6979
  zip64UncompressedSize,
6969
6980
  zip64CompressedSize,
@@ -6980,8 +6991,7 @@ async function appendZipEntries(zipWriter, reader, options = {}) {
6980
6991
  rawCentralExtraField: EMPTY_UINT8_ARRAY,
6981
6992
  headerArray,
6982
6993
  headerView
6983
- });
6984
- fileEntries.set(entry.filename, entry);
6994
+ }));
6985
6995
  });
6986
6996
  } catch (error) {
6987
6997
  appendedFilenames.forEach(filename => filenames.delete(filename));
@@ -6995,6 +7005,11 @@ async function appendZipEntries(zipWriter, reader, options = {}) {
6995
7005
  }
6996
7006
  }
6997
7007
 
7008
+ function getExistingEntry(fileEntries, filename) {
7009
+ const fileEntry = fileEntries.get(filename);
7010
+ return fileEntry ? new Entry(fileEntry) : UNDEFINED_VALUE;
7011
+ }
7012
+
6998
7013
  async function prependZipEntries(zipWriter, reader) {
6999
7014
  if (zipWriter.filenames.size) {
7000
7015
  throw new Error(ERR_ZIP_NOT_EMPTY);
@@ -9237,7 +9252,7 @@ function formatSupported(StreamClass, format) {
9237
9252
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9238
9253
  */
9239
9254
 
9240
- const VERSION = "2.21.0";
9255
+ const VERSION = "2.22.0";
9241
9256
 
9242
9257
  /*
9243
9258
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -10312,7 +10327,6 @@ const DUPLICATES_KEEP_FIRST = "keep-first";
10312
10327
  const DUPLICATES_KEEP_LAST = "keep-last";
10313
10328
  const DUPLICATES_VALUES = new Set([DUPLICATES_THROW, DUPLICATES_KEEP_FIRST, DUPLICATES_KEEP_LAST]);
10314
10329
  const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
10315
- const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
10316
10330
  const ERR_UNSUPPORTED_PASS_THROUGH_VALUE = "The 'compressed' passThrough option is only supported by Entry#getData() and ZipWriter#add()";
10317
10331
  const ERR_INVALID_PASSWORDS = "Invalid passwords option (must be an array of strings)";
10318
10332
  const ERR_INVALID_REQUEST_PASSWORD = "Invalid requestPassword option (must be a function returning a string or undefined)";
@@ -11238,9 +11252,7 @@ function getDeterminedSize(child, passThrough) {
11238
11252
  }
11239
11253
 
11240
11254
  function checkReaderOptions(readerOptions) {
11241
- if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
11242
- throw new Error(ERR_INVALID_READER_OPTIONS);
11243
- }
11255
+ checkReaderOptionsObject(readerOptions);
11244
11256
  if (readerOptions) {
11245
11257
  checkPassThroughValue(readerOptions.passThrough);
11246
11258
  checkPasswordCandidatesOptions(readerOptions);
package/index.d.cts CHANGED
@@ -635,13 +635,6 @@ export interface WorkerConfiguration {
635
635
  * @defaultValue true
636
636
  */
637
637
  useCompressionStream?: boolean;
638
- /**
639
- * `true` to transfer stream ownership to web workers.
640
- *
641
- * @deprecated The option is ignored whatever its value: the data always crosses the worker boundary chunk by
642
- * chunk, transferring the streams instead was slower on every engine measured.
643
- */
644
- transferStreams?: boolean;
645
638
  }
646
639
 
647
640
  /**
@@ -3086,11 +3079,15 @@ export class ZipWriter<Type> {
3086
3079
  * are not applied to the copied entries. The comment and the digital signature of the zip file are not copied,
3087
3080
  * since its central directory is rebuilt: pass them to {@link ZipWriter#close}.
3088
3081
  *
3089
- * Pending {@link ZipWriter#add} calls are completed before the data is copied, and add() calls made
3090
- * while the copy is in progress are written after it. If an entry of the zip file has the same
3082
+ * Pending {@link ZipWriter#add} calls are completed before the data is copied. add() calls made while the
3083
+ * `filter` option runs are written before the copied entries, and add() calls made once the copy has started
3084
+ * are written after it. If an entry of the zip file has the same
3091
3085
  * filename as an entry of the current zip, the method throws with the `ERR_DUPLICATED_NAME` error
3092
3086
  * message and leaves the current zip unchanged; call {@link ZipWriter#remove} beforehand to resolve
3093
- * the conflicts. An entry whose sizes or offset are unusable because its Zip64 extra field is missing (see
3087
+ * the conflicts. The same error is thrown when two entries of the zip file share a filename, since a
3088
+ * `ZipWriter` holds one entry per filename: use the {@link ZipWriterAppendZipOptions#filter} option to
3089
+ * keep one of them. The entries passed to `filter` are not modified by the copy. An entry whose sizes or
3090
+ * offset are unusable because its Zip64 extra field is missing (see
3094
3091
  * {@link WARNING_MISSING_ZIP64_EXTRA_FIELD}) cannot be copied: the method throws
3095
3092
  * {@link ERR_EXTRAFIELD_ZIP64_NOT_FOUND} and leaves the current zip unchanged, unless the
3096
3093
  * {@link ZipWriterAppendZipOptions#filter} option leaves the entry out.
@@ -3174,10 +3171,11 @@ export class ZipWriter<Type> {
3174
3171
  * Removes an entry from the central directory that will be written for the zip file. The entry
3175
3172
  * data itself cannot be removed because it has already been streamed to the output.
3176
3173
  *
3177
- * @param entry The entry to remove. This can be an {@link Entry} instance or the filename of the entry.
3174
+ * @param entry The entry to remove. This can be an {@link Entry} instance, the {@link EntryMetaData} returned by
3175
+ * {@link ZipWriter#add} or passed to the `filter` option of {@link ZipWriter#appendZip}, or the filename of the entry.
3178
3176
  * @returns `true` if the entry has been removed, `false` otherwise.
3179
3177
  */
3180
- remove(entry: Entry | string): boolean;
3178
+ remove(entry: EntryMetaData | string): boolean;
3181
3179
 
3182
3180
  /**
3183
3181
  * Writes the entries directory, writes the global comment, and returns the content of the zip file
@@ -3342,6 +3340,13 @@ export interface ZipWriterAppendZipOptions {
3342
3340
  * central directory, and the entry is copied when it returns (or resolves to) `true`. The function can read
3343
3341
  * the data of the entry with {@link Entry#getData} to decide: every call completes before any data is copied.
3344
3342
  *
3343
+ * The second argument is the entry of the current zip which has the same filename, as {@link ZipWriter#add}
3344
+ * or a previous call to {@link ZipWriter#appendZip} left it, or `undefined` when there is none. It is the way to
3345
+ * apply a duplicate filename policy, since keeping both entries throws `ERR_DUPLICATED_NAME`: return
3346
+ * `!existingEntry` to keep the entry of the current zip, call {@link ZipWriter#remove} with `existingEntry` and
3347
+ * return `true` to replace it, or compare `crc32`, `uncompressedSize` or `lastModDate` to decide. An entry
3348
+ * being added concurrently by a pending {@link ZipWriter#add} call is not passed.
3349
+ *
3345
3350
  * @remarks
3346
3351
  * When the option is set, the data of the zip file is copied entry by entry and the entries left out leave no
3347
3352
  * bytes behind in the output, unlike {@link ZipWriter#remove}, which drops an entry from the central directory
@@ -3360,9 +3365,25 @@ export interface ZipWriterAppendZipOptions {
3360
3365
  * as well.
3361
3366
  *
3362
3367
  * @param entry The entry read from the zip file.
3368
+ * @param existingEntry The entry of the current zip with the same filename, if any.
3363
3369
  * @returns `true` to copy the entry.
3364
3370
  */
3365
- filter?: (entry: Entry) => boolean | Promise<boolean>;
3371
+ filter?: (entry: Entry, existingEntry?: EntryMetaData) => boolean | Promise<boolean>;
3372
+ /**
3373
+ * The options of the {@link ZipReader} which reads the zip file.
3374
+ *
3375
+ * @remarks
3376
+ * The zip file is read with the default options otherwise, so a zip file which a `ZipReader` rejects by default
3377
+ * cannot be appended as-is: set {@link ZipReaderConstructorOptions#filenameValidation} or
3378
+ * {@link ZipReaderConstructorOptions#strictness} here to copy the entries of a zip file holding unsafe or
3379
+ * unusual filenames, {@link ZipReaderConstructorOptions#filenameEncoding} to decode the filenames the
3380
+ * duplicate check and the `filter` option see, and {@link ZipReaderConstructorOptions#password} to let
3381
+ * `filter` read the data of encrypted entries with {@link Entry#getData}. The bytes of the entries are copied
3382
+ * as-is whatever the options are.
3383
+ *
3384
+ * A value which is neither an object nor unset throws an {@link ERR_INVALID_READER_OPTIONS} error.
3385
+ */
3386
+ readerOptions?: ZipReaderConstructorOptions;
3366
3387
  }
3367
3388
 
3368
3389
  export interface ZipWriterCloseOptions extends EntryOnprogressOptions {
@@ -5506,7 +5527,8 @@ export const ERR_UNSUPPORTED_PASS_THROUGH_VALUE: string;
5506
5527
  /**
5507
5528
  * Invalid readerOptions error (thrown by `{@link ZipDirectoryEntry}#export*()`,
5508
5529
  * {@link ZipDirectoryEntry#getExportedSize} and {@link ZipDirectoryEntry#exportFileSystemHandle} when the
5509
- * {@link ZipDirectoryEntryExportOptions#readerOptions} option is neither an object nor unset)
5530
+ * {@link ZipDirectoryEntryExportOptions#readerOptions} option is neither an object nor unset, and by
5531
+ * {@link ZipWriter#appendZip} for {@link ZipWriterAppendZipOptions#readerOptions})
5510
5532
  *
5511
5533
  * @remarks A value of another type was silently ignored: a password passed as a string instead of an object failed
5512
5534
  * with the unrelated {@link ERR_ENCRYPTED}, while the other options were dropped without any error. Note that an
package/index.d.ts CHANGED
@@ -635,13 +635,6 @@ export interface WorkerConfiguration {
635
635
  * @defaultValue true
636
636
  */
637
637
  useCompressionStream?: boolean;
638
- /**
639
- * `true` to transfer stream ownership to web workers.
640
- *
641
- * @deprecated The option is ignored whatever its value: the data always crosses the worker boundary chunk by
642
- * chunk, transferring the streams instead was slower on every engine measured.
643
- */
644
- transferStreams?: boolean;
645
638
  }
646
639
 
647
640
  /**
@@ -3086,11 +3079,15 @@ export class ZipWriter<Type> {
3086
3079
  * are not applied to the copied entries. The comment and the digital signature of the zip file are not copied,
3087
3080
  * since its central directory is rebuilt: pass them to {@link ZipWriter#close}.
3088
3081
  *
3089
- * Pending {@link ZipWriter#add} calls are completed before the data is copied, and add() calls made
3090
- * while the copy is in progress are written after it. If an entry of the zip file has the same
3082
+ * Pending {@link ZipWriter#add} calls are completed before the data is copied. add() calls made while the
3083
+ * `filter` option runs are written before the copied entries, and add() calls made once the copy has started
3084
+ * are written after it. If an entry of the zip file has the same
3091
3085
  * filename as an entry of the current zip, the method throws with the `ERR_DUPLICATED_NAME` error
3092
3086
  * message and leaves the current zip unchanged; call {@link ZipWriter#remove} beforehand to resolve
3093
- * the conflicts. An entry whose sizes or offset are unusable because its Zip64 extra field is missing (see
3087
+ * the conflicts. The same error is thrown when two entries of the zip file share a filename, since a
3088
+ * `ZipWriter` holds one entry per filename: use the {@link ZipWriterAppendZipOptions#filter} option to
3089
+ * keep one of them. The entries passed to `filter` are not modified by the copy. An entry whose sizes or
3090
+ * offset are unusable because its Zip64 extra field is missing (see
3094
3091
  * {@link WARNING_MISSING_ZIP64_EXTRA_FIELD}) cannot be copied: the method throws
3095
3092
  * {@link ERR_EXTRAFIELD_ZIP64_NOT_FOUND} and leaves the current zip unchanged, unless the
3096
3093
  * {@link ZipWriterAppendZipOptions#filter} option leaves the entry out.
@@ -3174,10 +3171,11 @@ export class ZipWriter<Type> {
3174
3171
  * Removes an entry from the central directory that will be written for the zip file. The entry
3175
3172
  * data itself cannot be removed because it has already been streamed to the output.
3176
3173
  *
3177
- * @param entry The entry to remove. This can be an {@link Entry} instance or the filename of the entry.
3174
+ * @param entry The entry to remove. This can be an {@link Entry} instance, the {@link EntryMetaData} returned by
3175
+ * {@link ZipWriter#add} or passed to the `filter` option of {@link ZipWriter#appendZip}, or the filename of the entry.
3178
3176
  * @returns `true` if the entry has been removed, `false` otherwise.
3179
3177
  */
3180
- remove(entry: Entry | string): boolean;
3178
+ remove(entry: EntryMetaData | string): boolean;
3181
3179
 
3182
3180
  /**
3183
3181
  * Writes the entries directory, writes the global comment, and returns the content of the zip file
@@ -3342,6 +3340,13 @@ export interface ZipWriterAppendZipOptions {
3342
3340
  * central directory, and the entry is copied when it returns (or resolves to) `true`. The function can read
3343
3341
  * the data of the entry with {@link Entry#getData} to decide: every call completes before any data is copied.
3344
3342
  *
3343
+ * The second argument is the entry of the current zip which has the same filename, as {@link ZipWriter#add}
3344
+ * or a previous call to {@link ZipWriter#appendZip} left it, or `undefined` when there is none. It is the way to
3345
+ * apply a duplicate filename policy, since keeping both entries throws `ERR_DUPLICATED_NAME`: return
3346
+ * `!existingEntry` to keep the entry of the current zip, call {@link ZipWriter#remove} with `existingEntry` and
3347
+ * return `true` to replace it, or compare `crc32`, `uncompressedSize` or `lastModDate` to decide. An entry
3348
+ * being added concurrently by a pending {@link ZipWriter#add} call is not passed.
3349
+ *
3345
3350
  * @remarks
3346
3351
  * When the option is set, the data of the zip file is copied entry by entry and the entries left out leave no
3347
3352
  * bytes behind in the output, unlike {@link ZipWriter#remove}, which drops an entry from the central directory
@@ -3360,9 +3365,25 @@ export interface ZipWriterAppendZipOptions {
3360
3365
  * as well.
3361
3366
  *
3362
3367
  * @param entry The entry read from the zip file.
3368
+ * @param existingEntry The entry of the current zip with the same filename, if any.
3363
3369
  * @returns `true` to copy the entry.
3364
3370
  */
3365
- filter?: (entry: Entry) => boolean | Promise<boolean>;
3371
+ filter?: (entry: Entry, existingEntry?: EntryMetaData) => boolean | Promise<boolean>;
3372
+ /**
3373
+ * The options of the {@link ZipReader} which reads the zip file.
3374
+ *
3375
+ * @remarks
3376
+ * The zip file is read with the default options otherwise, so a zip file which a `ZipReader` rejects by default
3377
+ * cannot be appended as-is: set {@link ZipReaderConstructorOptions#filenameValidation} or
3378
+ * {@link ZipReaderConstructorOptions#strictness} here to copy the entries of a zip file holding unsafe or
3379
+ * unusual filenames, {@link ZipReaderConstructorOptions#filenameEncoding} to decode the filenames the
3380
+ * duplicate check and the `filter` option see, and {@link ZipReaderConstructorOptions#password} to let
3381
+ * `filter` read the data of encrypted entries with {@link Entry#getData}. The bytes of the entries are copied
3382
+ * as-is whatever the options are.
3383
+ *
3384
+ * A value which is neither an object nor unset throws an {@link ERR_INVALID_READER_OPTIONS} error.
3385
+ */
3386
+ readerOptions?: ZipReaderConstructorOptions;
3366
3387
  }
3367
3388
 
3368
3389
  export interface ZipWriterCloseOptions extends EntryOnprogressOptions {
@@ -5506,7 +5527,8 @@ export const ERR_UNSUPPORTED_PASS_THROUGH_VALUE: string;
5506
5527
  /**
5507
5528
  * Invalid readerOptions error (thrown by `{@link ZipDirectoryEntry}#export*()`,
5508
5529
  * {@link ZipDirectoryEntry#getExportedSize} and {@link ZipDirectoryEntry#exportFileSystemHandle} when the
5509
- * {@link ZipDirectoryEntryExportOptions#readerOptions} option is neither an object nor unset)
5530
+ * {@link ZipDirectoryEntryExportOptions#readerOptions} option is neither an object nor unset, and by
5531
+ * {@link ZipWriter#appendZip} for {@link ZipWriterAppendZipOptions#readerOptions})
5510
5532
  *
5511
5533
  * @remarks A value of another type was silently ignored: a password passed as a string instead of an object failed
5512
5534
  * with the unrelated {@link ERR_ENCRYPTED}, while the other options were dropped without any error. Note that an