@zip.js/zip.js 2.8.48 → 2.8.50

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 (47) hide show
  1. package/deno.json +1 -1
  2. package/dist/zip-core-external.js +351 -205
  3. package/dist/zip-core-external.min.js +1 -1
  4. package/dist/zip-core.js +357 -205
  5. package/dist/zip-core.min.js +1 -1
  6. package/dist/zip-fs-core-external.js +438 -216
  7. package/dist/zip-fs-core-external.min.js +1 -1
  8. package/dist/zip-fs-core.js +213 -435
  9. package/dist/zip-fs-core.min.js +1 -1
  10. package/dist/zip-fs-external.js +438 -216
  11. package/dist/zip-fs-external.min.js +1 -1
  12. package/dist/zip-fs-native.js +447 -219
  13. package/dist/zip-fs-native.min.js +1 -1
  14. package/dist/zip-fs.js +445 -217
  15. package/dist/zip-fs.min.js +1 -1
  16. package/dist/zip-legacy.js +359 -207
  17. package/dist/zip-legacy.min.js +1 -1
  18. package/dist/zip-native.js +360 -208
  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 +358 -206
  23. package/dist/zip.min.js +1 -1
  24. package/index-native.cjs +446 -218
  25. package/index-native.min.js +1 -1
  26. package/index.cjs +444 -216
  27. package/index.d.cts +124 -2
  28. package/index.d.ts +124 -2
  29. package/index.min.js +1 -1
  30. package/lib/core/codec-pool.js +1 -1
  31. package/lib/core/codec-registry.js +2 -2
  32. package/lib/core/codec-worker-web.js +436 -0
  33. package/lib/core/codec-worker.js +23 -388
  34. package/lib/core/constants.js +5 -1
  35. package/lib/core/io.js +2 -1
  36. package/lib/core/options.js +8 -0
  37. package/lib/core/streams/zlib-js/zlib-streams.min.js +1 -1
  38. package/lib/core/web-worker-base.js +10 -2
  39. package/lib/core/web-worker-inline-native.js +1 -1
  40. package/lib/core/web-worker-inline-wasm.js +1 -1
  41. package/lib/core/zip-fs.js +88 -12
  42. package/lib/core/zip-reader.js +90 -16
  43. package/lib/core/zip-writer.js +36 -13
  44. package/lib/zip-core-reader.js +6 -0
  45. package/lib/zip-core-writer.js +4 -0
  46. package/package.json +1 -1
  47. package/worker-message-property-names.js +1 -1
@@ -4,7 +4,7 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.zip = {}));
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
- const { Array, Object, String, Number, BigInt, Math, Date, Map, Set, Response, URL, Error, Uint8Array, Uint16Array, Uint32Array, DataView, Blob, Promise, TextEncoder, TextDecoder, document, crypto, btoa, TransformStream, ReadableStream, WritableStream, CompressionStream, DecompressionStream, navigator, Worker } = typeof globalThis !== 'undefined' ? globalThis : this || self;
7
+ const { Array, Object, String, Number, BigInt, Math, Date, Map, Set, Response, URL, Error, Uint8Array, Uint16Array, Uint32Array, DataView, Blob, Promise, TextEncoder, TextDecoder, document, crypto, btoa, TransformStream, ReadableStream, WritableStream, CompressionStream, DecompressionStream, navigator, Worker, Symbol, setTimeout, clearTimeout, structuredClone } = typeof globalThis !== 'undefined' ? globalThis : this || self;
8
8
 
9
9
  /*
10
10
  Copyright (c) 2022 Gildas Lormeau. All rights reserved.
@@ -116,6 +116,8 @@
116
116
  const UNDEFINED_TYPE = "undefined";
117
117
  const FUNCTION_TYPE = "function";
118
118
  const OBJECT_TYPE = "object";
119
+ const STRING_TYPE = "string";
120
+ const NUMBER_TYPE = "number";
119
121
 
120
122
  const EMPTY_UINT8_ARRAY = new Uint8Array();
121
123
 
@@ -707,7 +709,7 @@
707
709
  forceRangeRequests,
708
710
  combineSizeEocd
709
711
  } = httpReader;
710
- if (isHttpFamily(url) && (useRangeHeader || forceRangeRequests) && (typeof preventHeadRequest == "undefined" || preventHeadRequest)) {
712
+ if (isHttpFamily(url) && (useRangeHeader || forceRangeRequests) && (typeof preventHeadRequest == UNDEFINED_TYPE || preventHeadRequest)) {
711
713
  const response = await sendRequest(HTTP_METHOD_GET, httpReader, getRangeHeaders(httpReader, combineSizeEocd ? -END_OF_CENTRAL_DIR_LENGTH : undefined));
712
714
  const acceptRanges = response.headers.get(HTTP_HEADER_ACCEPT_RANGES);
713
715
  if (!forceRangeRequests && (!acceptRanges || acceptRanges.toLowerCase() != HTTP_RANGE_UNIT)) {
@@ -3203,12 +3205,6 @@
3203
3205
 
3204
3206
 
3205
3207
  const DEFAULT_CHUNK_SIZE = 64 * 1024;
3206
- const MESSAGE_EVENT_TYPE = "message";
3207
- const MESSAGE_START = "start";
3208
- const MESSAGE_PULL = "pull";
3209
- const MESSAGE_DATA = "data";
3210
- const MESSAGE_ACK_DATA = "ack";
3211
- const MESSAGE_CLOSE = "close";
3212
3208
  const CODEC_DEFLATE = "deflate";
3213
3209
  const CODEC_INFLATE = "inflate";
3214
3210
 
@@ -3331,8 +3327,8 @@
3331
3327
  1. Redistributions of source code must retain the above copyright notice,
3332
3328
  this list of conditions and the following disclaimer.
3333
3329
 
3334
- 2. Redistributions in binary form must reproduce the above copyright
3335
- notice, this list of conditions and the following disclaimer in
3330
+ 2. Redistributions in binary form must reproduce the above copyright
3331
+ notice, this list of conditions and the following disclaimer in
3336
3332
  the documentation and/or other materials provided with the distribution.
3337
3333
 
3338
3334
  3. The names of the authors may not be used to endorse or promote products
@@ -3350,19 +3346,6 @@
3350
3346
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3351
3347
  */
3352
3348
 
