@zip.js/zip.js 2.11.4 → 2.13.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 (52) hide show
  1. package/deno.json +1 -1
  2. package/dist/zip-core-external.js +549 -139
  3. package/dist/zip-core-external.min.js +1 -1
  4. package/dist/zip-core.js +559 -136
  5. package/dist/zip-core.min.js +1 -1
  6. package/dist/zip-fs-core-external.js +615 -260
  7. package/dist/zip-fs-core-external.min.js +1 -1
  8. package/dist/zip-fs-core.js +628 -257
  9. package/dist/zip-fs-core.min.js +1 -1
  10. package/dist/zip-fs-external.js +615 -260
  11. package/dist/zip-fs-external.min.js +1 -1
  12. package/dist/zip-fs-native.js +630 -259
  13. package/dist/zip-fs-native.min.js +1 -1
  14. package/dist/zip-fs.js +631 -260
  15. package/dist/zip-fs.min.js +1 -1
  16. package/dist/zip-legacy.js +561 -138
  17. package/dist/zip-legacy.min.js +1 -1
  18. package/dist/zip-native.js +561 -138
  19. package/dist/zip-native.min.js +1 -1
  20. package/dist/zip-web-worker-native.js +1 -1
  21. package/dist/zip-web-worker.js +1 -1
  22. package/dist/zip.js +562 -139
  23. package/dist/zip.min.js +1 -1
  24. package/index-native.cjs +630 -259
  25. package/index-native.min.js +1 -1
  26. package/index.cjs +631 -260
  27. package/index.d.cts +474 -59
  28. package/index.d.ts +474 -59
  29. package/index.min.js +1 -1
  30. package/lib/core/codec-worker-web.js +72 -22
  31. package/lib/core/codec-worker.js +18 -5
  32. package/lib/core/configuration.js +20 -5
  33. package/lib/core/constants.js +2 -0
  34. package/lib/core/io.js +13 -5
  35. package/lib/core/options.js +16 -0
  36. package/lib/core/streams/codec-stream.js +6 -0
  37. package/lib/core/util/error.js +35 -0
  38. package/lib/core/util/warnings.js +42 -0
  39. package/lib/core/version.js +1 -1
  40. package/lib/core/web-worker-base.js +46 -9
  41. package/lib/core/web-worker-inline-native.js +1 -1
  42. package/lib/core/web-worker-inline-wasm.js +1 -1
  43. package/lib/core/zip-entry.js +49 -1
  44. package/lib/core/zip-fs.js +45 -133
  45. package/lib/core/zip-reader.js +115 -52
  46. package/lib/core/zip-writer.js +244 -57
  47. package/lib/zip-core-base.js +9 -3
  48. package/lib/zip-core-reader.js +2 -0
  49. package/lib/zip-core-writer.js +6 -1
  50. package/lib/zip-module-wasm-base.js +2 -2
  51. package/package.json +5 -3
  52. package/worker-message-property-names.js +1 -1
package/dist/zip-core.js CHANGED
@@ -134,6 +134,7 @@
134
134
  const BOOLEAN_TYPE = "boolean";
135
135
 
136
136
  const EMPTY_UINT8_ARRAY = new Uint8Array();
137
+ const SYMBOL_ASYNC_DISPOSE = Symbol.asyncDispose || Symbol();
137
138
 