3353
-
3354
- const MODULE_WORKER_OPTIONS = { type: "module" };
3355
- const ERROR_EVENT_TYPE = "error";
3356
- const MESSAGE_ERROR_EVENT_TYPE = "messageerror";
3357
- const ERR_WORKER_STARTUP_TIMEOUT = "Worker startup timeout";
3358
-
3359
- let webWorkerSupported, webWorkerSource, webWorkerURI, webWorkerOptions, createWorkerFailed;
3360
- let transferStreamsSupported = true;
3361
- try {
3362
- transferStreamsSupported = typeof structuredClone == FUNCTION_TYPE && structuredClone(new DOMException("", "AbortError")).code !== UNDEFINED_VALUE;
3363
- } catch {
3364
- // ignored
3365
- }
3366
3349
  let initModule = () => { };
3367
3350
 
3368
3351
  async function supportsDeflate(config) {
@@ -3388,9 +3371,6 @@
3388
3371
 
3389
3372
  constructor(workerData, { readable, writable }, { options, config, streamOptions, useWebWorkers, transferStreams, workerURI, createWorker }, onTaskFinished) {
3390
3373
  const { signal } = streamOptions;
3391
- if (createWorkerFailed) {
3392
- createWorker = UNDEFINED_VALUE;
3393
- }
3394
3374
  Object.assign(workerData, {
3395
3375
  busy: true,
3396
3376
  generation: (workerData.generation || 0) + 1,
@@ -3432,11 +3412,7 @@
3432
3412
  }
3433
3413
  }
3434
3414
  });
3435
- if (webWorkerSupported === UNDEFINED_VALUE) {
3436
- // deno-lint-ignore valid-typeof
3437
- webWorkerSupported = typeof Worker != UNDEFINED_TYPE;
3438
- }
3439
- return (useWebWorkers && ((webWorkerSupported && workerURI) || createWorker) ? createWebWorkerInterface : createWorkerInterface)(workerData, config);
3415
+ return (createWorkerInterface)(workerData, config);
3440
3416
  }
3441
3417
  }
3442
3418
 
@@ -3480,58 +3456,6 @@
3480
3456
  };
3481
3457
  }
3482
3458
 
3483
- function createWebWorkerInterface(workerData, config) {
3484
- const { baseURI, chunkSize, workerStartupTimeout } = config;
3485
- let { wasmURI } = config;
3486
-
3487
- if (!workerData.interface) {
3488
- // deno-lint-ignore valid-typeof
3489
- if (typeof wasmURI == FUNCTION_TYPE) {
3490
- wasmURI = wasmURI();
3491
- }
3492
- let worker;
3493
- try {
3494
- worker = getWebWorker(workerData.workerURI, baseURI, workerData);
3495
- } catch {
3496
- if (workerData.createWorker) {
3497
- createWorkerFailed = true;
3498
- } else {
3499
- webWorkerSupported = false;
3500
- }
3501
- return createWorkerInterface(workerData, config);
3502
- }
3503
- Object.assign(workerData, {
3504
- worker,
3505
- workerAlive: false,
3506
- terminated: false,
3507
- interface: {
3508
- run: async () => {
3509
- try {
3510
- return await runWebWorker(workerData, { chunkSize, wasmURI, baseURI, workerStartupTimeout });
3511
- } catch (error) {
3512
- if (error && error.workerStartupFailed) {
3513
- if (workerData.createWorker) {
3514
- createWorkerFailed = true;
3515
- } else {
3516
- webWorkerSupported = false;
3517
- }
3518
- const { reader } = workerData;
3519
- if (reader) {
3520
- reader.releaseLock();
3521
- }
3522
- workerData.reader = null;
3523
- workerData.writer = null;
3524
- return runWorker$1(workerData, config);
3525
- }
3526
- throw error;
3527
- }
3528
- }
3529
- }
3530
- });
3531
- }
3532
- return workerData.interface;
3533
- }
3534
-
3535
3459
  async function runWorker$1({ options, readable, writable, onTaskFinished }, config) {
3536
3460
  let codecStream;
3537
3461
  try {
@@ -3580,318 +3504,6 @@
3580
3504
  }
3581
3505
  }
3582
3506
 