138
139
  /*
139
140
  Copyright (c) 2022 Gildas Lormeau. All rights reserved.
@@ -178,6 +179,7 @@
178
179
  const OPTION_CHECK_OVERLAPPING_ENTRY = "checkOverlappingEntry";
179
180
  const OPTION_CHECK_AMBIGUITY = "checkAmbiguity";
180
181
  const OPTION_CHECK_LOCAL_DIRECTORY = "checkLocalDirectory";
182
+ const OPTION_CHECK_LOCAL_FILENAME = "checkLocalFilename";
181
183
  const OPTION_CHECK_SIGNATURE = "checkSignature";
182
184
  const OPTION_CHECK_CRC32 = "checkCrc32";
183
185
  const OPTION_CHECK_AUTHENTICATION_CODE = "checkAuthenticationCode";
@@ -208,15 +210,18 @@
208
210
  const OPTION_MAX_APPENDED_DATA_SIZE = "maxAppendedDataSize";
209
211
  const OPTION_DECRYPT_CENTRAL_DIRECTORY = "decryptCentralDirectory";
210
212
  const OPTION_SIGN_CENTRAL_DIRECTORY = "signCentralDirectory";
213
+ const OPTION_ENTRY = "entry";
211
214
  const TEXT_TYPE_FILENAME = "filename";
212
215
  const TEXT_TYPE_COMMENT = "comment";
213
216
  const STRICTNESS_STRICT = "strict";
214
217
  const STRICTNESS_BALANCED = "balanced";
215
218
  const STRICTNESS_TOLERANT = "tolerant";
219
+ const PASS_THROUGH_COMPRESSED = "compressed";
216
220
 
217
221
  const ERR_INVALID_FUNCTION_OPTION = "Invalid option (must be a function)";
218
222
  const ERR_INVALID_SIGNAL = "Invalid signal (must be an AbortSignal instance)";
219
223
  const ERR_INVALID_PASSWORD_TYPE = "Invalid password (password must be a string, rawPassword must be a Uint8Array)";
224
+ const ERR_INVALID_PASS_THROUGH_VALUE = "Invalid passThrough option (must be a boolean or 'compressed')";
220
225
  const ERR_ABORTED = "The operation was aborted";
221
226
  const ABORT_ERROR_NAME = "AbortError";
222
227
 
@@ -246,6 +251,13 @@
246
251
  }
247
252
  }
248
253
 
254
+ function checkPassThroughOption(passThrough) {
255
+ if (passThrough !== UNDEFINED_VALUE && typeof passThrough != BOOLEAN_TYPE && passThrough !== PASS_THROUGH_COMPRESSED) {
256
+ throw new Error(ERR_INVALID_PASS_THROUGH_VALUE);
257
+ }
258
+ return passThrough;
259
+ }
260
+
249
261
  function checkInteger(value, maxValue, errorMessage) {
250
262
  if (!Number.isInteger(value) || value < 0 || value > maxValue) {
251
263
  throw new Error(errorMessage);
@@ -295,6 +307,8 @@
295
307
  const MINIMUM_CHUNK_SIZE = 64;
296
308
  const MINIMUM_PROPERTY_VALUE = 1;
297
309
  const ERR_INVALID_MAX_WORKERS = "Invalid maxWorkers (must be an integer greater than 0)";
310
+ const ERR_INVALID_BASE_URI = "Invalid baseURI (must be a string)";
311
+ const ERR_INVALID_URI = "Invalid URI (must be a string or a function returning a string)";
298
312
  let maxWorkers = 2;
299
313
  try {
300
314
  if (typeof navigator != UNDEFINED_TYPE && navigator.hardwareConcurrency) {
@@ -319,9 +333,9 @@
319
333
  };
320
334
 
321
335
  const PROPERTY_NAME_MAX_WORKERS = "maxWorkers";
336
+ const PROPERTY_NAME_BASE_URI = "baseURI";
322
337
 
323
- const STRING_PROPERTY_NAMES = [
324
- "baseURI",
338
+ const URI_PROPERTY_NAMES = [
325
339
  "wasmURI",
326
340
  "workerURI"
327
341
  ];
@@ -345,7 +359,8 @@
345
359
  "DecompressionStreamFallback"
346
360
  ];
347
361
  const CONFIGURABLE_PROPERTY_NAMES = [
348
- ...STRING_PROPERTY_NAMES,
362
+ PROPERTY_NAME_BASE_URI,
363
+ ...URI_PROPERTY_NAMES,
349
364
  ...BOOLEAN_PROPERTY_NAMES,
350
365
  ...NUMBER_PROPERTY_NAMES,
351
366
  ...FUNCTION_PROPERTY_NAMES
@@ -389,6 +404,14 @@
389
404
  }
390
405
  } else if (FUNCTION_PROPERTY_NAMES.includes(propertyName)) {
391
406
  checkFunctionOption(propertyValue);
407
+ } else if (propertyName == PROPERTY_NAME_BASE_URI) {
408
+ if (propertyValue && typeof propertyValue != STRING_TYPE) {
409
+ throw new Error(ERR_INVALID_BASE_URI);
410
+ }
411
+ } else if (URI_PROPERTY_NAMES.includes(propertyName)) {
412
+ if (propertyValue && typeof propertyValue != STRING_TYPE && typeof propertyValue != FUNCTION_TYPE) {
413
+ throw new Error(ERR_INVALID_URI);
414
+ }
392
415
  }
393
416
  return propertyValue;
394
417
  }
@@ -466,6 +489,39 @@
466
489
  return new DataView(array.buffer, array.byteOffset, array.byteLength);
467
490
  }
468
491
 
492
+ /*
493
+ Copyright (c) 2026 Gildas Lormeau. All rights reserved.
494
+
495
+ Redistribution and use in source and binary forms, with or without
496
+ modification, are permitted provided that the following conditions are met:
497
+
498
+ 1. Redistributions of source code must retain the above copyright notice,
499
+ this list of conditions and the following disclaimer.
500
+
501
+ 2. Redistributions in binary form must reproduce the above copyright
502
+ notice, this list of conditions and the following disclaimer in
503
+ the documentation and/or other materials provided with the distribution.
504
+
505
+ 3. The names of the authors may not be used to endorse or promote products
506
+ derived from this software without specific prior written permission.
507
+
508
+ THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
509
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
510
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
511
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
512
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
513
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
514
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
515
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
516
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
517
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
518
+ */
519
+
520
+
521
+ function isErrorObject(error) {
522
+ return Boolean(error) && typeof error == "object";
523
+ }
524
+
469
525
  /*
470
526
  Copyright (c) 2022 Gildas Lormeau. All rights reserved.
471
527
 
@@ -2605,6 +2661,7 @@
2605
2661
  constructor(chunkSize) {
2606
2662
  const pendingChunks = [];
2607
2663
  let pendingLength = 0;
2664
+ let outputSize = 0;
2608
2665
  if (!Number.isFinite(chunkSize) || chunkSize < 1) {
2609
2666
  chunkSize = DEFAULT_CHUNK_SIZE;
2610
2667
  }
@@ -2613,15 +2670,20 @@
2613
2670
  pendingChunks.push(chunk);
2614
2671
  pendingLength += chunk.length;
2615
2672
  while (pendingLength > chunkSize) {
2673
+ outputSize += chunkSize;
2616
2674
  controller.enqueue(shiftChunk());
2617
2675
  }
2618
2676
  },
2619
2677
  flush(controller) {
2620
2678
  if (pendingLength) {
2679
+ outputSize += pendingLength;
2621
2680
  controller.enqueue(concatChunks(pendingChunks, pendingLength));
2622
2681
  }
2623
2682
  }
2624
2683
  });
2684
+ Object.defineProperty(this, "outputSize", {
2685
+ get: () => outputSize
2686
+ });
2625
2687
 
2626
2688
  function shiftChunk() {
2627
2689
  const result = new Uint8Array(chunkSize);
@@ -2727,7 +2789,9 @@
2727
2789
 
2728
2790
  class CodecWorker {
2729
2791
 
2730
- constructor(workerData, { readable, writable }, { options, config, streamOptions, useWebWorkers, transferStreams, workerURI, createWorker }, onTaskFinished) {
2792
+ constructor(workerData, { readable, writable }, workerOptions, onTaskFinished) {
2793
+ const { options, config, streamOptions, useWebWorkers, transferStreams, workerURI } = workerOptions;
2794
+ let { createWorker } = workerOptions;
2731
2795
  const { signal } = streamOptions;
2732
2796
  if (createWorkerFailed) {
2733
2797
  createWorker = UNDEFINED_VALUE;
@@ -2740,6 +2804,7 @@
2740
2804
  .pipeThrough(new ProgressWatcherStream(streamOptions), { signal }),
2741
2805
  writable,
2742
2806
  options: Object.assign({}, options),
2807
+ workerOptions,
2743
2808
  workerURI,
2744
2809
  createWorker,
2745
2810
  transferStreams,
@@ -2821,8 +2886,8 @@
2821
2886
  };
2822
2887
  }
2823
2888
 
2824
- async function runWorker$1({ options, readable, writable, onTaskFinished }, config) {
2825
- let codecStream;
2889
+ async function runWorker$1({ options, readable, writable, onTaskFinished, workerOptions }, config) {
2890
+ let codecStream, chunkStream;
2826
2891
  try {
2827
2892
  if (options.compressed && !options.format) {
2828
2893
  const deflate = options.codecType.startsWith(CODEC_DEFLATE);
@@ -2845,9 +2910,10 @@
2845
2910
  }
2846
2911
  }
2847
2912
  codecStream = new CodecStream(options, config);
2913
+ chunkStream = new ChunkStream(getChunkSize(config));
2848
2914
  await readable
2849
2915
  .pipeThrough(codecStream)
2850
- .pipeThrough(new ChunkStream(getChunkSize(config)))
2916
+ .pipeThrough(chunkStream)
2851
2917
  .pipeTo(writable, { preventClose: true, preventAbort: true });
2852
2918
  const {
2853
2919
  crc32,
@@ -2861,7 +2927,15 @@
2861
2927
  };
2862
2928
  } catch (error) {
2863
2929
  if (codecStream) {
2864
- error.outputSize = codecStream.outputSize;
2930
+ const outputSize = chunkStream ? chunkStream.outputSize : 0;
2931
+ workerOptions.outputSize = outputSize;
2932
+ if (isErrorObject(error)) {
2933
+ try {
2934
+ error.outputSize = outputSize;
2935
+ } catch {
2936
+ // ignored
2937
+ }
2938
+ }
2865
2939
  }
2866
2940
  throw error;
2867
2941
  } finally {
@@ -2970,11 +3044,23 @@
2970
3044
  let resolveResult, rejectResult;
2971
3045
  const result = new Promise((resolve, reject) => {
2972
3046
  resolveResult = resolve;
2973
- rejectResult = reject;
3047
+ rejectResult = error => {
3048
+ const { outputSize, workerOptions } = workerData;
3049
+ workerOptions.outputSize = outputSize;
3050
+ if (isErrorObject(error) && error.outputSize === UNDEFINED_VALUE) {
3051
+ try {
3052
+ error.outputSize = outputSize;
3053
+ } catch {
3054
+ // ignored
3055
+ }
3056
+ }
3057
+ reject(error);
3058
+ };
2974
3059
  });
2975
3060
  Object.assign(workerData, {
2976
3061
  reader: null,
2977
3062
  writer: null,
3063
+ outputSize: 0,
2978
3064
  resolveResult,
2979
3065
  rejectResult,
2980
3066
  result
@@ -3235,21 +3321,15 @@
3235
3321
  }
3236
3322
 
3237
3323
  async function onMessage({ data }, workerData) {
3238
- const { type, value, messageId, result, error } = data;
3324
+ const { type, value, messageId, result, error, errorValue } = data;
3239
3325
  const { reader, writer, resolveResult, rejectResult, onTaskFinished, generation } = workerData;
3240
3326
  const stale = () => workerData.generation != generation;
3241
3327
  try {
3242
3328
  if (error) {
3243
- const { message, stack, code, name, outputSize, cause, codecImportFailed } = error;
3244
- const responseError = new Error(message);
3245
- Object.assign(responseError, { stack, code, name, outputSize });
3246
- if (cause) {
3247
- responseError.cause = Object.assign(new Error(cause.message), { name: cause.name });
3248
- }
3249
- if (codecImportFailed) {
3250
- responseError.codecImportFailed = true;
3329
+ if (error.outputSize !== UNDEFINED_VALUE) {
3330
+ workerData.outputSize = error.outputSize;
3251
3331
  }
3252
- close(responseError);
3332
+ fail(getResponseError(error, errorValue));
3253
3333
  } else {
3254
3334
  if (type == MESSAGE_PULL) {
3255
3335
  const { value, done } = await reader.read();
@@ -3258,39 +3338,82 @@
3258
3338
  }
3259
3339
  }
3260
3340
  if (type == MESSAGE_DATA) {
3341
+ const chunk = new Uint8Array(value);
3261
3342
  await writer.ready;
3262
- await writer.write(new Uint8Array(value));
3343
+ await writer.write(chunk);
3344
+ workerData.outputSize += chunk.length;
3263
3345
  if (!stale()) {
3264
3346
  sendMessage({ type: MESSAGE_ACK_DATA, messageId }, workerData);
3265
3347
  }
3266
3348
  }
3267
3349
  if (type == MESSAGE_CLOSE) {
3268
- close(null, result);
3350
+ succeed(result);
3269
3351
  }
3270
3352
  }
3271
3353
  } catch (error) {
3272
3354
  if (!stale()) {
3273
3355
  terminateWorker$1(workerData);
3274
- close(error);
3356
+ fail(error);
3275
3357
  }
3276
3358
  }
3277
3359
 
3278
- function close(error, result) {
3279
- if (stale()) {
3280
- return;
3281
- }
3282
- if (error) {
3360
+ function fail(error) {
3361
+ if (!stale()) {
3283
3362
  rejectResult(error);
3284
- } else {
3363
+ releaseWriter();
3364
+ if (!(isErrorObject(error) && error.codecImportFailed)) {
3365
+ onTaskFinished();
3366
+ }
3367
+ }
3368
+ }
3369
+
3370
+ function succeed(result) {
3371
+ if (!stale()) {
3285
3372
  resolveResult(result);
3373
+ releaseWriter();
3374
+ onTaskFinished();
3286
3375
  }
3376
+ }
3377
+
3378
+ function releaseWriter() {
3287
3379
  if (writer) {
3288
3380
  writer.releaseLock();
3289
3381
  }
3290
- if (!(error && error.codecImportFailed)) {
3291
- onTaskFinished();
3382
+ }
3383
+ }
3384
+
3385
+ function getResponseError(errorData, errorValue) {
3386
+ const { message, stack, code, name, outputSize, cause, codecImportFailed } = errorData;
3387
+ let responseError;
3388
+ if (errorValue) {
3389
+ responseError = errorValue.value;
3390
+ } else {
3391
+ responseError = Object.assign(new Error(message), { stack, code, name });
3392
+ if (cause) {
3393
+ responseError.cause = Object.assign(new Error(cause.message), { name: cause.name });
3394
+ }
3395
+ }
3396
+ if (isErrorObject(responseError)) {
3397
+ try {
3398
+ if (outputSize !== UNDEFINED_VALUE) {
3399
+ responseError.outputSize = outputSize;
3400
+ }
3401
+ if (codecImportFailed) {
3402
+ responseError.codecImportFailed = true;
3403
+ }
3404
+ if (errorValue) {
3405
+ if (responseError.name !== name) {
3406
+ responseError.name = name;
3407
+ }
3408
+ if (responseError.code !== code) {
3409
+ responseError.code = code;
3410
+ }
3411
+ }
3412
+ } catch {
3413
+ // ignored
3292
3414
  }
3293
3415
  }
3416
+ return responseError;
3294
3417
  }
3295
3418
 
3296
3419
  /*
@@ -3567,10 +3690,13 @@
3567
3690
 
3568
3691
 
3569
3692
  const ERR_HTTP_STATUS = "HTTP error ";
3693
+ const MIN_SUCCESS_HTTP_STATUS = 200;
3694
+ const MAX_SUCCESS_HTTP_STATUS = 299;
3570
3695
  const ERR_HTTP_RANGE = "HTTP Range not supported";
3571
3696
  const ERR_HTTP_RESOURCE_CHANGED = "HTTP resource changed";
3572
3697
  const ERR_ITERATOR_COMPLETED_TOO_SOON = "Writer iterator completed too soon";
3573
3698
  const ERR_WRITER_NOT_INITIALIZED = "Writer not initialized";
3699
+ const ERR_WRITER_SIZE_NOT_WRITABLE = "Invalid writer (size must be writable)";
3574
3700
 
3575
3701
  const CONTENT_TYPE_TEXT_PLAIN = "text/plain";
3576
3702
  const HTTP_HEADER_CONTENT_LENGTH = "Content-Length";
@@ -3762,6 +3888,7 @@
3762
3888
  const reader = this;
3763
3889
  const { sourceBlob, size } = reader;
3764
3890
  const { offset = 0, size: readSize = size - offset } = options || {};
3891
+ // deno-lint-ignore valid-typeof
3765
3892
  if (typeof sourceBlob.stream == FUNCTION_TYPE) {
3766
3893
  if (!offset && readSize >= size) {
3767
3894
  return toCompatibleReadable(sourceBlob.stream());
@@ -4148,7 +4275,7 @@
4148
4275
 
4149
4276
  async function sendFetchRequest(method, { fetch: fetchFunction = fetch, options, url }, headers) {
4150
4277
  const response = await fetchFunction(url, Object.assign({}, options, { method, headers }));
4151
- if (response.status < 400) {
4278
+ if (response.status >= MIN_SUCCESS_HTTP_STATUS && response.status <= MAX_SUCCESS_HTTP_STATUS) {
4152
4279
  return response;
4153
4280
  } else {
4154
4281
  throw response.status == 416 ? new Error(ERR_HTTP_RANGE) : new Error(ERR_HTTP_STATUS + (response.statusText || response.status));
@@ -4159,7 +4286,7 @@
4159
4286
  return new Promise((resolve, reject) => {
4160
4287
  const request = new XMLHttpRequest();
4161
4288
  request.addEventListener("load", () => {
4162
- if (request.status < 400) {
4289
+ if (request.status >= MIN_SUCCESS_HTTP_STATUS && request.status <= MAX_SUCCESS_HTTP_STATUS) {
4163
4290
  const headers = [];
4164
4291
  request.getAllResponseHeaders().trim().split(/[\r\n]+/).forEach(header => {
4165
4292
  const splitHeader = header.trim().split(/\s*:\s*/);