3583
- async function runWebWorker(workerData, config) {
3584
- let resolveResult, rejectResult;
3585
- const result = new Promise((resolve, reject) => {
3586
- resolveResult = resolve;
3587
- rejectResult = reject;
3588
- });
3589
- Object.assign(workerData, {
3590
- reader: null,
3591
- writer: null,
3592
- resolveResult,
3593
- rejectResult,
3594
- result
3595
- });
3596
- const { readable, options } = workerData;
3597
- const { writable, closed, abortPipe } = watchClosedStream(workerData.writable);
3598
- let streamsTransferred;
3599
- try {
3600
- streamsTransferred = sendMessage({
3601
- type: MESSAGE_START,
3602
- options,
3603
- config,
3604
- readable,
3605
- writable
3606
- }, workerData);
3607
- } catch (error) {
3608
- abortPipe();
3609
- try {
3610
- await closed;
3611
- } catch {
3612
- // ignored
3613
- }
3614
- workerData.onTaskFinished();
3615
- throw error;
3616
- }
3617
- if (!streamsTransferred) {
3618
- Object.assign(workerData, {
3619
- reader: readable.getReader(),
3620
- writer: writable.getWriter()
3621
- });
3622
- }
3623
- const { workerStartupTimeout } = config;
3624
- if (!workerData.workerAlive && Number.isFinite(workerStartupTimeout) && workerStartupTimeout >= 0) {
3625
- workerData.startupTimeout = setTimeout(() => onStartupTimeout(workerData), workerStartupTimeout);
3626
- }
3627
- try {
3628
- const resultValue = await result;
3629
- await closeWritable();
3630
- await closed;
3631
- return resultValue;
3632
- } catch (error) {
3633
- await closeWritable();
3634
- abortPipe();
3635
- try {
3636
- await closed;
3637
- } catch {
3638
- // ignored
3639
- }
3640
- throw error;
3641
- }
3642
-
3643
- async function closeWritable() {
3644
- if (!streamsTransferred && !writable.locked) {
3645
- try {
3646
- await writable.getWriter().close();
3647
- } catch {
3648
- // ignored
3649
- }
3650
- }
3651
- }
3652
- }
3653
-
3654
- function watchClosedStream(writableSource) {
3655
- const abortController = new AbortController();
3656
- const { writable, readable } = new TransformStream();
3657
- const closed = readable.pipeTo(writableSource, { preventClose: true, preventAbort: true, signal: abortController.signal });
3658
- closed.catch(() => { });
3659
- return { writable, closed, abortPipe: () => abortController.abort() };
3660
- }
3661
-
3662
- function terminateWorker$1(workerData) {
3663
- const { worker } = workerData;
3664
- if (worker) {
3665
- try {
3666
- worker.terminate();
3667
- } catch {
3668
- // ignored
3669
- }
3670
- }
3671
- workerData.interface = null;
3672
- }
3673
-
3674
- function getWebWorker(url, baseURI, workerData, isModuleType, useBlobURI = true) {
3675
- const { createWorker } = workerData;
3676
- let worker, resolvedURI, resolvedOptions;
3677
- if (createWorker) {
3678
- worker = createWorker();
3679
- } else if (webWorkerURI === UNDEFINED_VALUE || webWorkerSource !== url) {
3680
- // deno-lint-ignore valid-typeof
3681
- const isFunctionURI = typeof url == FUNCTION_TYPE;
3682
- if (isFunctionURI) {
3683
- resolvedURI = url(useBlobURI);
3684
- } else {
3685
- resolvedURI = url;
3686
- }
3687
- const isDataURI = resolvedURI.startsWith("data:");
3688
- const isBlobURI = resolvedURI.startsWith("blob:");
3689
- if (isDataURI || isBlobURI) {
3690
- if (isModuleType === UNDEFINED_VALUE) {
3691
- isModuleType = false;
3692
- }
3693
- if (isModuleType) {
3694
- resolvedOptions = MODULE_WORKER_OPTIONS;
3695
- }
3696
- try {
3697
- worker = new Worker(resolvedURI, resolvedOptions);
3698
- } catch (error) {
3699
- if (isBlobURI) {
3700
- try {
3701
- URL.revokeObjectURL(resolvedURI);
3702
- } catch {
3703
- // ignored
3704
- }
3705
- }
3706
- if (isFunctionURI && isBlobURI) {
3707
- return getWebWorker(url, baseURI, workerData, isModuleType, false);
3708
- } else if (!isModuleType) {
3709
- return getWebWorker(url, baseURI, workerData, true, false);
3710
- } else {
3711
- throw error;
3712
- }
3713
- }
3714
- } else {
3715
- if (isModuleType === UNDEFINED_VALUE) {
3716
- isModuleType = true;
3717
- }
3718
- if (isModuleType) {
3719
- resolvedOptions = MODULE_WORKER_OPTIONS;
3720
- }
3721
- try {
3722
- resolvedURI = new URL(resolvedURI, baseURI);
3723
- } catch {
3724
- // ignored
3725
- }
3726
- try {
3727
- worker = new Worker(resolvedURI, resolvedOptions);
3728
- } catch (error) {
3729
- if (!isModuleType) {
3730
- return getWebWorker(url, baseURI, workerData, false, useBlobURI);
3731
- } else {
3732
- throw error;
3733
- }
3734
- }
3735
- }
3736
- webWorkerSource = url;
3737
- webWorkerURI = resolvedURI;
3738
- webWorkerOptions = resolvedOptions;
3739
- } else {
3740
- worker = new Worker(webWorkerURI, webWorkerOptions);
3741
- }
3742
- worker.addEventListener(MESSAGE_EVENT_TYPE, event => {
3743
- workerData.workerAlive = true;
3744
- clearStartupTimeout(workerData);
3745
- onMessage(event, workerData);
3746
- });
3747
- worker.addEventListener(ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
3748
- worker.addEventListener(MESSAGE_ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
3749
- return worker;
3750
- }
3751
-
3752
- function onStartupTimeout(workerData) {
3753
- workerData.startupTimeout = null;
3754
- if (workerData.workerAlive) {
3755
- return;
3756
- }
3757
- const { rejectResult, writer } = workerData;
3758
- terminateWorker$1(workerData);
3759
- workerData.worker = null;
3760
- if (rejectResult) {
3761
- const error = new Error(ERR_WORKER_STARTUP_TIMEOUT);
3762
- error.workerStartupFailed = true;
3763
- rejectResult(error);
3764
- if (writer) {
3765
- writer.releaseLock();
3766
- }
3767
- }
3768
- }
3769
-
3770
- function clearStartupTimeout(workerData) {
3771
- const { startupTimeout } = workerData;
3772
- if (startupTimeout) {
3773
- clearTimeout(startupTimeout);
3774
- workerData.startupTimeout = null;
3775
- }
3776
- }
3777
-
3778
- function onWorkerError(event, workerData) {
3779
- if (event.preventDefault) {
3780
- event.preventDefault();
3781
- }
3782
- clearStartupTimeout(workerData);
3783
- const { workerAlive, rejectResult, writer, onTaskFinished } = workerData;
3784
- terminateWorker$1(workerData);
3785
- if (!workerAlive) {
3786
- workerData.worker = null;
3787
- }
3788
- if (rejectResult) {
3789
- let error = event.error || new Error(event.message || ERROR_EVENT_TYPE);
3790
- if (!workerAlive) {
3791
- error = Object.assign(new Error(error.message || ERROR_EVENT_TYPE), { workerStartupFailed: true });
3792
- }
3793
- rejectResult(error);
3794
- if (writer) {
3795
- writer.releaseLock();
3796
- }
3797
- if (workerAlive) {
3798
- onTaskFinished();
3799
- }
3800
- }
3801
- }
3802
-
3803
- function sendMessage(message, { worker, writer, transferStreams, workerAlive }) {
3804
- try {
3805
- const { value, readable, writable } = message;
3806
- const transferables = [];
3807
- if (value) {
3808
- message.value = toExactUint8Array(value);
3809
- transferables.push(message.value.buffer);
3810
- }
3811
- if (transferStreams && transferStreamsSupported && workerAlive) {
3812
- if (readable) {
3813
- transferables.push(readable);
3814
- }
3815
- if (writable) {
3816
- transferables.push(writable);
3817
- }
3818
- } else {
3819
- message.readable = message.writable = null;
3820
- }
3821
- if (transferables.length) {
3822
- try {
3823
- worker.postMessage(message, transferables);
3824
- return true;
3825
- } catch {
3826
- transferStreamsSupported = false;
3827
- message.readable = message.writable = null;
3828
- worker.postMessage(message);
3829
- }
3830
- } else {
3831
- worker.postMessage(message);
3832
- }
3833
- } catch (error) {
3834
- if (writer) {
3835
- writer.releaseLock();
3836
- }
3837
- throw error;
3838
- }
3839
- }
3840
-
3841
- async function onMessage({ data }, workerData) {
3842
- const { type, value, messageId, result, error } = data;
3843
- const { reader, writer, resolveResult, rejectResult, onTaskFinished, generation } = workerData;
3844
- const stale = () => workerData.generation != generation;
3845
- try {
3846
- if (error) {
3847
- const { message, stack, code, name, outputSize, cause } = error;
3848
- const responseError = new Error(message);
3849
- Object.assign(responseError, { stack, code, name, outputSize });
3850
- if (cause) {
3851
- responseError.cause = Object.assign(new Error(cause.message), { name: cause.name });
3852
- }
3853
- close(responseError);
3854
- } else {
3855
- if (type == MESSAGE_PULL) {
3856
- const { value, done } = await reader.read();
3857
- if (!stale()) {
3858
- sendMessage({ type: MESSAGE_DATA, value, done, messageId }, workerData);
3859
- }
3860
- }
3861
- if (type == MESSAGE_DATA) {
3862
- await writer.ready;
3863
- await writer.write(new Uint8Array(value));
3864
- if (!stale()) {
3865
- sendMessage({ type: MESSAGE_ACK_DATA, messageId }, workerData);
3866
- }
3867
- }
3868
- if (type == MESSAGE_CLOSE) {
3869
- close(null, result);
3870
- }
3871
- }
3872
- } catch (error) {
3873
- if (!stale()) {
3874
- terminateWorker$1(workerData);
3875
- close(error);
3876
- }
3877
- }
3878
-
3879
- function close(error, result) {
3880
- if (stale()) {
3881
- return;
3882
- }
3883
- if (error) {
3884
- rejectResult(error);
3885
- } else {
3886
- resolveResult(result);
3887
- }
3888
- if (writer) {
3889
- writer.releaseLock();
3890
- }
3891
- onTaskFinished();
3892
- }
3893
- }
3894
-
3895
3507
  /*
3896
3508
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
3897
3509
 
@@ -3938,7 +3550,7 @@
3938
3550
  }
3939
3551
  await ensureCodecStreams(format, options.codecURI);
3940
3552
  }
3941
- workerOptions.transferStreams = transferStreams || (transferStreams === UNDEFINED_VALUE && config.transferStreams);
3553
+ workerOptions.transferStreams = !format && (transferStreams || (transferStreams === UNDEFINED_VALUE && config.transferStreams));
3942
3554
  const streamCopy = !compressed && !checkCrc32 && !computeCrc32 && !encrypted;
3943
3555
  const workerSupported = format === UNDEFINED_VALUE || Boolean(options.codecURI);
3944
3556
  workerOptions.useWebWorkers = !streamCopy && workerSupported && (useWebWorkers || (useWebWorkers === UNDEFINED_VALUE && config.useWebWorkers));
@@ -4334,9 +3946,13 @@
4334
3946
  const OPTION_UNIX_EXTRA_FIELD_TYPE = "unixExtraFieldType";
4335
3947
  const OPTION_LOCAL_EXTRA_FIELD = "localExtraField";
4336
3948
  const OPTION_STRICTNESS = "strictness";
3949
+ const OPTION_FILENAME_VALIDATION = "filenameValidation";
3950
+ const OPTION_NORMALIZE_FILENAME = "normalizeFilename";
4337
3951
  const OPTION_MAX_APPENDED_DATA_SIZE = "maxAppendedDataSize";
4338
3952
  const OPTION_DECRYPT_CENTRAL_DIRECTORY = "decryptCentralDirectory";
4339
3953
  const OPTION_SIGN_CENTRAL_DIRECTORY = "signCentralDirectory";
3954
+ const TEXT_TYPE_FILENAME = "filename";
3955
+ const TEXT_TYPE_COMMENT = "comment";
4340
3956
  const STRICTNESS_STRICT = "strict";
4341
3957
  const STRICTNESS_BALANCED = "balanced";
4342
3958
  const STRICTNESS_TOLERANT = "tolerant";
@@ -4383,6 +3999,11 @@
4383
3999
  const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4384
4000
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4385
4001
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
4002
+ const ERR_UNSAFE_FILENAME = "Unsafe filename";
4003
+ const ERR_INVALID_STRICTNESS = "Invalid strictness (must be 'strict', 'balanced' or 'tolerant')";
4004
+ const ERR_INVALID_FILENAME_VALIDATION = "Invalid filenameValidation (must be 'strict', 'balanced' or 'tolerant')";
4005
+ const ERR_INVALID_MAX_APPENDED_DATA_SIZE = "Invalid maxAppendedDataSize (must be a number greater than or equal to 0)";
4006
+ const DRIVE_LETTER_REGEXP = /^[a-zA-Z]:/;
4386
4007
  const CHARSET_UTF8 = "utf-8";
4387
4008
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
4388
4009
  const CHARSET_CP437 = "cp437";
@@ -4404,6 +4025,10 @@
4404
4025
  bytes: 8
4405
4026
  }
4406
4027
  };
4028
+ const MAX_END_OF_CENTRAL_DIR_PROBES = 64;
4029
+ const CENTRAL_DIRECTORY_UNREACHABLE = 0;
4030
+ const CENTRAL_DIRECTORY_PLAUSIBLE = 1;
4031
+ const CENTRAL_DIRECTORY_REACHABLE = 2;
4407
4032
 
4408
4033
  class ZipReader {
4409
4034
 
@@ -4432,6 +4057,8 @@
4432
4057
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
4433
4058
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
4434
4059
  const maxAppendedDataSize = getMaxAppendedDataSize(getOptionValue$1(zipReader, options, OPTION_MAX_APPENDED_DATA_SIZE), strictness);
4060
+ const filenameValidation = getFilenameValidation(getOptionValue$1(zipReader, options, OPTION_FILENAME_VALIDATION), strictness);
4061
+ const normalizeFilename = getOptionValue$1(zipReader, options, OPTION_NORMALIZE_FILENAME);
4435
4062
  const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
4436
4063
  if (!endOfDirectoryInfo) {
4437
4064
  const signatureArray = await readUint8Array(reader, 0, 4);
@@ -4625,11 +4252,22 @@
4625
4252
  const decode = getOptionValue$1(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
4626
4253
  const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
4627
4254
  const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
4628
- let filename = decode(rawFilename, rawFilenameEncoding);
4255
+ let filename = decode(rawFilename, rawFilenameEncoding, TEXT_TYPE_FILENAME);
4629
4256
  if (filename === UNDEFINED_VALUE) {
4630
4257
  filename = decodeText(rawFilename, rawFilenameEncoding);
4631
4258
  }
4632
- let comment = decode(rawComment, rawCommentEncoding);
4259
+ if (normalizeFilename) {
4260
+ const normalizedFilename = normalizeFilename(filename);
4261
+ if (normalizedFilename !== UNDEFINED_VALUE) {
4262
+ filename = normalizedFilename;
4263
+ }
4264
+ }
4265
+ if (isUnsafeFilename(filename, filenameValidation)) {
4266
+ const error = new Error(ERR_UNSAFE_FILENAME);
4267
+ error.filename = filename;
4268
+ throw error;
4269
+ }
4270
+ let comment = decode(rawComment, rawCommentEncoding, TEXT_TYPE_COMMENT);
4633
4271
  if (comment === UNDEFINED_VALUE) {
4634
4272
  comment = decodeText(rawComment, rawCommentEncoding);
4635
4273
  }
@@ -4690,10 +4328,10 @@
4690
4328
  entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
4691
4329
  entry.arrayBuffer = async options => {
4692
4330
  const writer = new TransformStream();
4693
- const [arrayBuffer] = await Promise.all([
4694
- streamToBlob(writer.readable).then(blob => blob.arrayBuffer()),
4695
- fileEntry.getData(writer, entry, zipReader.readRanges, options)]);
4696
- return arrayBuffer;
4331
+ const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
4332
+ arrayBufferPromise.catch(() => { });
4333
+ await fileEntry.getData(writer, entry, zipReader.readRanges, options);
4334
+ return arrayBufferPromise;
4697
4335
  };
4698
4336
  offset = endOffset;
4699
4337
  const { onprogress } = options;
@@ -4904,7 +4542,7 @@
4904
4542
  readRanges
4905
4543
  });
4906
4544
  }
4907
- let writable;
4545
+ let writable, abortError;
4908
4546
  try {
4909
4547
  if (!checkOverlappingEntryOnly) {
4910
4548
  if (checkPasswordOnly) {
@@ -4924,12 +4562,22 @@
4924
4562
  writer.size += error.outputSize;
4925
4563
  }
4926
4564
  if (!checkPasswordOnly || error.message != ERR_ABORT_CHECK_PASSWORD) {
4565
+ abortError = error;
4927
4566
  throw error;
4928
4567
  }
4929
4568
  } finally {
4930
4569
  const preventClose = getOptionValue$1(zipEntry, options, OPTION_PREVENT_CLOSE);
4931
4570
  if (!preventClose && writable && !writable.locked) {
4932
- await writable.getWriter().close();
4571
+ const writableWriter = writable.getWriter();
4572
+ if (abortError) {
4573
+ try {
4574
+ await writableWriter.abort(abortError);
4575
+ } catch {
4576
+ // the error being propagated is more relevant; ignored
4577
+ }
4578
+ } else {
4579
+ await writableWriter.close();
4580
+ }
4933
4581
  }
4934
4582
  }
4935
4583
  return checkPasswordOnly || checkOverlappingEntryOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
@@ -5309,16 +4957,51 @@
5309
4957
  return reader.getDiskOffset ? reader.getDiskOffset(diskNumber) : 0;
5310
4958
  }
5311
4959
 
4960
+ function isStrictnessValue(value) {
4961
+ return value === STRICTNESS_STRICT || value === STRICTNESS_BALANCED || value === STRICTNESS_TOLERANT;
4962
+ }
4963
+
5312
4964
  function getStrictness(strictness, checkAmbiguity) {
5313
4965
  if (strictness === UNDEFINED_VALUE) {
5314
4966
  return checkAmbiguity ? STRICTNESS_STRICT : STRICTNESS_BALANCED;
5315
4967
  }
4968
+ if (!isStrictnessValue(strictness)) {
4969
+ throw new Error(ERR_INVALID_STRICTNESS);
4970
+ }
5316
4971
  return strictness;
5317
4972
  }
5318
4973
 
4974
+ function getFilenameValidation(filenameValidation, strictness) {
4975
+ if (filenameValidation === UNDEFINED_VALUE) {
4976
+ return strictness;
4977
+ }
4978
+ if (!isStrictnessValue(filenameValidation)) {
4979
+ throw new Error(ERR_INVALID_FILENAME_VALIDATION);
4980
+ }
4981
+ return filenameValidation;
4982
+ }
4983
+
4984
+ function isUnsafeFilename(filename, filenameValidation) {
4985
+ if (filenameValidation == STRICTNESS_TOLERANT) {
4986
+ return false;
4987
+ }
4988
+ const pathParts = filename.split("/");
4989
+ if (pathParts.length > 1 && pathParts[pathParts.length - 1] === "") {
4990
+ pathParts.pop();
4991
+ }
4992
+ if (pathParts.includes("..") || filename.startsWith("/") || filename.startsWith("\\\\") || DRIVE_LETTER_REGEXP.test(filename)) {
4993
+ return true;
4994
+ }
4995
+ return filenameValidation == STRICTNESS_STRICT && (pathParts.includes(".") || pathParts.includes(""));
4996
+ }
4997
+
5319
4998
  function getMaxAppendedDataSize(maxAppendedDataSize, strictness) {
5320
4999
  if (maxAppendedDataSize !== UNDEFINED_VALUE) {
5321
- return maxAppendedDataSize;
5000
+ const size = toNumber$1(maxAppendedDataSize);
5001
+ if (typeof size != NUMBER_TYPE || Number.isNaN(size) || size < 0) {
5002
+ throw new Error(ERR_INVALID_MAX_APPENDED_DATA_SIZE);
5003
+ }
5004
+ return size;
5322
5005
  }
5323
5006
  if (strictness == STRICTNESS_STRICT) {
5324
5007
  return 0;
@@ -5329,12 +5012,6 @@
5329
5012
  return MAX_16_BITS;
5330
5013
  }
5331
5014
 
5332
- const MAX_END_OF_CENTRAL_DIR_PROBES = 64;
5333
-
5334
- const CENTRAL_DIRECTORY_UNREACHABLE = 0;
5335
- const CENTRAL_DIRECTORY_PLAUSIBLE = 1;
5336
- const CENTRAL_DIRECTORY_REACHABLE = 2;
5337
-
5338
5015
  async function findEndOfCentralDirectory(reader, rejectAmbiguous, maxAppendedDataSize) {
5339
5016
  const { size } = reader;
5340
5017
  const anchoredLength = Math.min(size, END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS);
@@ -5470,6 +5147,10 @@
5470
5147
  return options[name] === UNDEFINED_VALUE ? zipReader.options[name] : options[name];
5471
5148
  }
5472
5149
 
5150
+ function toNumber$1(value) {
5151
+ return typeof value == STRING_TYPE && value.trim() ? Number(value) : value;
5152
+ }
5153
+
5473
5154
  function getDate(timeRaw) {
5474
5155
  const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
5475
5156
  return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
@@ -5508,15 +5189,18 @@
5508
5189
  ERR_INVALID_AUTHENTICATION_CODE: ERR_INVALID_AUTHENTICATION_CODE,
5509
5190
  ERR_INVALID_COMPRESSED_DATA: ERR_INVALID_COMPRESSED_DATA,
5510
5191
  ERR_INVALID_CRC32: ERR_INVALID_CRC32,
5192
+ ERR_INVALID_FILENAME_VALIDATION: ERR_INVALID_FILENAME_VALIDATION,
5193
+ ERR_INVALID_MAX_APPENDED_DATA_SIZE: ERR_INVALID_MAX_APPENDED_DATA_SIZE,
5511
5194
  ERR_INVALID_PASSWORD: ERR_INVALID_PASSWORD,
5512
5195
  ERR_INVALID_SIGNATURE: ERR_INVALID_SIGNATURE,
5196
+ ERR_INVALID_STRICTNESS: ERR_INVALID_STRICTNESS,
5513
5197
  ERR_INVALID_UNCOMPRESSED_SIZE: ERR_INVALID_UNCOMPRESSED_SIZE,
5514
5198
  ERR_LOCAL_FILE_HEADER_NOT_FOUND: ERR_LOCAL_FILE_HEADER_NOT_FOUND,
5515
5199
  ERR_OVERLAPPING_ENTRY: ERR_OVERLAPPING_ENTRY,
5516
5200
  ERR_SPLIT_ZIP_FILE: ERR_SPLIT_ZIP_FILE,
5201
+ ERR_UNSAFE_FILENAME: ERR_UNSAFE_FILENAME,
5517
5202
  ERR_UNSUPPORTED_COMPRESSION: ERR_UNSUPPORTED_COMPRESSION$1,
5518
5203
  ERR_UNSUPPORTED_ENCRYPTION: ERR_UNSUPPORTED_ENCRYPTION,
5519
- ERR_WORKER_STARTUP_TIMEOUT: ERR_WORKER_STARTUP_TIMEOUT,
5520
5204
  ZipReader: ZipReader
5521
5205
  });
5522
5206
 
@@ -5574,6 +5258,8 @@
5574
5258
  const ERR_INVALID_UNIX_ID_SIZE = "uid/gid must be 0..65535 for unixExtraFieldType 'unix' (use 'infozip' for larger ids)";
5575
5259
  const ERR_INVALID_MSDOS_ATTRIBUTES = "Invalid msdosAttributesRaw (must be integer 0..255)";
5576
5260
  const ERR_INVALID_MSDOS_DATA = "Invalid msdosAttributes (must be an object with boolean flags)";
5261
+ const ERR_INVALID_LEVEL = "Invalid level (must be integer 0..9)";
5262
+ const ERR_INVALID_PASSWORD_TYPE = "Invalid password (password must be a string, rawPassword must be a Uint8Array)";
5577
5263
  const ERR_INVALID_SIGNATURE_DATA = "Signature data exceeds 64KB";
5578
5264
 
5579
5265
  const EXTRAFIELD_DATA_AES = new Uint8Array([0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00]);
@@ -5581,6 +5267,7 @@
5581
5267
  const VENDOR_VERSION_AE_1 = 1;
5582
5268
  const INFOZIP_EXTRA_FIELD_TYPE$1 = "infozip";
5583
5269
  const UNIX_EXTRA_FIELD_TYPE = "unix";
5270
+ const MAX_LEVEL = 9;
5584
5271
 
5585
5272
  let workers = 0;
5586
5273
  const pendingEntries = [];
@@ -5743,7 +5430,8 @@
5743
5430
 
5744
5431
  remove(entry) {
5745
5432
  const { filenames, files } = this;
5746
- if (typeof entry == "string") {
5433
+ // deno-lint-ignore valid-typeof
5434
+ if (typeof entry == STRING_TYPE) {
5747
5435
  entry = files.get(entry);
5748
5436
  }
5749
5437
  if (entry && entry.filename !== UNDEFINED_VALUE) {
@@ -5819,20 +5507,20 @@
5819
5507
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
5820
5508
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? 20 : 768);
5821
5509
  const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
5822
- const uid = getOptionValue(zipWriter, options, PROPERTY_NAME_UID);
5823
- const gid = getOptionValue(zipWriter, options, PROPERTY_NAME_GID);
5824
- let unixMode = getOptionValue(zipWriter, options, PROPERTY_NAME_UNIX_MODE);
5510
+ const uid = getNumberOptionValue(zipWriter, options, PROPERTY_NAME_UID);
5511
+ const gid = getNumberOptionValue(zipWriter, options, PROPERTY_NAME_GID);
5512
+ let unixMode = getNumberOptionValue(zipWriter, options, PROPERTY_NAME_UNIX_MODE);
5825
5513
  let unixExtraFieldType = getOptionValue(zipWriter, options, OPTION_UNIX_EXTRA_FIELD_TYPE);
5826
5514
  let setuid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETUID);
5827
5515
  let setgid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETGID);
5828
5516
  let sticky = getOptionValue(zipWriter, options, PROPERTY_NAME_STICKY);
5829
- if (uid !== UNDEFINED_VALUE && (uid < 0 || uid > MAX_32_BITS)) {
5517
+ if (uid !== UNDEFINED_VALUE && (!Number.isInteger(uid) || uid < 0 || uid > MAX_32_BITS)) {
5830
5518
  throw new Error(ERR_INVALID_UID);
5831
5519
  }
5832
- if (gid !== UNDEFINED_VALUE && (gid < 0 || gid > MAX_32_BITS)) {
5520
+ if (gid !== UNDEFINED_VALUE && (!Number.isInteger(gid) || gid < 0 || gid > MAX_32_BITS)) {
5833
5521
  throw new Error(ERR_INVALID_GID);
5834
5522
  }
5835
- if (unixMode !== UNDEFINED_VALUE && (unixMode < 0 || unixMode > MAX_16_BITS)) {
5523
+ if (unixMode !== UNDEFINED_VALUE && (!Number.isInteger(unixMode) || unixMode < 0 || unixMode > MAX_16_BITS)) {
5836
5524
  throw new Error(ERR_INVALID_UNIX_MODE);
5837
5525
  }
5838
5526
  if (unixExtraFieldType !== UNDEFINED_VALUE && unixExtraFieldType !== INFOZIP_EXTRA_FIELD_TYPE$1 && unixExtraFieldType !== UNIX_EXTRA_FIELD_TYPE) {
@@ -5944,7 +5632,7 @@
5944
5632
 
5945
5633
  function resolveMetadata(zipWriter, name, options) {
5946
5634
  const encode = getOptionValue(zipWriter, options, OPTION_ENCODE_TEXT, encodeText);
5947
- let rawFilename = encode(name);
5635
+ let rawFilename = encode(name, TEXT_TYPE_FILENAME);
5948
5636
  if (rawFilename === UNDEFINED_VALUE) {
5949
5637
  rawFilename = encodeText(name);
5950
5638
  }
@@ -5952,7 +5640,7 @@
5952
5640
  throw new Error(ERR_INVALID_ENTRY_NAME);
5953
5641
  }
5954
5642
  const comment = options[PROPERTY_NAME_COMMENT] || "";
5955
- let rawComment = encode(comment);
5643
+ let rawComment = encode(comment, TEXT_TYPE_COMMENT);
5956
5644
  if (rawComment === UNDEFINED_VALUE) {
5957
5645
  rawComment = encodeText(comment);
5958
5646
  }
@@ -5972,8 +5660,11 @@
5972
5660
  if (!passThrough) {
5973
5661
  password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
5974
5662
  rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
5663
+ if ((password && typeof password != STRING_TYPE) || (rawPassword && !(rawPassword instanceof Uint8Array))) {
5664
+ throw new Error(ERR_INVALID_PASSWORD_TYPE);
5665
+ }
5975
5666
  }
5976
- const encryptionStrength = getOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
5667
+ const encryptionStrength = getNumberOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
5977
5668
  const zipCrypto = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIPCRYPTO);
5978
5669
  const extendedTimestamp = getOptionValue(zipWriter, options, OPTION_EXTENDED_TIMESTAMP, true);
5979
5670
  const ntfsTimestamp = getOptionValue(zipWriter, options, OPTION_NTFS_TIMESTAMP);
@@ -5991,7 +5682,10 @@
5991
5682
  compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE && !registeredCodec) {
5992
5683
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
5993
5684
  }
5994
- let level = getOptionValue(zipWriter, options, OPTION_LEVEL);
5685
+ let level = getNumberOptionValue(zipWriter, options, OPTION_LEVEL);
5686
+ if (level !== UNDEFINED_VALUE && (!Number.isInteger(level) || level < 0 || level > MAX_LEVEL)) {
5687
+ throw new Error(ERR_INVALID_LEVEL);
5688
+ }
5995
5689
  if (zipWriter.options[OPTION_USDZ]) {
5996
5690
  if (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) {
5997
5691
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION_USDZ);
@@ -6012,7 +5706,7 @@
6012
5706
  useCompressionStream = false;
6013
5707
  }
6014
5708
  const zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
6015
- if (!zipCrypto && (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) && !(encryptionStrength >= 1 && encryptionStrength <= 3)) {
5709
+ if (!zipCrypto && (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) && !(Number.isInteger(encryptionStrength) && encryptionStrength >= 1 && encryptionStrength <= 3)) {
6016
5710
  throw new Error(ERR_INVALID_ENCRYPTION_STRENGTH);
6017
5711
  }
6018
5712
  const rawExtraField = serializeExtraField(options[PROPERTY_NAME_EXTRA_FIELD]);
@@ -7206,6 +6900,14 @@
7206
6900
  return result === UNDEFINED_VALUE ? defaultValue : result;
7207
6901
  }
7208
6902
 
6903
+ function getNumberOptionValue(zipWriter, options, name, defaultValue) {
6904
+ return toNumber(getOptionValue(zipWriter, options, name, defaultValue));
6905
+ }
6906
+
6907
+ function toNumber(value) {
6908
+ return typeof value == STRING_TYPE && value.trim() ? Number(value) : value;
6909
+ }
6910
+
7209
6911
  function getMaximumCompressedSize(uncompressedSize) {
7210
6912
  return uncompressedSize + (5 * (Math.floor(uncompressedSize / 16383) + 1));
7211
6913
  }
@@ -7340,6 +7042,7 @@
7340
7042
 
7341
7043
  const ERR_ENTRY_EXISTS = "Entry filename already exists";
7342
7044
  const ERR_READABLE_CONSUMED = "Readable stream already consumed";
7045
+ const ERR_ABORT_EXPORT = "zipjs-abort-export";
7343
7046
  const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
7344
7047
  const INTERPRETED_EXTRA_FIELD_TYPES = new Set([
7345
7048
  EXTRAFIELD_TYPE_ZIP64,
@@ -7448,8 +7151,9 @@
7448
7151
  const uncompressedSize = zipEntry.data ? zipEntry.data.uncompressedSize : reader.size;
7449
7152
  await Promise.all([initStream(reader), initStream(writer, uncompressedSize)]);
7450
7153
  const { readable } = reader;
7154
+ const { signal } = options;
7451
7155
  zipEntry.uncompressedSize = reader.size;
7452
- await toCompatibleReadable(readable).pipeTo(toCompatibleWritable(writer.writable));
7156
+ await toCompatibleReadable(readable).pipeTo(toCompatibleWritable(writer.writable), { signal });
7453
7157
  return writer.getData ? writer.getData() : writer.writable;
7454
7158
  }
7455
7159
  }
@@ -7716,7 +7420,7 @@
7716
7420
  for (const entry of entries) {
7717
7421
  let parent = this;
7718
7422
  try {
7719
- const path = entry.filename.split("/");
7423
+ const path = entry.filename.split("/").filter(pathPart => pathPart != "" && pathPart != ".");
7720
7424
  const name = path.pop();
7721
7425
  path.forEach(pathPart => {
7722
7426
  const previousParent = parent;
@@ -8091,6 +7795,39 @@
8091
7795
  }
8092
7796
  }
8093
7797
 
7798
+ function forwardAbort(signal, abortController) {
7799
+ if (!signal) {
7800
+ return () => { };
7801
+ }
7802
+ if (signal.aborted) {
7803
+ abortController.abort(signal.reason);
7804
+ return () => { };
7805
+ }
7806
+ const abort = () => abortController.abort(signal.reason);
7807
+ signal.addEventListener("abort", abort, { once: true });
7808
+ return () => signal.removeEventListener("abort", abort);
7809
+ }
7810
+
7811
+ function isExportAborted(error) {
7812
+ return Boolean(error) && error.message == ERR_ABORT_EXPORT;
7813
+ }
7814
+
7815
+ function aggregateEntryErrors(errors) {
7816
+ const [error] = errors;
7817
+ const otherErrors = errors
7818
+ .slice(1)
7819
+ .flatMap(otherError => [otherError, ...(otherError && otherError.entryErrors || [])])
7820
+ .filter(otherError => otherError !== error);
7821
+ if (otherErrors.length) {
7822
+ try {
7823
+ error.entryErrors = [...(error.entryErrors || []), ...otherErrors];
7824
+ } catch {
7825
+ // ignored
7826
+ }
7827
+ }
7828
+ return error;
7829
+ }
7830
+
8094
7831
  function getUserExtraField(extraField) {
8095
7832
  if (extraField) {
8096
7833
  const userExtraField = new Map();
@@ -8211,8 +7948,9 @@
8211
7948
  function addFileSystemHandle(zipEntry, handle, options) {
8212
7949
  return addFile(zipEntry, handle, []);
8213
7950
 
8214
- async function addFile(parentEntry, handle, addedEntries) {
7951
+ async function addFile(parentEntry, handle, addedEntries, parentName = "") {
8215
7952
  if (handle) {
7953
+ const entryName = parentName ? parentName + "/" + handle.name : handle.name;
8216
7954
  try {
8217
7955
  if (handle.isFile || handle.isDirectory) {
8218
7956
  handle = await transformToFileSystemhandle(handle);
@@ -8234,12 +7972,18 @@
8234
7972
  const directoryEntry = parentEntry.addDirectory(handle.name);
8235
7973
  addedEntries.push(directoryEntry);
8236
7974
  for await (const childHandle of handle.values()) {
8237
- await addFile(directoryEntry, childHandle, addedEntries);
7975
+ await addFile(directoryEntry, childHandle, addedEntries, entryName);
8238
7976
  }
8239
7977
  }
8240
7978
  } catch (error) {
8241
- const message = error.message + (handle ? " (" + handle.name + ")" : "");
8242
- throw new Error(message, { cause: error });
7979
+ try {
7980
+ if (error.entryName === UNDEFINED_VALUE) {
7981
+ error.entryName = entryName;
7982
+ }
7983
+ } catch {
7984
+ // ignored
7985
+ }
7986
+ throw error;
8243
7987
  }
8244
7988
  }
8245
7989
  return addedEntries;
@@ -8249,15 +7993,32 @@
8249
7993
  async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
8250
7994
  const totalSize = getTotalSize([zipEntry], "uncompressedSize");
8251
7995
  const writtenSizes = new Map();
8252
- await exportChildren(zipEntry, directoryHandle);
7996
+ const abortController = new AbortController();
7997
+ const { signal } = abortController;
7998
+ const releaseSignal = forwardAbort(options.signal, abortController);
7999
+ const exportedEntryNames = [];
8000
+ try {
8001
+ await exportChildren(zipEntry, directoryHandle);
8002
+ } catch (error) {
8003
+ try {
8004
+ error.exportedEntryNames = exportedEntryNames;
8005
+ } catch {
8006
+ // ignored
8007
+ }
8008
+ throw error;
8009
+ } finally {
8010
+ releaseSignal();
8011
+ }
8253
8012
  return directoryHandle;
8254
8013
 
8255
8014
  async function exportChildren(entry, parentHandle) {
8256
8015
  if (options.concurrent) {
8257
8016
  const results = await Promise.allSettled(entry.children.map(child => exportChild(child, parentHandle)));
8258
- const failedResult = results.find(result => result.status == "rejected");
8259
- if (failedResult) {
8260
- throw failedResult.reason;
8017
+ const rejectedResults = results.filter(result => result.status == "rejected");
8018
+ if (rejectedResults.length) {
8019
+ const failedResults = rejectedResults.filter(result => !isExportAborted(result.reason));
8020
+ const reportedResults = failedResults.length ? failedResults : rejectedResults;
8021
+ throw aggregateEntryErrors(reportedResults.map(result => result.reason));
8261
8022
  }
8262
8023
  } else {
8263
8024
  for (const child of entry.children) {
@@ -8267,6 +8028,12 @@
8267
8028
  }
8268
8029
 
8269
8030
  async function exportChild(child, parentHandle) {
8031
+ if (signal.aborted) {
8032
+ if (isExportAborted(signal.reason)) {
8033
+ return;
8034
+ }
8035
+ throw signal.reason;
8036
+ }
8270
8037
  try {
8271
8038
  if (child.directory) {
8272
8039
  const childDirectoryHandle = await parentHandle.getDirectoryHandle(child.name, { create: true });
@@ -8275,6 +8042,7 @@
8275
8042
  const fileHandle = await parentHandle.getFileHandle(child.name, { create: true });
8276
8043
  const writable = await fileHandle.createWritable();
8277
8044
  await child.getData({ writable }, Object.assign({}, options, {
8045
+ signal,
8278
8046
  onprogress: async progress => {
8279
8047
  if (options.onprogress) {
8280
8048
  writtenSizes.set(child.id, progress);
@@ -8286,9 +8054,19 @@
8286
8054
  }
8287
8055
  }
8288
8056
  }));
8057
+ exportedEntryNames.push(child.getRelativeName(zipEntry));
8289
8058
  }
8290
8059
  } catch (error) {
8291
- throw new Error(error.message + (child ? " (" + child.name + ")" : ""), { cause: error });
8060
+ abortController.abort(new Error(ERR_ABORT_EXPORT));
8061
+ try {
8062
+ if (error.entryName === UNDEFINED_VALUE) {
8063
+ error.entryName = child.getRelativeName(zipEntry);
8064
+ error.entryId = child.id;
8065
+ }
8066
+ } catch {
8067
+ // ignored
8068
+ }
8069
+ throw error;
8292
8070
  }
8293
8071
  }
8294
8072
  }