@@ -4463,8 +4590,10 @@
4463
4590
  writable: toCompatibleWritable(writer)
4464
4591
  };
4465
4592
  }
4466
- if (writer.size === UNDEFINED_VALUE) {
4467
- writer.size = 0;
4593
+ try {
4594
+ writer.size = writer.size === UNDEFINED_VALUE ? 0 : writer.size;
4595
+ } catch {
4596
+ throw new Error(ERR_WRITER_SIZE_NOT_WRITABLE);
4468
4597
  }
4469
4598
  return writer;
4470
4599
  }
@@ -4512,6 +4641,45 @@
4512
4641
  }
4513
4642
  }
4514
4643
 
4644
+ /*
4645
+ Copyright (c) 2026 Gildas Lormeau. All rights reserved.
4646
+
4647
+ Redistribution and use in source and binary forms, with or without
4648
+ modification, are permitted provided that the following conditions are met:
4649
+
4650
+ 1. Redistributions of source code must retain the above copyright notice,
4651
+ this list of conditions and the following disclaimer.
4652
+
4653
+ 2. Redistributions in binary form must reproduce the above copyright
4654
+ notice, this list of conditions and the following disclaimer in
4655
+ the documentation and/or other materials provided with the distribution.
4656
+
4657
+ 3. The names of the authors may not be used to endorse or promote products
4658
+ derived from this software without specific prior written permission.
4659
+
4660
+ THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
4661
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
4662
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
4663
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
4664
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
4665
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
4666
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
4667
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
4668
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
4669
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4670
+ */
4671
+
4672
+
4673
+ function addWarning(warnings, reason, filename) {
4674
+ if (!warnings.some(warning => warning.reason == reason)) {
4675
+ const warning = { reason };
4676
+ if (filename !== UNDEFINED_VALUE) {
4677
+ warning.filename = filename;
4678
+ }
4679
+ warnings.push(warning);
4680
+ }
4681
+ }
4682
+
4515
4683
  /*
4516
4684
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
4517
4685
 
@@ -4540,6 +4708,7 @@
4540
4708
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4541
4709
  */
4542
4710
 
4711
+
4543
4712
  const PROPERTY_NAME_FILENAME = "filename";
4544
4713
  const PROPERTY_NAME_RAW_FILENAME = "rawFilename";
4545
4714
  const PROPERTY_NAME_COMMENT = "comment";
@@ -4664,6 +4833,38 @@
4664
4833
 
4665
4834
  }
4666
4835
 
4836
+ const INTERPRETED_EXTRA_FIELD_TYPES = new Set([
4837
+ EXTRAFIELD_TYPE_ZIP64,
4838
+ EXTRAFIELD_TYPE_AES,
4839
+ EXTRAFIELD_TYPE_NTFS,
4840
+ EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP,
4841
+ EXTRAFIELD_TYPE_UNICODE_PATH,
4842
+ EXTRAFIELD_TYPE_UNICODE_COMMENT,
4843
+ EXTRAFIELD_TYPE_USDZ,
4844
+ EXTRAFIELD_TYPE_INFOZIP,
4845
+ EXTRAFIELD_TYPE_UNIX,
4846
+ EXTRAFIELD_TYPE_UNIX_TYPE1,
4847
+ EXTRAFIELD_TYPE_PKWARE_UNIX
4848
+ ]);
4849
+
4850
+ function getUserExtraField(extraField) {
4851
+ if (extraField) {
4852
+ const userExtraField = new Map();
4853
+ extraField.forEach((field, type) => {
4854
+ if (!INTERPRETED_EXTRA_FIELD_TYPES.has(type)) {
4855
+ userExtraField.set(type, field.data);
4856
+ }
4857
+ });
4858
+ if (userExtraField.size) {
4859
+ return userExtraField;
4860
+ }
4861
+ }
4862
+ }
4863
+
4864
+ function getEncryptionOverhead(encrypted, zipCrypto, encryptionStrength) {
4865
+ return encrypted ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
4866
+ }
4867
+
4667
4868
  /*
4668
4869
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
4669
4870
 
@@ -4723,6 +4924,8 @@
4723
4924
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
4724
4925
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
4725
4926
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
4927
+ const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
4928
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = "mismatched local file header (filename)";
4726
4929
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
4727
4930
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
4728
4931
  const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
@@ -4793,7 +4996,7 @@
4793
4996
  }
4794
4997
  }
4795
4998
  if (rejectAmbiguousEndOfDirectory && endOfDirectoryReachingEndCount > 1) {
4796
- throwAmbiguousArchive("multiple end of central directory records");
4999
+ throwAmbiguousArchive(WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY);
4797
5000
  }
4798
5001
  const endOfDirectoryView = getDataView(endOfDirectoryInfo);
4799
5002
  let directoryDataLength = getUint32$1(endOfDirectoryView, 12);
@@ -5091,7 +5294,8 @@
5091
5294
  const writer = new TransformStream();
5092
5295
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5093
5296
  arrayBufferPromise.catch(() => { });
5094
- await fileEntry.getData(writer, entry, zipReader.readRanges, options);
5297
+ await fileEntry.getData(writer, entry, zipReader.readRanges,
5298
+ Object.assign({}, options, { preventClose: false }));
5095
5299
  return arrayBufferPromise;
5096
5300
  };
5097
5301
  offset = endOffset;
@@ -5172,49 +5376,101 @@
5172
5376
  await reader.readable.cancel();
5173
5377
  }
5174
5378
  }
5379
+
5380
+ [SYMBOL_ASYNC_DISPOSE]() {
5381
+ return this.close();
5382
+ }
5175
5383
  }
5176
5384
 
5177
5385
  class ZipReaderStream {
5178
5386
 
5179
5387
  constructor(options = {}) {
5180
- const { readable, writable } = new TransformStream();
5181
- const gen = new ZipReader(readable, options).getEntriesGenerator();
5388
+ let sourceController;
5389
+ const { readable, writable } = new TransformStream({
5390
+ start(controller) {
5391
+ sourceController = controller;
5392
+ }
5393
+ });
5394
+ const zipReader = new ZipReader(readable, options);
5395
+ const gen = zipReader.getEntriesGenerator();
5396
+ const pendingEntries = new Set();
5182
5397
  this.readable = new ReadableStream({
5183
5398
  async pull(controller) {
5184
5399
  const { done, value } = await gen.next();
5185
5400
  if (done)
5186
5401
  return controller.close();
5187
- const entryReadable = (function () {
5188
- const { readable, writable } = new TransformStream();
5189
- if (value.getData) {
5190
- getData();
5191
- return readable;
5192
- }
5193
-
5194
- async function getData() {
5195
- try {
5196
- await value.getData(writable);
5197
- } catch (error) {
5198
- try {
5199
- await writable.abort(error);
5200
- } catch {
5201
- // ignored
5202
- }
5203
- }
5204
- }
5205
- })();
5402
+ const entryStream = createEntryStream(value, pendingEntries);
5206
5403
  const chunk = {
5207
5404
  ...value,
5208
- readable: entryReadable
5405
+ readable: entryStream.readable
5209
5406
  };
5210
5407
  delete chunk.getData;
5408
+ Object.defineProperties(chunk, {
5409
+ localDirectory: {
5410
+ get: () => value.localDirectory,
5411
+ enumerable: true
5412
+ },
5413
+ warnings: {
5414
+ get: () => value.warnings,
5415
+ enumerable: true
5416
+ }
5417
+ });
5211
5418
  controller.enqueue(chunk);
5419
+ },
5420
+ async cancel(reason) {
5421
+ const entryStreams = Array.from(pendingEntries);
5422
+ pendingEntries.clear();
5423
+ sourceController.error(reason);
5424
+ await Promise.allSettled(entryStreams.map(entryStream => entryStream.cancel(reason)));
5425
+ await Promise.allSettled([gen.return(), zipReader.close()]);
5212
5426
  }
5213
5427
  });
5214
5428
  this.writable = writable;
5215
5429
  }
5216
5430
  }
5217
5431
 
5432
+ function createEntryStream(entry, pendingEntries) {
5433
+ const { readable, writable } = new TransformStream();
5434
+ let dataReader;
5435
+ const entryStream = {
5436
+ cancel: async reason => {
5437
+ pendingEntries.delete(entryStream);
5438
+ await (dataReader ? dataReader.cancel(reason) : readable.cancel(reason));
5439
+ }
5440
+ };
5441
+ entryStream.readable = new ReadableStream({
5442
+ async pull(controller) {
5443
+ if (!dataReader) {
5444
+ dataReader = readable.getReader();
5445
+ pendingEntries.add(entryStream);
5446
+ getData();
5447
+ }
5448
+ const { done, value } = await dataReader.read();
5449
+ if (done) {
5450
+ controller.close();
5451
+ } else {
5452
+ controller.enqueue(value);
5453
+ }
5454
+ },
5455
+ cancel: reason => entryStream.cancel(reason)
5456
+ }, { highWaterMark: 0 });
5457
+ return entryStream;
5458
+
5459
+ async function getData() {
5460
+ try {
5461
+ await entry.getData(writable, { preventClose: false });
5462
+ } catch (error) {
5463
+ try {
5464
+ await writable.abort(error);
5465
+ } catch {
5466
+ // ignored
5467
+ }
5468
+ } finally {
5469
+ pendingEntries.delete(entryStream);
5470
+ }
5471
+ }
5472
+ }
5473
+
5218
5474
  async function isZipFile(reader, options = {}) {
5219
5475
  reader = new GenericReader(reader);
5220
5476
  await initStream(reader);
@@ -5274,7 +5530,9 @@
5274
5530
  const dataView = getDataView(dataArray);
5275
5531
  let password = getOptionValue$1(zipEntry, options, OPTION_PASSWORD);
5276
5532
  let rawPassword = getOptionValue$1(zipEntry, options, OPTION_RAW_PASSWORD);
5277
- const passThrough = getOptionValue$1(zipEntry, options, OPTION_PASS_THROUGH);
5533
+ const passThrough = checkPassThroughOption(getOptionValue$1(zipEntry, options, OPTION_PASS_THROUGH));
5534
+ const passThroughCompression = Boolean(passThrough);
5535
+ const passThroughEncryption = passThrough === true;
5278
5536
  checkPasswordOption(password, rawPassword);
5279
5537
  password = password && password.length && password;
5280
5538
  rawPassword = rawPassword && rawPassword.length && rawPassword;
@@ -5295,7 +5553,9 @@
5295
5553
  const checkLocalDirectoryOption = getOptionValue$1(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
5296
5554
  const entryStrictness = getStrictness(options, zipEntry.options);
5297
5555
  const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
5298
- const checkLocalFilename = getCheckLocalFilename(checkLocalDirectoryOption, entryStrictness);
5556
+ const checkLocalFilenameOption = getOptionValue$1(zipEntry, options, OPTION_CHECK_LOCAL_FILENAME);
5557
+ const checkLocalFilename = getCheckLocalFilename(
5558
+ checkLocalFilenameOption === UNDEFINED_VALUE ? checkLocalDirectoryOption : checkLocalFilenameOption, entryStrictness);
5299
5559
  let rawLocalFilename = EMPTY_UINT8_ARRAY;
5300
5560
  if (checkLocalFilename && (filenameLength || extraFieldLength)) {
5301
5561
  const trailingDataArray = await readUint8Array(reader, localHeaderOffset + HEADER_SIZE, filenameLength + extraFieldLength);
@@ -5326,16 +5586,17 @@
5326
5586
  if (gid !== UNDEFINED_VALUE && fileEntry.gid === UNDEFINED_VALUE) {
5327
5587
  fileEntry.gid = gid;
5328
5588
  }
5329
- const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
5589
+ const checkPasswordOnly = getOptionValue$1(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
5590
+ const encrypted = zipEntry.encrypted && localDirectory.encrypted && (!passThroughEncryption || checkPasswordOnly);
5330
5591
  const zipCrypto = encrypted && !extraFieldAES;
5331
- if (!passThrough) {
5592
+ if (!passThroughEncryption) {
5332
5593
  fileEntry.zipCrypto = zipCrypto;
5333
5594
  }
5334
5595
  if (encrypted && (localDirectory.rawBitFlag & BITFLAG_STRONG_ENCRYPTION) == BITFLAG_STRONG_ENCRYPTION) {
5335
5596
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION);
5336
5597
  }
5337
- const registeredCodec = passThrough ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
5338
- if (compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64 && !registeredCodec && !passThrough) {
5598
+ const registeredCodec = passThroughCompression ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
5599
+ if (compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64 && !registeredCodec && !passThroughCompression) {
5339
5600
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
5340
5601
  }
5341
5602
  if (encrypted) {
@@ -5352,15 +5613,16 @@
5352
5613
  const readable = toCompatibleReadable(reader.createReadable({ offset: dataOffset, size }));
5353
5614
  const signal = checkSignalOption(getOptionValue$1(zipEntry, options, OPTION_SIGNAL));
5354
5615
  throwIfAborted(signal);
5355
- const checkPasswordOnly = getOptionValue$1(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
5356
5616
  let checkOverlappingEntry = getOptionValue$1(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
5357
5617
  const checkOverlappingEntryOnly = getOptionValue$1(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
5358
5618
  if (checkOverlappingEntryOnly) {
5359
5619
  checkOverlappingEntry = true;
5360
5620
  }
5361
5621
  const { onstart, onprogress, onend } = options;
5362
- const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThrough;
5363
- const outputSize = passThrough ? compressedSize : uncompressedSize;
5622
+ const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThroughCompression;
5623
+ const outputSize = passThroughCompression ?
5624
+ compressedSize - getEncryptionOverhead(encrypted, zipCrypto, extraFieldAES && extraFieldAES.strength) :
5625
+ uncompressedSize;
5364
5626
  const deflate64 = compressionMethod == COMPRESSION_METHOD_DEFLATE_64;
5365
5627
  let useCompressionStream = getOptionValue$1(zipEntry, options, OPTION_USE_COMPRESSION_STREAM);
5366
5628
  if (deflate64) {
@@ -5369,7 +5631,7 @@
5369
5631
  const checkCrc32Option = getOptionValue$1(zipEntry, options, OPTION_CHECK_CRC32);
5370
5632
  const checkCrc32 = (checkCrc32Option === UNDEFINED_VALUE ?
5371
5633
  getOptionValue$1(zipEntry, options, OPTION_CHECK_SIGNATURE) :
5372
- checkCrc32Option) && !passThrough &&
5634
+ checkCrc32Option) && !passThroughCompression &&
5373
5635
  (!encrypted || zipCrypto || (extraFieldAES && extraFieldAES.vendorVersion == VENDOR_VERSION_AE_1$1));
5374
5636
  const workerOptions = {
5375
5637
  options: {
@@ -5413,7 +5675,7 @@
5413
5675
  readRanges
5414
5676
  });
5415
5677
  }
5416
- let writable, abortError;
5678
+ let writable, abortError, aborted;
5417
5679
  try {
5418
5680
  if (!checkOverlappingEntryOnly) {
5419
5681
  if (checkPasswordOnly) {
@@ -5423,24 +5685,28 @@
5423
5685
  await initStream(writer, getDecodableOutputSize(outputSize, compressedSize, compressed));
5424
5686
  ({ writable } = writer);
5425
5687
  const { outputSize: writtenSize } = await runWorker({ readable, writable }, workerOptions);
5426
- writer.size += writtenSize;
5427
5688
  if (writtenSize != outputSize) {
5428
- throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
5689
+ throw Object.assign(new Error(ERR_INVALID_UNCOMPRESSED_SIZE), { outputSize: writtenSize });
5429
5690
  }
5691
+ writer.size += writtenSize;
5430
5692
  }
5431
5693
  } catch (error) {
5432
- if (error.outputSize !== UNDEFINED_VALUE) {
5694
+ const { outputSize: failedOutputSize } = workerOptions;
5695
+ if (failedOutputSize !== UNDEFINED_VALUE) {
5696
+ writer.size += failedOutputSize;
5697
+ } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5433
5698
  writer.size += error.outputSize;
5434
5699
  }
5435
- if (!checkPasswordOnly || error.message != ERR_ABORT_CHECK_PASSWORD) {
5700
+ if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
5436
5701
  abortError = error;
5702
+ aborted = true;
5437
5703
  throw error;
5438
5704
  }
5439
5705
  } finally {
5440
5706
  const preventClose = !ownsWritable(writer) && getOptionValue$1(zipEntry, options, OPTION_PREVENT_CLOSE);
5441
5707
  if (!preventClose && writable && !writable.locked) {
5442
5708
  const writableWriter = writable.getWriter();
5443
- if (abortError) {
5709
+ if (aborted) {
5444
5710
  try {
5445
5711
  await writableWriter.abort(abortError);
5446
5712
  } catch {
@@ -6092,7 +6358,7 @@
6092
6358
  if (checkLocalFilename && !maskedLocalDirectory &&
6093
6359
  (rawLocalFilename.length != rawFilename.length ||
6094
6360
  rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
6095
- reportAmbiguity(reject, warnings, "mismatched local file header (filename)");
6361
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME);
6096
6362
  }
6097
6363
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
6098
6364
  reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
@@ -6117,16 +6383,6 @@
6117
6383
  }
6118
6384
  }
6119
6385
 
6120
- function addWarning(warnings, reason, filename) {
6121
- if (!warnings.some(warning => warning.reason == reason)) {
6122
- const warning = { reason };
6123
- if (filename !== UNDEFINED_VALUE) {
6124
- warning.filename = filename;
6125
- }
6126
- warnings.push(warning);
6127
- }
6128
- }
6129
-
6130
6386
  function throwAmbiguousArchive(reason) {
6131
6387
  const error = new Error(ERR_AMBIGUOUS_ARCHIVE);
6132
6388
  error.reason = reason;
@@ -6206,7 +6462,9 @@
6206
6462
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG: WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
6207
6463
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD: WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
6208
6464
  WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES: WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES,
6465
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME: WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
6209
6466
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY: WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
6467
+ WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY: WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
6210
6468
  WARNING_PREPENDED_CENTRAL_DIRECTORY: WARNING_PREPENDED_CENTRAL_DIRECTORY,
6211
6469
  WARNING_PREPENDED_DATA: WARNING_PREPENDED_DATA,
6212
6470
  WARNING_TRAILING_CENTRAL_DIRECTORY_DATA: WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
@@ -6259,7 +6517,7 @@
6259
6517
  const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
6260
6518
  const ERR_UNSUPPORTED_ENCRYPTION_USDZ = "Encryption is not supported in USDZ files";
6261
6519
  const ERR_UNSUPPORTED_SPLIT_USDZ = "Split zip files are not supported in USDZ files";
6262
- const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set";
6520
+ const ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH = "Encryption is not supported when the 'passThrough' option is set to true (use 'compressed' instead)";
6263
6521
  const ERR_INVALID_EXTRAFIELD = "Invalid extra field (must be a Map)";
6264
6522
  const ERR_INVALID_EXTRAFIELD_TYPE = "Invalid extra field type (must be integer 0..65535)";
6265
6523
  const ERR_INVALID_EXTRAFIELD_DATA_TYPE = "Invalid extra field data (must be a Uint8Array)";
@@ -6271,6 +6529,7 @@
6271
6529
  const ERR_UNSUPPORTED_FORMAT = "Zip64 is not supported (set the 'zip64' option to 'true')";
6272
6530
  const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
6273
6531
  const ERR_UNDEFINED_COMPRESSION_METHOD = "Undefined compression method";
6532
+ const ERR_UNDEFINED_CRC32 = "Undefined CRC32";
6274
6533
  const ERR_UNDETERMINED_SIZE = "Undetermined size";
6275
6534
  const ERR_UNDEFINED_READER = "Undefined reader";
6276
6535
  const ERR_INVALID_READER = "Invalid reader (must be a Reader instance, a ReadableStream instance, or an object with a 'readable' property)";
@@ -6284,14 +6543,21 @@
6284
6543
  const ERR_INVALID_MSDOS_DATA = "Invalid msdosAttributes (must be an object with boolean flags)";
6285
6544
  const ERR_INVALID_LEVEL = "Invalid level (must be integer 0..9)";
6286
6545
  const ERR_INVALID_SIGNATURE_DATA = "Signature data exceeds 64KB";
6546
+ const ERR_INVALID_ENTRY = "Invalid entry option (must be an entry returned by ZipReader#getEntries())";
6547
+ const ERR_ZIP_CRYPTO_LAST_MOD_DATE = "The last modification date of an entry encrypted with ZipCrypto cannot be changed when passThrough is set";
6548
+ const WARNING_COMPRESSION_UNAVAILABLE = "compression unavailable";
6549
+ const WARNING_CLAMPED_LAST_MODIFICATION_DATE = "clamped last modification date";
6287
6550
 
6288
6551
  const EXTRAFIELD_DATA_AES = new Uint8Array([0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00]);
6289
6552
  const EXTRAFIELD_OFFSET_AES_VENDOR_VERSION = 4;
6290
6553
  const EXTRAFIELD_OFFSET_AES_COMPRESSION_METHOD = 9;
6291
6554
  const EXTRAFIELD_USDZ_MAX_LENGTH = 67;
6555
+ const MIN_PRINTABLE_ASCII_CHARACTER_CODE = 0x20;
6556
+ const MAX_PRINTABLE_ASCII_CHARACTER_CODE = 0x7e;
6292
6557
  const VENDOR_VERSION_AE_1 = 1;
6293
6558
  const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
6294
6559
  const UNIX_EXTRA_FIELD_TYPE = "unix";
6560
+ const LEVEL_BY_BITFLAG_LEVEL = [8, 9, 5, 3];
6295
6561
  const MAX_LEVEL = 9;
6296
6562
 
6297
6563
  let workers = 0;
@@ -6318,9 +6584,11 @@
6318
6584
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
6319
6585
  pendingAddFileCalls: new Set(),
6320
6586
  pendingErrors: [],
6587
+ warnings: [],
6321
6588
  bufferedWrites: 0,
6322
6589
  directWrites: 0,
6323
- lastFileEntry: UNDEFINED_VALUE
6590
+ lastFileEntry: UNDEFINED_VALUE,
6591
+ archiveClosed: false
6324
6592
  });
6325
6593
  }
6326
6594
 
@@ -6485,6 +6753,9 @@
6485
6753
  const zipWriter = this;
6486
6754
  const { pendingAddFileCalls, writer } = this;
6487
6755
  const { writable } = writer;
6756
+ if (zipWriter.archiveClosed) {
6757
+ return getWriterData(writer);
6758
+ }
6488
6759
  if (!(comment instanceof Uint8Array)) {
6489
6760
  throw new Error(ERR_INVALID_COMMENT_TYPE);
6490
6761
  }
@@ -6495,7 +6766,7 @@
6495
6766
  await Promise.allSettled(Array.from(pendingAddFileCalls));
6496
6767
  }
6497
6768
  await Promise.allSettled(zipWriter.pendingErrors.map(watcher => watcher.recorded));
6498
- const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.error && !watcher.observed);
6769
+ const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.failed && !watcher.observed);
6499
6770
  if (unobservedWatchers.length) {
6500
6771
  const unobservedErrors = unobservedWatchers.map(watcher => watcher.error);
6501
6772
  unobservedWatchers.forEach(watcher => watcher.observed = true);
@@ -6508,11 +6779,16 @@
6508
6779
  throw error;
6509
6780
  }
6510
6781
  await closeFile(zipWriter, comment, options);
6782
+ zipWriter.archiveClosed = true;
6511
6783
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
6512
6784
  if (!preventClose) {
6513
6785
  await writable.getWriter().close();
6514
6786
  }
6515
- return writer.getData ? writer.getData() : writable;
6787
+ return getWriterData(writer);
6788
+ }
6789
+
6790
+ [SYMBOL_ASYNC_DISPOSE]() {
6791
+ return this.close();
6516
6792
  }
6517
6793
  }
6518
6794
 
@@ -6591,11 +6867,16 @@
6591
6867
  }
6592
6868
  }
6593
6869
 
6870
+ function getWriterData(writer) {
6871
+ return writer.getData ? writer.getData() : writer.writable;
6872
+ }
6873
+
6594
6874
  function watchPromiseError(zipWriter, promise) {
6595
6875
  const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
6596
6876
  const watcher = {};
6597
6877
  watchedPromise.watcher = watcher;
6598
- watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE, error => watcher.error = error);
6878
+ watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE,
6879
+ error => Object.assign(watcher, { failed: true, error }));
6599
6880
  zipWriter.pendingErrors.push(watcher);
6600
6881
  return watchedPromise;
6601
6882
  }
@@ -6609,6 +6890,14 @@
6609
6890
 
6610
6891
  async function addFileEntry(zipWriter, name, reader, options) {
6611
6892
  options = Object.assign({}, options);
6893
+ const entry = options[OPTION_ENTRY];
6894
+ if (entry !== UNDEFINED_VALUE) {
6895
+ const { entryOptions, passThroughOptions } = getSourceEntryOptions(entry,
6896
+ checkPassThroughOption(getOptionValue(zipWriter, options, OPTION_PASS_THROUGH)),
6897
+ getOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE));
6898
+ delete options[OPTION_ENTRY];
6899
+ options = Object.assign(entryOptions, passThroughOptions, options);
6900
+ }
6612
6901
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
6613
6902
  name += DIRECTORY_SIGNATURE;
6614
6903
  }
@@ -6673,21 +6962,33 @@
6673
6962
  try {
6674
6963
  const { resolvedOptions } = metadataInfo;
6675
6964
  if (resolvedOptions.level != 0 && resolvedOptions.compressionMethod === UNDEFINED_VALUE &&
6676
- !resolvedOptions.passThrough && !(await supportsDeflate(getConfiguration()))) {
6965
+ !resolvedOptions.passThroughCompression && !(await supportsDeflate(getConfiguration()))) {
6677
6966
  resolvedOptions.level = 0;
6967
+ addWarning(zipWriter.warnings, WARNING_COMPRESSION_UNAVAILABLE, name);
6678
6968
  }
6679
6969
  const sizesInfo = await resolveSizes(zipWriter, reader, metadataInfo, options);
6680
6970
  ({ reader } = sizesInfo);
6681
6971
  const diskOffset = getDiskOffset(zipWriter.writer);
6682
6972
  const diskNumber = getDiskNumber(zipWriter.writer);
6973
+ let crc32 = options.crc32 === UNDEFINED_VALUE ? options[PROPERTY_NAME_SIGNATURE] : options.crc32;
6974
+ const storesAE2 = sizesInfo.resolvedOptions.encrypted && !resolvedOptions.zipCrypto;
6975
+ if (resolvedOptions.passThroughCompression && !resolvedOptions.passThroughEncryption && storesAE2) {
6976
+ crc32 = UNDEFINED_VALUE;
6977
+ }
6978
+ if (resolvedOptions.passThroughCompression && reader && !storesAE2 && crc32 === UNDEFINED_VALUE) {
6979
+ throw new Error(ERR_UNDEFINED_CRC32);
6980
+ }
6683
6981
  options = Object.assign({}, options, attributesInfo.resolvedOptions, metadataInfo.resolvedOptions, sizesInfo.resolvedOptions, {
6684
6982
  signature: options[PROPERTY_NAME_SIGNATURE],
6685
- crc32: options.crc32 === UNDEFINED_VALUE ? options[PROPERTY_NAME_SIGNATURE] : options.crc32,
6983
+ crc32,
6686
6984
  offset: zipWriter.offset - diskOffset,
6687
6985
  diskNumberStart: diskNumber,
6688
6986
  [OPTION_USDZ]: zipWriter.options[OPTION_USDZ]
6689
6987
  });
6690
6988
  const headerInfo = getHeaderInfo(options);
6989
+ if (headerInfo.lastModDateClamped) {
6990
+ addWarning(zipWriter.warnings, WARNING_CLAMPED_LAST_MODIFICATION_DATE, name);
6991
+ }
6691
6992
  const dataDescriptorInfo = getDataDescriptorInfo(options);
6692
6993
  const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
6693
6994
  fileEntry = await getFileEntry(zipWriter, name, reader, {
@@ -6714,6 +7015,95 @@
6714
7015
  return new Entry(fileEntry);
6715
7016
  }
6716
7017
 
7018
+ function getSourceEntryOptions(entry, passThrough, lastModDateOverride) {
7019
+ if (entry === null || typeof entry != OBJECT_TYPE || Array.isArray(entry)) {
7020
+ throw new Error(ERR_INVALID_ENTRY);
7021
+ }
7022
+ const {
7023
+ externalFileAttributes,
7024
+ versionMadeBy,
7025
+ comment,
7026
+ lastModDate,
7027
+ rawLastModDate,
7028
+ creationDate,
7029
+ lastAccessDate,
7030
+ uncompressedSize,
7031
+ encrypted,
7032
+ zipCrypto,
7033
+ crc32,
7034
+ compressionMethod,
7035
+ extraFieldAES,
7036
+ extraFieldUnix,
7037
+ internalFileAttributes,
7038
+ extraField,
7039
+ bitFlag,
7040
+ directory,
7041
+ uid,
7042
+ gid
7043
+ } = entry;
7044
+ const entryOptions = {
7045
+ externalFileAttributes,
7046
+ versionMadeBy,
7047
+ comment,
7048
+ lastModDate,
7049
+ creationDate,
7050
+ lastAccessDate,
7051
+ internalFileAttributes,
7052
+ directory
7053
+ };
7054
+ if (bitFlag && bitFlag.languageEncodingFlag) {
7055
+ entryOptions[OPTION_USE_UNICODE_FILE_NAMES] = true;
7056
+ }
7057
+ const userExtraField = getUserExtraField(extraField);
7058
+ if (userExtraField) {
7059
+ entryOptions[PROPERTY_NAME_EXTRA_FIELD] = userExtraField;
7060
+ }
7061
+ if (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE) {
7062
+ Object.assign(entryOptions, {
7063
+ uid,
7064
+ gid,
7065
+ unixExtraFieldType: extraFieldUnix ? UNIX_EXTRA_FIELD_TYPE : INFOZIP_EXTRA_FIELD_TYPE
7066
+ });
7067
+ }
7068
+ const passThroughOptions = {};
7069
+ if (passThrough && !directory) {
7070
+ Object.assign(passThroughOptions, {
7071
+ uncompressedSize,
7072
+ crc32,
7073
+ compressionMethod
7074
+ });
7075
+ if (passThrough !== PASS_THROUGH_COMPRESSED) {
7076
+ Object.assign(passThroughOptions, {
7077
+ encrypted,
7078
+ zipCrypto,
7079
+ encryptionStrength: extraFieldAES ? extraFieldAES.strength : UNDEFINED_VALUE
7080
+ });
7081
+ }
7082
+ if (bitFlag) {
7083
+ passThroughOptions.dataDescriptor = bitFlag.dataDescriptor;
7084
+ passThroughOptions[OPTION_LEVEL] = LEVEL_BY_BITFLAG_LEVEL[bitFlag.level];
7085
+ }
7086
+ if (lastModDateOverride === UNDEFINED_VALUE) {
7087
+ passThroughOptions.rawLastModDate = rawLastModDate;
7088
+ } else if (passThrough !== PASS_THROUGH_COMPRESSED && zipCrypto && (!bitFlag || bitFlag.dataDescriptor) &&
7089
+ lastModDateOverride instanceof Date &&
7090
+ getDosTimeHighByte(lastModDateOverride) != ((rawLastModDate >>> 8) & MAX_8_BITS)) {
7091
+ throw new Error(ERR_ZIP_CRYPTO_LAST_MOD_DATE);
7092
+ }
7093
+ }
7094
+ return { entryOptions, passThroughOptions };
7095
+ }
7096
+
7097
+ function getDosTimeHighByte(lastModDate) {
7098
+ let dosLastModDate = new Date(Math.ceil(Math.floor(lastModDate.getTime() / 1000) / 2) * 2000);
7099
+ if (dosLastModDate < MIN_DATE) {
7100
+ dosLastModDate = MIN_DATE;
7101
+ } else if (dosLastModDate > MAX_DATE) {
7102
+ dosLastModDate = MAX_DATE;
7103
+ }
7104
+ return ((dosLastModDate.getHours() << 3) | (dosLastModDate.getMinutes() >> 3)) & MAX_8_BITS;
7105
+ }
7106
+
6717
7107
  function resolveAttributes(zipWriter, name, options) {
6718
7108
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
6719
7109
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
@@ -6867,7 +7257,9 @@
6867
7257
  const lastAccessDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
6868
7258
  const creationDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
6869
7259
  const internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, 0);
6870
- const passThrough = getOptionValue(zipWriter, options, OPTION_PASS_THROUGH);
7260
+ const passThrough = checkPassThroughOption(getOptionValue(zipWriter, options, OPTION_PASS_THROUGH));
7261
+ const passThroughCompression = Boolean(passThrough);
7262
+ const passThroughEncryption = passThrough === true;
6871
7263
  const password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
6872
7264
  const rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
6873
7265
  checkPasswordOption(password, rawPassword);
@@ -6883,10 +7275,11 @@
6883
7275
  const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, true);
6884
7276
  const signal = checkSignalOption(getOptionValue(zipWriter, options, OPTION_SIGNAL));
6885
7277
  throwIfAborted(signal);
6886
- const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES, true);
7278
+ const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES,
7279
+ !isPrintableASCIIText(rawFilename) || !isPrintableASCIIText(rawComment));
6887
7280
  const compressionMethod = getOptionValue(zipWriter, options, PROPERTY_NAME_COMPRESSION_METHOD);
6888
- const registeredCodec = passThrough || compressionMethod === UNDEFINED_VALUE ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
6889
- if (!passThrough && compressionMethod !== UNDEFINED_VALUE &&
7281
+ const registeredCodec = passThroughCompression || compressionMethod === UNDEFINED_VALUE ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
7282
+ if (!passThroughCompression && compressionMethod !== UNDEFINED_VALUE &&
6890
7283
  compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE && !registeredCodec) {
6891
7284
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
6892
7285
  }
@@ -6900,15 +7293,15 @@
6900
7293
  level = 0;
6901
7294
  }
6902
7295
  }
6903
- if (passThrough) {
6904
- level = UNDEFINED_VALUE;
7296
+ if (passThroughCompression) {
7297
+ level = toNumber(options[OPTION_LEVEL]);
6905
7298
  }
6906
7299
  let useCompressionStream = getOptionValue(zipWriter, options, OPTION_USE_COMPRESSION_STREAM);
6907
7300
  let dataDescriptor = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR);
6908
7301
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
6909
7302
  dataDescriptor = false;
6910
7303
  }
6911
- if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThrough)) {
7304
+ if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThroughEncryption)) {
6912
7305
  dataDescriptor = true;
6913
7306
  }
6914
7307
  if (level !== UNDEFINED_VALUE && level != 6) {
@@ -6932,7 +7325,8 @@
6932
7325
  lastAccessDate,
6933
7326
  creationDate,
6934
7327
  internalFileAttributes,
6935
- passThrough,
7328
+ passThroughCompression,
7329
+ passThroughEncryption,
6936
7330
  password,
6937
7331
  rawPassword,
6938
7332
  encryptionStrength,
@@ -6993,7 +7387,7 @@
6993
7387
  }
6994
7388
 
6995
7389
  async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, options) {
6996
- if (metadata.passThrough && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
7390
+ if (metadata.passThroughCompression && !reader && !getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY)) {
6997
7391
  throw new Error(ERR_UNDEFINED_READER);
6998
7392
  }
6999
7393
  let contentSize;
@@ -7009,12 +7403,12 @@
7009
7403
  }
7010
7404
 
7011
7405
  function resolveEntrySizes(zipWriter, hasContent, contentSize, metadata, options) {
7012
- const { passThrough, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
7406
+ const { passThroughCompression, passThroughEncryption, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
7013
7407
  let { dataDescriptor, zip64, level, compressionMethod } = metadata;
7014
7408
  let maximumCompressedSize = 0;
7015
7409
  let uncompressedSize = 0;
7016
7410
  let unknownSize = false;
7017
- if (passThrough && hasContent) {
7411
+ if (passThroughCompression && hasContent) {
7018
7412
  uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
7019
7413
  if (uncompressedSize === UNDEFINED_VALUE) {
7020
7414
  throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
@@ -7025,17 +7419,17 @@
7025
7419
  }
7026
7420
  const zip64Enabled = zip64 === true;
7027
7421
  const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
7028
- if (hasContent && passThrough && !encrypted && getLength(password, rawPassword)) {
7422
+ if (hasContent && passThroughEncryption && !encrypted && getLength(password, rawPassword)) {
7029
7423
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH);
7030
7424
  }
7031
- const encryptedEntry = hasContent && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
7425
+ const encryptedEntry = hasContent && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThroughEncryption && encrypted));
7032
7426
  if (!hasContent) {
7033
7427
  level = 0;
7034
7428
  compressionMethod = COMPRESSION_METHOD_STORE;
7035
7429
  }
7036
- const encryptionOverhead = encryptedEntry ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
7430
+ const encryptionOverhead = getEncryptionOverhead(encryptedEntry, zipCrypto, encryptionStrength);
7037
7431
  if (hasContent) {
7038
- if (!passThrough) {
7432
+ if (!passThroughCompression) {
7039
7433
  if (contentSize === UNDEFINED_VALUE) {
7040
7434
  dataDescriptor = true;
7041
7435
  if (zip64 || zip64 === UNDEFINED_VALUE) {
@@ -7048,10 +7442,12 @@
7048
7442
  }
7049
7443
  } else {
7050
7444
  options.uncompressedSize = uncompressedSize;
7051
- maximumCompressedSize = contentSize === UNDEFINED_VALUE ? getMaximumCompressedSize(uncompressedSize) + encryptionOverhead : contentSize;
7445
+ maximumCompressedSize = contentSize === UNDEFINED_VALUE ?
7446
+ getMaximumCompressedSize(uncompressedSize) + encryptionOverhead :
7447
+ contentSize + (passThroughEncryption ? 0 : encryptionOverhead);
7052
7448
  }
7053
7449
  }
7054
- const emptyEntry = !encryptedEntry && (!hasContent || (contentSize === 0 && !passThrough)) && !isCompressed(compressionMethod, level);
7450
+ const emptyEntry = !encryptedEntry && (!hasContent || (contentSize === 0 && !passThroughCompression)) && !isCompressed(compressionMethod, level);
7055
7451
  if (emptyEntry && !zipCrypto && getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR) === UNDEFINED_VALUE) {
7056
7452
  dataDescriptor = false;
7057
7453
  }
@@ -7258,6 +7654,7 @@
7258
7654
  localExtraFieldZip64Length,
7259
7655
  rawExtraFieldExtendedTimestamp,
7260
7656
  extraFieldExtendedTimestampFlag,
7657
+ extraFieldExtendedTimestampTime,
7261
7658
  rawExtraFieldNTFS,
7262
7659
  rawExtraFieldUnix,
7263
7660
  rawExtraFieldAES,
@@ -7270,6 +7667,7 @@
7270
7667
  password,
7271
7668
  rawPassword,
7272
7669
  level,
7670
+ useUnicodeFileNames,
7273
7671
  zip64,
7274
7672
  zip64Enabled,
7275
7673
  zip64UncompressedSize,
@@ -7304,7 +7702,8 @@
7304
7702
  msdosAttributesRaw,
7305
7703
  msdosAttributes,
7306
7704
  useCompressionStream,
7307
- passThrough,
7705
+ passThroughCompression,
7706
+ passThroughEncryption,
7308
7707
  format,
7309
7708
  codecURI
7310
7709
  } = options;
@@ -7315,9 +7714,9 @@
7315
7714
  zip64Enabled,
7316
7715
  directory: Boolean(directory),
7317
7716
  executable: Boolean(executable),
7318
- filenameUTF8: true,
7717
+ filenameUTF8: Boolean(useUnicodeFileNames),
7319
7718
  rawFilename,
7320
- commentUTF8: true,
7719
+ commentUTF8: Boolean(useUnicodeFileNames),
7321
7720
  rawComment,
7322
7721
  rawExtraFieldZip64,
7323
7722
  localExtraFieldZip64Length,
@@ -7348,7 +7747,7 @@
7348
7747
  uncompressedSize
7349
7748
  } = options;
7350
7749
  let compressedSize = 0;
7351
- if (!passThrough) {
7750
+ if (!passThroughCompression) {
7352
7751
  uncompressedSize = 0;
7353
7752
  }
7354
7753
  const { writable } = writer;
@@ -7365,9 +7764,9 @@
7365
7764
  encryptionStrength,
7366
7765
  zipCrypto: encrypted && zipCrypto,
7367
7766
  passwordVerification: encrypted && zipCrypto && (rawLastModDate >> 8) & MAX_8_BITS,
7368
- computeCrc32: !passThrough,
7369
- compressed: compressed && !passThrough,
7370
- encrypted: encrypted && !passThrough,
7767
+ computeCrc32: !passThroughCompression,
7768
+ compressed: compressed && !passThroughCompression,
7769
+ encrypted: encrypted && !passThroughEncryption,
7371
7770
  useWebWorkers,
7372
7771
  useCompressionStream,
7373
7772
  transferStreams,
@@ -7382,7 +7781,7 @@
7382
7781
  const result = await runWorker({ readable, writable }, workerOptions);
7383
7782
  compressedSize = result.outputSize;
7384
7783
  writer.size += compressedSize;
7385
- if (!passThrough) {
7784
+ if (!passThroughCompression) {
7386
7785
  uncompressedSize = result.inputSize;
7387
7786
  if (!encrypted || zipCrypto) {
7388
7787
  crc32 = result.crc32;
@@ -7393,7 +7792,10 @@
7393
7792
  throw new Error(ERR_UNSUPPORTED_FORMAT);
7394
7793
  }
7395
7794
  } catch (error) {
7396
- if (error.outputSize !== UNDEFINED_VALUE) {
7795
+ const { outputSize: failedOutputSize } = workerOptions;
7796
+ if (failedOutputSize !== UNDEFINED_VALUE) {
7797
+ writer.size += failedOutputSize;
7798
+ } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
7397
7799
  writer.size += error.outputSize;
7398
7800
  }
7399
7801
  throw error;
@@ -7425,8 +7827,9 @@
7425
7827
  headerArray,
7426
7828
  headerView,
7427
7829
  signature: crc32,
7428
- crc32: encrypted && !zipCrypto && !passThrough ? UNDEFINED_VALUE : crc32,
7830
+ crc32: encrypted && !zipCrypto && !passThroughCompression ? UNDEFINED_VALUE : crc32,
7429
7831
  extraFieldExtendedTimestampFlag,
7832
+ extraFieldExtendedTimestampTime,
7430
7833
  zip64UncompressedSize,
7431
7834
  zip64CompressedSize
7432
7835
  });
@@ -7451,7 +7854,7 @@
7451
7854
  encryptionStrength,
7452
7855
  extendedTimestamp,
7453
7856
  ntfsTimestamp,
7454
- passThrough,
7857
+ passThroughCompression,
7455
7858
  encrypted,
7456
7859
  zip64UncompressedSize,
7457
7860
  zip64CompressedSize,
@@ -7462,7 +7865,7 @@
7462
7865
  let { version, compressionMethod } = options;
7463
7866
  const compressed = !directory && isCompressed(compressionMethod, level);
7464
7867
  let rawLocalExtraFieldZip64;
7465
- const uncompressedFile = passThrough || !compressed;
7868
+ const uncompressedFile = passThroughCompression || !compressed;
7466
7869
  const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || (uncompressedFile && !unknownSize)));
7467
7870
  const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
7468
7871
  if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
@@ -7474,8 +7877,8 @@
7474
7877
  if (zip64ExtraFieldComplete) {
7475
7878
  extraFieldZip64.writeUint64(uncompressedSize);
7476
7879
  if (uncompressedFile) {
7477
- const encryptionOverhead = encrypted ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
7478
- extraFieldZip64.writeUint64(passThrough ? 0 : uncompressedSize + encryptionOverhead);
7880
+ const encryptionOverhead = getEncryptionOverhead(encrypted, zipCrypto, encryptionStrength);
7881
+ extraFieldZip64.writeUint64(passThroughCompression ? 0 : uncompressedSize + encryptionOverhead);
7479
7882
  }
7480
7883
  }
7481
7884
  } else {
@@ -7494,6 +7897,7 @@
7494
7897
  let rawExtraFieldNTFS;
7495
7898
  let rawExtraFieldExtendedTimestamp;
7496
7899
  let extraFieldExtendedTimestampFlag;
7900
+ let extraFieldExtendedTimestampTime;
7497
7901
  if (extendedTimestamp) {
7498
7902
  const lastModTimeUnix = getTimeUnix(lastModDate);
7499
7903
  const lastModTimeUnixInRange = inUnixTimeRange(lastModTimeUnix);
@@ -7501,6 +7905,7 @@
7501
7905
  const extraFieldTimestampLength = 9 + (lastAccessDate ? 4 : 0) + (creationDate ? 4 : 0);
7502
7906
  const extraFieldTimestamp = createRecordWriter(extraFieldTimestampLength);
7503
7907
  extraFieldExtendedTimestampFlag = 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0);
7908
+ extraFieldExtendedTimestampTime = lastModTimeUnix;
7504
7909
  extraFieldTimestamp.writeUint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
7505
7910
  extraFieldTimestamp.writeUint16(extraFieldTimestampLength - 4);
7506
7911
  extraFieldTimestamp.writeUint8(extraFieldExtendedTimestampFlag);
@@ -7584,7 +7989,7 @@
7584
7989
  }
7585
7990
  if (encrypted && !zipCrypto) {
7586
7991
  version = version > VERSION_AES ? version : VERSION_AES;
7587
- if (passThrough && crc32 !== UNDEFINED_VALUE) {
7992
+ if (passThroughCompression && crc32 !== UNDEFINED_VALUE) {
7588
7993
  rawExtraFieldAES[EXTRAFIELD_OFFSET_AES_VENDOR_VERSION] = VENDOR_VERSION_AE_1;
7589
7994
  }
7590
7995
  setUint16(getDataView(rawExtraFieldAES), EXTRAFIELD_OFFSET_AES_COMPRESSION_METHOD, compressionMethod);
@@ -7646,12 +8051,14 @@
7646
8051
  headerArray,
7647
8052
  headerView,
7648
8053
  lastModDate: storedLastModDate,
8054
+ lastModDateClamped: storedLastModDate === clampedLastModDate && dosLastModDate.getTime() != clampedLastModDate.getTime(),
7649
8055
  rawLastModDate,
7650
8056
  encrypted,
7651
8057
  compressed,
7652
8058
  version,
7653
8059
  compressionMethod,
7654
8060
  extraFieldExtendedTimestampFlag,
8061
+ extraFieldExtendedTimestampTime,
7655
8062
  rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
7656
8063
  localExtraFieldZip64Length,
7657
8064
  rawExtraFieldExtendedTimestamp,
@@ -7754,7 +8161,7 @@
7754
8161
  }, {
7755
8162
  zip64,
7756
8163
  zipCrypto,
7757
- passThrough,
8164
+ passThroughCompression,
7758
8165
  dataDescriptor
7759
8166
  }) {
7760
8167
  const {
@@ -7765,7 +8172,7 @@
7765
8172
  dataDescriptorView,
7766
8173
  dataDescriptorOffset
7767
8174
  } = dataDescriptorInfo;
7768
- if ((!encrypted || zipCrypto || passThrough) && crc32 !== UNDEFINED_VALUE) {
8175
+ if ((!encrypted || zipCrypto || passThroughCompression) && crc32 !== UNDEFINED_VALUE) {
7769
8176
  setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
7770
8177
  if (dataDescriptor) {
7771
8178
  setUint32(dataDescriptorView, dataDescriptorOffset, crc32);
@@ -7796,9 +8203,9 @@
7796
8203
  uncompressedSize,
7797
8204
  zip64UncompressedSize,
7798
8205
  zip64CompressedSize
7799
- }, localHeaderView, { dataDescriptor, passThrough }) {
8206
+ }, localHeaderView, { dataDescriptor, passThroughCompression }) {
7800
8207
  if (!dataDescriptor) {
7801
- if (!encrypted || (passThrough && crc32 !== UNDEFINED_VALUE)) {
8208
+ if (!encrypted || (passThroughCompression && crc32 !== UNDEFINED_VALUE)) {
7802
8209
  setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE + LOCAL_HEADER_COMMON_OFFSET, crc32);
7803
8210
  }
7804
8211
  if (!zip64CompressedSize) {
@@ -7835,9 +8242,8 @@
7835
8242
  rawExtraFieldUnix,
7836
8243
  rawExtraField,
7837
8244
  rawCentralExtraField,
7838
- extendedTimestamp,
7839
8245
  extraFieldExtendedTimestampFlag,
7840
- lastModDate,
8246
+ extraFieldExtendedTimestampTime,
7841
8247
  zip64Enabled,
7842
8248
  uncompressedSize,
7843
8249
  compressedSize
@@ -7880,16 +8286,15 @@
7880
8286
  fileEntry.zip64Offset = zip64Offset;
7881
8287
  fileEntry.zip64DiskNumberStart = zip64DiskNumberStart;
7882
8288
  let rawExtraFieldTimestamp;
7883
- const lastModTimeUnix = getTimeUnix(lastModDate);
7884
- if (extendedTimestamp && inUnixTimeRange(lastModTimeUnix)) {
8289
+ if (extraFieldExtendedTimestampTime === UNDEFINED_VALUE) {
8290
+ rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
8291
+ } else {
7885
8292
  const extraFieldTimestamp = createRecordWriter(9);
7886
8293
  extraFieldTimestamp.writeUint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
7887
8294
  extraFieldTimestamp.writeUint16(5);
7888
8295
  extraFieldTimestamp.writeUint8(extraFieldExtendedTimestampFlag);
7889
- extraFieldTimestamp.writeUint32(lastModTimeUnix);
8296
+ extraFieldTimestamp.writeUint32(extraFieldExtendedTimestampTime);
7890
8297
  rawExtraFieldTimestamp = extraFieldTimestamp.array;
7891
- } else {
7892
- rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
7893
8298
  }
7894
8299
  fileEntry.rawExtraFieldExtendedTimestamp = rawExtraFieldTimestamp;
7895
8300
  const extraFieldLength = getLength(
@@ -8389,6 +8794,11 @@
8389
8794
  };
8390
8795
  }
8391
8796
 
8797
+ function isPrintableASCIIText(rawText) {
8798
+ return rawText.every(characterCode =>
8799
+ characterCode >= MIN_PRINTABLE_ASCII_CHARACTER_CODE && characterCode <= MAX_PRINTABLE_ASCII_CHARACTER_CODE);
8800
+ }
8801
+
8392
8802
  function getBitFlag(level, useUnicodeFileNames, dataDescriptor, encrypted, compressionMethod) {
8393
8803
  let bitFlag = 0;
8394
8804
  if (useUnicodeFileNames) {
@@ -8550,7 +8960,7 @@
8550
8960
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8551
8961
  */
8552
8962
 
8553
- const VERSION = "2.11.4";
8963
+ const VERSION = "2.13.0";
8554
8964
 
8555
8965
  /*
8556
8966
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
@@ -9003,6 +9413,7 @@
9003
9413
  exports.BlobWriter = BlobWriter;
9004
9414
  exports.Data64URIReader = Data64URIReader;
9005
9415
  exports.Data64URIWriter = Data64URIWriter;
9416
+ exports.ERR_ABORTED = ERR_ABORTED;
9006
9417
  exports.ERR_AMBIGUOUS_ARCHIVE = ERR_AMBIGUOUS_ARCHIVE;
9007
9418
  exports.ERR_BAD_FORMAT = ERR_BAD_FORMAT;
9008
9419
  exports.ERR_CENTRAL_DIRECTORY_NOT_FOUND = ERR_CENTRAL_DIRECTORY_NOT_FOUND;
@@ -9015,7 +9426,9 @@
9015
9426
  exports.ERR_EXTRAFIELD_ZIP64_NOT_FOUND = ERR_EXTRAFIELD_ZIP64_NOT_FOUND;
9016
9427
  exports.ERR_HTTP_RANGE = ERR_HTTP_RANGE;
9017
9428
  exports.ERR_HTTP_RESOURCE_CHANGED = ERR_HTTP_RESOURCE_CHANGED;
9429
+ exports.ERR_HTTP_STATUS = ERR_HTTP_STATUS;
9018
9430
  exports.ERR_INVALID_AUTHENTICATION_CODE = ERR_INVALID_AUTHENTICATION_CODE;
9431
+ exports.ERR_INVALID_BASE_URI = ERR_INVALID_BASE_URI;
9019
9432
  exports.ERR_INVALID_CODEC_DEFINITION = ERR_INVALID_CODEC_DEFINITION;
9020
9433
  exports.ERR_INVALID_CODEC_MODULE = ERR_INVALID_CODEC_MODULE;
9021
9434
  exports.ERR_INVALID_COMMENT = ERR_INVALID_COMMENT;
@@ -9024,6 +9437,7 @@
9024
9437
  exports.ERR_INVALID_CRC32 = ERR_INVALID_CRC32;
9025
9438
  exports.ERR_INVALID_DATE = ERR_INVALID_DATE;
9026
9439
  exports.ERR_INVALID_ENCRYPTION_STRENGTH = ERR_INVALID_ENCRYPTION_STRENGTH;
9440
+ exports.ERR_INVALID_ENTRY = ERR_INVALID_ENTRY;
9027
9441
  exports.ERR_INVALID_ENTRY_COMMENT = ERR_INVALID_ENTRY_COMMENT;
9028
9442
  exports.ERR_INVALID_ENTRY_COMMENT_TYPE = ERR_INVALID_ENTRY_COMMENT_TYPE;
9029
9443
  exports.ERR_INVALID_ENTRY_NAME = ERR_INVALID_ENTRY_NAME;
@@ -9041,6 +9455,7 @@
9041
9455
  exports.ERR_INVALID_MSDOS_DATA = ERR_INVALID_MSDOS_DATA;
9042
9456
  exports.ERR_INVALID_PASSWORD = ERR_INVALID_PASSWORD;
9043
9457
  exports.ERR_INVALID_PASSWORD_TYPE = ERR_INVALID_PASSWORD_TYPE;
9458
+ exports.ERR_INVALID_PASS_THROUGH_VALUE = ERR_INVALID_PASS_THROUGH_VALUE;
9044
9459
  exports.ERR_INVALID_READER = ERR_INVALID_READER;
9045
9460
  exports.ERR_INVALID_SIGNAL = ERR_INVALID_SIGNAL;
9046
9461
  exports.ERR_INVALID_SIGNATURE_DATA = ERR_INVALID_SIGNATURE_DATA;
@@ -9050,6 +9465,7 @@
9050
9465
  exports.ERR_INVALID_UNIX_EXTRA_FIELD_TYPE = ERR_INVALID_UNIX_EXTRA_FIELD_TYPE;
9051
9466
  exports.ERR_INVALID_UNIX_ID_SIZE = ERR_INVALID_UNIX_ID_SIZE;
9052
9467
  exports.ERR_INVALID_UNIX_MODE = ERR_INVALID_UNIX_MODE;
9468
+ exports.ERR_INVALID_URI = ERR_INVALID_URI;
9053
9469
  exports.ERR_INVALID_VERSION = ERR_INVALID_VERSION;
9054
9470
  exports.ERR_ITERATOR_COMPLETED_TOO_SOON = ERR_ITERATOR_COMPLETED_TOO_SOON;
9055
9471
  exports.ERR_LOCAL_FILE_HEADER_NOT_FOUND = ERR_LOCAL_FILE_HEADER_NOT_FOUND;
@@ -9057,6 +9473,7 @@
9057
9473
  exports.ERR_RESERVED_COMPRESSION_METHOD = ERR_RESERVED_COMPRESSION_METHOD;
9058
9474
  exports.ERR_SPLIT_ZIP_FILE = ERR_SPLIT_ZIP_FILE;
9059
9475
  exports.ERR_UNDEFINED_COMPRESSION_METHOD = ERR_UNDEFINED_COMPRESSION_METHOD;
9476
+ exports.ERR_UNDEFINED_CRC32 = ERR_UNDEFINED_CRC32;
9060
9477
  exports.ERR_UNDEFINED_READER = ERR_UNDEFINED_READER;
9061
9478
  exports.ERR_UNDEFINED_UNCOMPRESSED_SIZE = ERR_UNDEFINED_UNCOMPRESSED_SIZE;
9062
9479
  exports.ERR_UNDETERMINED_SIZE = ERR_UNDETERMINED_SIZE;
@@ -9072,6 +9489,8 @@
9072
9489
  exports.ERR_UNSUPPORTED_UINT64 = ERR_UNSUPPORTED_UINT64;
9073
9490
  exports.ERR_WORKER_STARTUP_TIMEOUT = ERR_WORKER_STARTUP_TIMEOUT;
9074
9491
  exports.ERR_WRITER_NOT_INITIALIZED = ERR_WRITER_NOT_INITIALIZED;
9492
+ exports.ERR_WRITER_SIZE_NOT_WRITABLE = ERR_WRITER_SIZE_NOT_WRITABLE;
9493
+ exports.ERR_ZIP_CRYPTO_LAST_MOD_DATE = ERR_ZIP_CRYPTO_LAST_MOD_DATE;
9075
9494
  exports.ERR_ZIP_NOT_EMPTY = ERR_ZIP_NOT_EMPTY;
9076
9495
  exports.HttpRangeReader = HttpRangeReader;
9077
9496
  exports.HttpReader = HttpReader;
@@ -9084,13 +9503,17 @@
9084
9503
  exports.Uint8ArrayWriter = Uint8ArrayWriter;
9085
9504
  exports.VERSION = VERSION;
9086
9505
  exports.WARNING_APPENDED_DATA = WARNING_APPENDED_DATA;
9506
+ exports.WARNING_CLAMPED_LAST_MODIFICATION_DATE = WARNING_CLAMPED_LAST_MODIFICATION_DATE;
9087
9507
  exports.WARNING_COMPRESSED_PATCHED_DATA = WARNING_COMPRESSED_PATCHED_DATA;
9508
+ exports.WARNING_COMPRESSION_UNAVAILABLE = WARNING_COMPRESSION_UNAVAILABLE;
9088
9509
  exports.WARNING_DUPLICATE_FILENAME = WARNING_DUPLICATE_FILENAME;
9089
9510
  exports.WARNING_MALFORMED_EXTRA_FIELD = WARNING_MALFORMED_EXTRA_FIELD;
9090
9511
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG;
9091
9512
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD;
9092
9513
  exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES;
9514
+ exports.WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME;
9093
9515
  exports.WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY;
9516
+ exports.WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY;
9094
9517
  exports.WARNING_PREPENDED_CENTRAL_DIRECTORY = WARNING_PREPENDED_CENTRAL_DIRECTORY;
9095
9518
  exports.WARNING_PREPENDED_DATA = WARNING_PREPENDED_DATA;
9096
9519
  exports.WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = WARNING_TRAILING_CENTRAL_DIRECTORY_DATA;