@zip.js/zip.js 2.8.49 → 2.8.51

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.
@@ -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,60 +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
- releaseWorkerStreams(workerData);
3519
- return runWorker$1(workerData, config);
3520
- }
3521
- if (error && error.codecImportFailed) {
3522
- if (workerData.reader) {
3523
- releaseWorkerStreams(workerData);
3524
- return runWorker$1(workerData, config);
3525
- }
3526
- workerData.onTaskFinished();
3527
- }
3528
- throw error;
3529
- }
3530
- }
3531
- }
3532
- });
3533
- }
3534
- return workerData.interface;
3535
- }
3536
-
3537
3459
  async function runWorker$1({ options, readable, writable, onTaskFinished }, config) {
3538
3460
  let codecStream;
3539
3461
  try {
@@ -3582,332 +3504,6 @@
3582
3504
  }
3583
3505
  }
3584
3506
 
3585
- async function runWebWorker(workerData, config) {
3586
- let resolveResult, rejectResult;
3587
- const result = new Promise((resolve, reject) => {
3588
- resolveResult = resolve;
3589
- rejectResult = reject;
3590
- });
3591
- Object.assign(workerData, {
3592
- reader: null,
3593
- writer: null,
3594
- resolveResult,
3595
- rejectResult,
3596
- result
3597
- });
3598
- const { readable, options } = workerData;
3599
- const { writable, closed, abortPipe } = watchClosedStream(workerData.writable);
3600
- let streamsTransferred;
3601
- try {
3602
- streamsTransferred = sendMessage({
3603
- type: MESSAGE_START,
3604
- options,
3605
- config,
3606
- readable,
3607
- writable
3608
- }, workerData);
3609
- } catch (error) {
3610
- abortPipe();
3611
- try {
3612
- await closed;
3613
- } catch {
3614
- // ignored
3615
- }
3616
- workerData.onTaskFinished();
3617
- throw error;
3618
- }
3619
- if (!streamsTransferred) {
3620
- Object.assign(workerData, {
3621
- reader: readable.getReader(),
3622
- writer: writable.getWriter()
3623
- });
3624
- }
3625
- const { workerStartupTimeout } = config;
3626
- if (!workerData.workerAlive && Number.isFinite(workerStartupTimeout) && workerStartupTimeout >= 0) {
3627
- workerData.startupTimeout = setTimeout(() => onStartupTimeout(workerData), workerStartupTimeout);
3628
- }
3629
- try {
3630
- const resultValue = await result;
3631
- await closeWritable();
3632
- await closed;
3633
- return resultValue;
3634
- } catch (error) {
3635
- await closeWritable();
3636
- abortPipe();
3637
- try {
3638
- await closed;
3639
- } catch {
3640
- // ignored
3641
- }
3642
- throw error;
3643
- }
3644
-
3645
- async function closeWritable() {
3646
- if (!streamsTransferred && !writable.locked) {
3647
- try {
3648
- await writable.getWriter().close();
3649
- } catch {
3650
- // ignored
3651
- }
3652
- }
3653
- }
3654
- }
3655
-
3656
- function watchClosedStream(writableSource) {
3657
- const abortController = new AbortController();
3658
- const { writable, readable } = new TransformStream();
3659
- const closed = readable.pipeTo(writableSource, { preventClose: true, preventAbort: true, signal: abortController.signal });
3660
- closed.catch(() => { });
3661
- return { writable, closed, abortPipe: () => abortController.abort() };
3662
- }
3663
-
3664
- function releaseWorkerStreams(workerData) {
3665
- const { reader } = workerData;
3666
- if (reader) {
3667
- reader.releaseLock();
3668
- }
3669
- workerData.reader = null;
3670
- workerData.writer = null;
3671
- }
3672
-
3673
- function terminateWorker$1(workerData) {
3674
- const { worker } = workerData;
3675
- if (worker) {
3676
- try {
3677
- worker.terminate();
3678
- } catch {
3679
- // ignored
3680
- }
3681
- }
3682
- workerData.interface = null;
3683
- }
3684
-
3685
- function getWebWorker(url, baseURI, workerData, isModuleType, useBlobURI = true) {
3686
- const { createWorker } = workerData;
3687
- let worker, resolvedURI, resolvedOptions;
3688
- if (createWorker) {
3689
- worker = createWorker();
3690
- } else if (webWorkerURI === UNDEFINED_VALUE || webWorkerSource !== url) {
3691
- // deno-lint-ignore valid-typeof
3692
- const isFunctionURI = typeof url == FUNCTION_TYPE;
3693
- if (isFunctionURI) {
3694
- resolvedURI = url(useBlobURI);
3695
- } else {
3696
- resolvedURI = url;
3697
- }
3698
- const isDataURI = resolvedURI.startsWith("data:");
3699
- const isBlobURI = resolvedURI.startsWith("blob:");
3700
- if (isDataURI || isBlobURI) {
3701
- if (isModuleType === UNDEFINED_VALUE) {
3702
- isModuleType = false;
3703
- }
3704
- if (isModuleType) {
3705
- resolvedOptions = MODULE_WORKER_OPTIONS;
3706
- }
3707
- try {
3708
- worker = new Worker(resolvedURI, resolvedOptions);
3709
- } catch (error) {
3710
- if (isBlobURI) {
3711
- try {
3712
- URL.revokeObjectURL(resolvedURI);
3713
- } catch {
3714
- // ignored
3715
- }
3716
- }
3717
- if (isFunctionURI && isBlobURI) {
3718
- return getWebWorker(url, baseURI, workerData, isModuleType, false);
3719
- } else if (!isModuleType) {
3720
- return getWebWorker(url, baseURI, workerData, true, false);
3721
- } else {
3722
- throw error;
3723
- }
3724
- }
3725
- } else {
3726
- if (isModuleType === UNDEFINED_VALUE) {
3727
- isModuleType = true;
3728
- }
3729
- if (isModuleType) {
3730
- resolvedOptions = MODULE_WORKER_OPTIONS;
3731
- }
3732
- try {
3733
- resolvedURI = new URL(resolvedURI, baseURI);
3734
- } catch {
3735
- // ignored
3736
- }
3737
- try {
3738
- worker = new Worker(resolvedURI, resolvedOptions);
3739
- } catch (error) {
3740
- if (isModuleType) {
3741
- return getWebWorker(url, baseURI, workerData, false, useBlobURI);
3742
- } else {
3743
- throw error;
3744
- }
3745
- }
3746
- }
3747
- webWorkerSource = url;
3748
- webWorkerURI = resolvedURI;
3749
- webWorkerOptions = resolvedOptions;
3750
- } else {
3751
- worker = new Worker(webWorkerURI, webWorkerOptions);
3752
- }
3753
- worker.addEventListener(MESSAGE_EVENT_TYPE, event => {
3754
- workerData.workerAlive = true;
3755
- clearStartupTimeout(workerData);
3756
- onMessage(event, workerData);
3757
- });
3758
- worker.addEventListener(ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
3759
- worker.addEventListener(MESSAGE_ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
3760
- return worker;
3761
- }
3762
-
3763
- function onStartupTimeout(workerData) {
3764
- workerData.startupTimeout = null;
3765
- if (workerData.workerAlive) {
3766
- return;
3767
- }
3768
- const { rejectResult, writer } = workerData;
3769
- terminateWorker$1(workerData);
3770
- workerData.worker = null;
3771
- if (rejectResult) {
3772
- const error = new Error(ERR_WORKER_STARTUP_TIMEOUT);
3773
- error.workerStartupFailed = true;
3774
- rejectResult(error);
3775
- if (writer) {
3776
- writer.releaseLock();
3777
- }
3778
- }
3779
- }
3780
-
3781
- function clearStartupTimeout(workerData) {
3782
- const { startupTimeout } = workerData;
3783
- if (startupTimeout) {
3784
- clearTimeout(startupTimeout);
3785
- workerData.startupTimeout = null;
3786
- }
3787
- }
3788
-
3789
- function onWorkerError(event, workerData) {
3790
- if (event.preventDefault) {
3791
- event.preventDefault();
3792
- }
3793
- clearStartupTimeout(workerData);
3794
- const { workerAlive, rejectResult, writer, onTaskFinished } = workerData;
3795
- terminateWorker$1(workerData);
3796
- if (!workerAlive) {
3797
- workerData.worker = null;
3798
- }
3799
- if (rejectResult) {
3800
- let error = event.error || new Error(event.message || ERROR_EVENT_TYPE);
3801
- if (!workerAlive) {
3802
- error = Object.assign(new Error(error.message || ERROR_EVENT_TYPE), { workerStartupFailed: true });
3803
- }
3804
- rejectResult(error);
3805
- if (writer) {
3806
- writer.releaseLock();
3807
- }
3808
- if (workerAlive) {
3809
- onTaskFinished();
3810
- }
3811
- }
3812
- }
3813
-
3814
- function sendMessage(message, { worker, writer, transferStreams, workerAlive }) {
3815
- try {
3816
- const { value, readable, writable } = message;
3817
- const transferables = [];
3818
- if (value) {
3819
- message.value = toExactUint8Array(value);
3820
- transferables.push(message.value.buffer);
3821
- }
3822
- if (transferStreams && transferStreamsSupported && workerAlive) {
3823
- if (readable) {
3824
- transferables.push(readable);
3825
- }
3826
- if (writable) {
3827
- transferables.push(writable);
3828
- }
3829
- } else {
3830
- message.readable = message.writable = null;
3831
- }
3832
- if (transferables.length) {
3833
- try {
3834
- worker.postMessage(message, transferables);
3835
- return true;
3836
- } catch {
3837
- transferStreamsSupported = false;
3838
- message.readable = message.writable = null;
3839
- worker.postMessage(message);
3840
- }
3841
- } else {
3842
- worker.postMessage(message);
3843
- }
3844
- } catch (error) {
3845
- if (writer) {
3846
- writer.releaseLock();
3847
- }
3848
- throw error;
3849
- }
3850
- }
3851
-
3852
- async function onMessage({ data }, workerData) {
3853
- const { type, value, messageId, result, error } = data;
3854
- const { reader, writer, resolveResult, rejectResult, onTaskFinished, generation } = workerData;
3855
- const stale = () => workerData.generation != generation;
3856
- try {
3857
- if (error) {
3858
- const { message, stack, code, name, outputSize, cause, codecImportFailed } = error;
3859
- const responseError = new Error(message);
3860
- Object.assign(responseError, { stack, code, name, outputSize });
3861
- if (cause) {
3862
- responseError.cause = Object.assign(new Error(cause.message), { name: cause.name });
3863
- }
3864
- if (codecImportFailed) {
3865
- responseError.codecImportFailed = true;
3866
- }
3867
- close(responseError);
3868
- } else {
3869
- if (type == MESSAGE_PULL) {
3870
- const { value, done } = await reader.read();
3871
- if (!stale()) {
3872
- sendMessage({ type: MESSAGE_DATA, value, done, messageId }, workerData);
3873
- }
3874
- }
3875
- if (type == MESSAGE_DATA) {
3876
- await writer.ready;
3877
- await writer.write(new Uint8Array(value));
3878
- if (!stale()) {
3879
- sendMessage({ type: MESSAGE_ACK_DATA, messageId }, workerData);
3880
- }
3881
- }
3882
- if (type == MESSAGE_CLOSE) {
3883
- close(null, result);
3884
- }
3885
- }
3886
- } catch (error) {
3887
- if (!stale()) {
3888
- terminateWorker$1(workerData);
3889
- close(error);
3890
- }
3891
- }
3892
-
3893
- function close(error, result) {
3894
- if (stale()) {
3895
- return;
3896
- }
3897
- if (error) {
3898
- rejectResult(error);
3899
- } else {
3900
- resolveResult(result);
3901
- }
3902
- if (writer) {
3903
- writer.releaseLock();
3904
- }
3905
- if (!(error && error.codecImportFailed)) {
3906
- onTaskFinished();
3907
- }
3908
- }
3909
- }
3910
-
3911
3507
  /*
3912
3508
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.
3913
3509
 
@@ -4350,9 +3946,13 @@
4350
3946
  const OPTION_UNIX_EXTRA_FIELD_TYPE = "unixExtraFieldType";
4351
3947
  const OPTION_LOCAL_EXTRA_FIELD = "localExtraField";
4352
3948
  const OPTION_STRICTNESS = "strictness";
3949
+ const OPTION_FILENAME_VALIDATION = "filenameValidation";
3950
+ const OPTION_NORMALIZE_FILENAME = "normalizeFilename";
4353
3951
  const OPTION_MAX_APPENDED_DATA_SIZE = "maxAppendedDataSize";
4354
3952
  const OPTION_DECRYPT_CENTRAL_DIRECTORY = "decryptCentralDirectory";
4355
3953
  const OPTION_SIGN_CENTRAL_DIRECTORY = "signCentralDirectory";
3954
+ const TEXT_TYPE_FILENAME = "filename";
3955
+ const TEXT_TYPE_COMMENT = "comment";
4356
3956
  const STRICTNESS_STRICT = "strict";
4357
3957
  const STRICTNESS_BALANCED = "balanced";
4358
3958
  const STRICTNESS_TOLERANT = "tolerant";
@@ -4399,6 +3999,11 @@
4399
3999
  const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4400
4000
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4401
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]:/;
4402
4007
  const CHARSET_UTF8 = "utf-8";
4403
4008
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
4404
4009
  const CHARSET_CP437 = "cp437";
@@ -4420,6 +4025,10 @@
4420
4025
  bytes: 8
4421
4026
  }
4422
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;
4423
4032
 
4424
4033
  class ZipReader {
4425
4034
 
@@ -4448,6 +4057,8 @@
4448
4057
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
4449
4058
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
4450
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);
4451
4062
  const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
4452
4063
  if (!endOfDirectoryInfo) {
4453
4064
  const signatureArray = await readUint8Array(reader, 0, 4);
@@ -4641,11 +4252,22 @@
4641
4252
  const decode = getOptionValue$1(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
4642
4253
  const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
4643
4254
  const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
4644
- let filename = decode(rawFilename, rawFilenameEncoding);
4255
+ let filename = decode(rawFilename, rawFilenameEncoding, TEXT_TYPE_FILENAME);
4645
4256
  if (filename === UNDEFINED_VALUE) {
4646
4257
  filename = decodeText(rawFilename, rawFilenameEncoding);
4647
4258
  }
4648
- 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);
4649
4271
  if (comment === UNDEFINED_VALUE) {
4650
4272
  comment = decodeText(rawComment, rawCommentEncoding);
4651
4273
  }
@@ -4706,10 +4328,10 @@
4706
4328
  entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
4707
4329
  entry.arrayBuffer = async options => {
4708
4330
  const writer = new TransformStream();
4709
- const [arrayBuffer] = await Promise.all([
4710
- streamToBlob(writer.readable).then(blob => blob.arrayBuffer()),
4711
- fileEntry.getData(writer, entry, zipReader.readRanges, options)]);
4712
- 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;
4713
4335
  };
4714
4336
  offset = endOffset;
4715
4337
  const { onprogress } = options;
@@ -4920,7 +4542,7 @@
4920
4542
  readRanges
4921
4543
  });
4922
4544
  }
4923
- let writable;
4545
+ let writable, abortError;
4924
4546
  try {
4925
4547
  if (!checkOverlappingEntryOnly) {
4926
4548
  if (checkPasswordOnly) {
@@ -4940,12 +4562,22 @@
4940
4562
  writer.size += error.outputSize;
4941
4563
  }
4942
4564
  if (!checkPasswordOnly || error.message != ERR_ABORT_CHECK_PASSWORD) {
4565
+ abortError = error;
4943
4566
  throw error;
4944
4567
  }
4945
4568
  } finally {
4946
4569
  const preventClose = getOptionValue$1(zipEntry, options, OPTION_PREVENT_CLOSE);
4947
4570
  if (!preventClose && writable && !writable.locked) {
4948
- 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
+ }
4949
4581
  }
4950
4582
  }
4951
4583
  return checkPasswordOnly || checkOverlappingEntryOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
@@ -5325,16 +4957,51 @@
5325
4957
  return reader.getDiskOffset ? reader.getDiskOffset(diskNumber) : 0;
5326
4958
  }
5327
4959
 
4960
+ function isStrictnessValue(value) {
4961
+ return value === STRICTNESS_STRICT || value === STRICTNESS_BALANCED || value === STRICTNESS_TOLERANT;
4962
+ }
4963
+
5328
4964
  function getStrictness(strictness, checkAmbiguity) {
5329
4965
  if (strictness === UNDEFINED_VALUE) {
5330
4966
  return checkAmbiguity ? STRICTNESS_STRICT : STRICTNESS_BALANCED;
5331
4967
  }
4968
+ if (!isStrictnessValue(strictness)) {
4969
+ throw new Error(ERR_INVALID_STRICTNESS);
4970
+ }
5332
4971
  return strictness;
5333
4972
  }
5334
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
+
5335
4998
  function getMaxAppendedDataSize(maxAppendedDataSize, strictness) {
5336
4999
  if (maxAppendedDataSize !== UNDEFINED_VALUE) {
5337
- 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;
5338
5005
  }
5339
5006
  if (strictness == STRICTNESS_STRICT) {
5340
5007
  return 0;
@@ -5345,12 +5012,6 @@
5345
5012
  return MAX_16_BITS;
5346
5013
  }
5347
5014
 
5348
- const MAX_END_OF_CENTRAL_DIR_PROBES = 64;
5349
-
5350
- const CENTRAL_DIRECTORY_UNREACHABLE = 0;
5351
- const CENTRAL_DIRECTORY_PLAUSIBLE = 1;
5352
- const CENTRAL_DIRECTORY_REACHABLE = 2;
5353
-
5354
5015
  async function findEndOfCentralDirectory(reader, rejectAmbiguous, maxAppendedDataSize) {
5355
5016
  const { size } = reader;
5356
5017
  const anchoredLength = Math.min(size, END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS);
@@ -5486,6 +5147,10 @@
5486
5147
  return options[name] === UNDEFINED_VALUE ? zipReader.options[name] : options[name];
5487
5148
  }
5488
5149
 
5150
+ function toNumber$1(value) {
5151
+ return typeof value == STRING_TYPE && value.trim() ? Number(value) : value;
5152
+ }
5153
+
5489
5154
  function getDate(timeRaw) {
5490
5155
  const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
5491
5156
  return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
@@ -5524,15 +5189,18 @@
5524
5189
  ERR_INVALID_AUTHENTICATION_CODE: ERR_INVALID_AUTHENTICATION_CODE,
5525
5190
  ERR_INVALID_COMPRESSED_DATA: ERR_INVALID_COMPRESSED_DATA,
5526
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,
5527
5194
  ERR_INVALID_PASSWORD: ERR_INVALID_PASSWORD,
5528
5195
  ERR_INVALID_SIGNATURE: ERR_INVALID_SIGNATURE,
5196
+ ERR_INVALID_STRICTNESS: ERR_INVALID_STRICTNESS,
5529
5197
  ERR_INVALID_UNCOMPRESSED_SIZE: ERR_INVALID_UNCOMPRESSED_SIZE,
5530
5198
  ERR_LOCAL_FILE_HEADER_NOT_FOUND: ERR_LOCAL_FILE_HEADER_NOT_FOUND,
5531
5199
  ERR_OVERLAPPING_ENTRY: ERR_OVERLAPPING_ENTRY,
5532
5200
  ERR_SPLIT_ZIP_FILE: ERR_SPLIT_ZIP_FILE,
5201
+ ERR_UNSAFE_FILENAME: ERR_UNSAFE_FILENAME,
5533
5202
  ERR_UNSUPPORTED_COMPRESSION: ERR_UNSUPPORTED_COMPRESSION$1,
5534
5203
  ERR_UNSUPPORTED_ENCRYPTION: ERR_UNSUPPORTED_ENCRYPTION,
5535
- ERR_WORKER_STARTUP_TIMEOUT: ERR_WORKER_STARTUP_TIMEOUT,
5536
5204
  ZipReader: ZipReader
5537
5205
  });
5538
5206
 
@@ -5590,6 +5258,8 @@
5590
5258
  const ERR_INVALID_UNIX_ID_SIZE = "uid/gid must be 0..65535 for unixExtraFieldType 'unix' (use 'infozip' for larger ids)";
5591
5259
  const ERR_INVALID_MSDOS_ATTRIBUTES = "Invalid msdosAttributesRaw (must be integer 0..255)";
5592
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)";
5593
5263
  const ERR_INVALID_SIGNATURE_DATA = "Signature data exceeds 64KB";
5594
5264
 
5595
5265
  const EXTRAFIELD_DATA_AES = new Uint8Array([0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00]);
@@ -5597,6 +5267,7 @@
5597
5267
  const VENDOR_VERSION_AE_1 = 1;
5598
5268
  const INFOZIP_EXTRA_FIELD_TYPE$1 = "infozip";
5599
5269
  const UNIX_EXTRA_FIELD_TYPE = "unix";
5270
+ const MAX_LEVEL = 9;
5600
5271
 
5601
5272
  let workers = 0;
5602
5273
  const pendingEntries = [];
@@ -5759,7 +5430,8 @@
5759
5430
 
5760
5431
  remove(entry) {
5761
5432
  const { filenames, files } = this;
5762
- if (typeof entry == "string") {
5433
+ // deno-lint-ignore valid-typeof
5434
+ if (typeof entry == STRING_TYPE) {
5763
5435
  entry = files.get(entry);
5764
5436
  }
5765
5437
  if (entry && entry.filename !== UNDEFINED_VALUE) {
@@ -5835,20 +5507,20 @@
5835
5507
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
5836
5508
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? 20 : 768);
5837
5509
  const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
5838
- const uid = getOptionValue(zipWriter, options, PROPERTY_NAME_UID);
5839
- const gid = getOptionValue(zipWriter, options, PROPERTY_NAME_GID);
5840
- 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);
5841
5513
  let unixExtraFieldType = getOptionValue(zipWriter, options, OPTION_UNIX_EXTRA_FIELD_TYPE);
5842
5514
  let setuid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETUID);
5843
5515
  let setgid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETGID);
5844
5516
  let sticky = getOptionValue(zipWriter, options, PROPERTY_NAME_STICKY);
5845
- if (uid !== UNDEFINED_VALUE && (uid < 0 || uid > MAX_32_BITS)) {
5517
+ if (uid !== UNDEFINED_VALUE && (!Number.isInteger(uid) || uid < 0 || uid > MAX_32_BITS)) {
5846
5518
  throw new Error(ERR_INVALID_UID);
5847
5519
  }
5848
- if (gid !== UNDEFINED_VALUE && (gid < 0 || gid > MAX_32_BITS)) {
5520
+ if (gid !== UNDEFINED_VALUE && (!Number.isInteger(gid) || gid < 0 || gid > MAX_32_BITS)) {
5849
5521
  throw new Error(ERR_INVALID_GID);
5850
5522
  }
5851
- if (unixMode !== UNDEFINED_VALUE && (unixMode < 0 || unixMode > MAX_16_BITS)) {
5523
+ if (unixMode !== UNDEFINED_VALUE && (!Number.isInteger(unixMode) || unixMode < 0 || unixMode > MAX_16_BITS)) {
5852
5524
  throw new Error(ERR_INVALID_UNIX_MODE);
5853
5525
  }
5854
5526
  if (unixExtraFieldType !== UNDEFINED_VALUE && unixExtraFieldType !== INFOZIP_EXTRA_FIELD_TYPE$1 && unixExtraFieldType !== UNIX_EXTRA_FIELD_TYPE) {
@@ -5960,7 +5632,7 @@
5960
5632
 
5961
5633
  function resolveMetadata(zipWriter, name, options) {
5962
5634
  const encode = getOptionValue(zipWriter, options, OPTION_ENCODE_TEXT, encodeText);
5963
- let rawFilename = encode(name);
5635
+ let rawFilename = encode(name, TEXT_TYPE_FILENAME);
5964
5636
  if (rawFilename === UNDEFINED_VALUE) {
5965
5637
  rawFilename = encodeText(name);
5966
5638
  }
@@ -5968,7 +5640,7 @@
5968
5640
  throw new Error(ERR_INVALID_ENTRY_NAME);
5969
5641
  }
5970
5642
  const comment = options[PROPERTY_NAME_COMMENT] || "";
5971
- let rawComment = encode(comment);
5643
+ let rawComment = encode(comment, TEXT_TYPE_COMMENT);
5972
5644
  if (rawComment === UNDEFINED_VALUE) {
5973
5645
  rawComment = encodeText(comment);
5974
5646
  }
@@ -5988,8 +5660,11 @@
5988
5660
  if (!passThrough) {
5989
5661
  password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
5990
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
+ }
5991
5666
  }
5992
- const encryptionStrength = getOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
5667
+ const encryptionStrength = getNumberOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
5993
5668
  const zipCrypto = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIPCRYPTO);
5994
5669
  const extendedTimestamp = getOptionValue(zipWriter, options, OPTION_EXTENDED_TIMESTAMP, true);
5995
5670
  const ntfsTimestamp = getOptionValue(zipWriter, options, OPTION_NTFS_TIMESTAMP);
@@ -6007,7 +5682,10 @@
6007
5682
  compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE && !registeredCodec) {
6008
5683
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
6009
5684
  }
6010
- 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
+ }
6011
5689
  if (zipWriter.options[OPTION_USDZ]) {
6012
5690
  if (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) {
6013
5691
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION_USDZ);
@@ -6028,7 +5706,7 @@
6028
5706
  useCompressionStream = false;
6029
5707
  }
6030
5708
  const zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
6031
- 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)) {
6032
5710
  throw new Error(ERR_INVALID_ENCRYPTION_STRENGTH);
6033
5711
  }
6034
5712
  const rawExtraField = serializeExtraField(options[PROPERTY_NAME_EXTRA_FIELD]);
@@ -7222,6 +6900,14 @@
7222
6900
  return result === UNDEFINED_VALUE ? defaultValue : result;
7223
6901
  }
7224
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
+
7225
6911
  function getMaximumCompressedSize(uncompressedSize) {
7226
6912
  return uncompressedSize + (5 * (Math.floor(uncompressedSize / 16383) + 1));
7227
6913
  }
@@ -7356,6 +7042,9 @@
7356
7042
 
7357
7043
  const ERR_ENTRY_EXISTS = "Entry filename already exists";
7358
7044
  const ERR_READABLE_CONSUMED = "Readable stream already consumed";
7045
+ const ERR_ABORT_EXPORT = "zipjs-abort-export";
7046
+ const ERR_ABORTED = "The operation was aborted";
7047
+ const ABORT_ERROR_NAME = "AbortError";
7359
7048
  const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
7360
7049
  const INTERPRETED_EXTRA_FIELD_TYPES = new Set([
7361
7050
  EXTRAFIELD_TYPE_ZIP64,
@@ -7464,8 +7153,9 @@
7464
7153
  const uncompressedSize = zipEntry.data ? zipEntry.data.uncompressedSize : reader.size;
7465
7154
  await Promise.all([initStream(reader), initStream(writer, uncompressedSize)]);
7466
7155
  const { readable } = reader;
7156
+ const { signal } = options;
7467
7157
  zipEntry.uncompressedSize = reader.size;
7468
- await toCompatibleReadable(readable).pipeTo(toCompatibleWritable(writer.writable));
7158
+ await toCompatibleReadable(readable).pipeTo(toCompatibleWritable(writer.writable), { signal });
7469
7159
  return writer.getData ? writer.getData() : writer.writable;
7470
7160
  }
7471
7161
  }
@@ -7732,7 +7422,7 @@
7732
7422
  for (const entry of entries) {
7733
7423
  let parent = this;
7734
7424
  try {
7735
- const path = entry.filename.split("/");
7425
+ const path = entry.filename.split("/").filter(pathPart => pathPart != "" && pathPart != ".");
7736
7426
  const name = path.pop();
7737
7427
  path.forEach(pathPart => {
7738
7428
  const previousParent = parent;
@@ -7807,6 +7497,10 @@
7807
7497
  }
7808
7498
  }
7809
7499
 
7500
+ getChildren(options = {}) {
7501
+ return collectChildren(this, options.recursive);
7502
+ }
7503
+
7810
7504
  isPasswordProtected() {
7811
7505
  const children = this.children;
7812
7506
  for (let childIndex = 0; childIndex < children.length; childIndex++) {
@@ -7898,6 +7592,10 @@
7898
7592
  return this.root.getChildByName(name);
7899
7593
  }
7900
7594
 
7595
+ getChildren(options) {
7596
+ return this.root.getChildren(options);
7597
+ }
7598
+
7901
7599
  addDirectory(name, options) {
7902
7600
  return this.root.addDirectory(name, options);
7903
7601
  }
@@ -8107,6 +7805,39 @@
8107
7805
  }
8108
7806
  }
8109
7807
 
7808
+ function forwardAbort(signal, abortController) {
7809
+ if (!signal) {
7810
+ return () => { };
7811
+ }
7812
+ if (signal.aborted) {
7813
+ abortController.abort(signal.reason);
7814
+ return () => { };
7815
+ }
7816
+ const abort = () => abortController.abort(signal.reason);
7817
+ signal.addEventListener("abort", abort, { once: true });
7818
+ return () => signal.removeEventListener("abort", abort);
7819
+ }
7820
+
7821
+ function isExportAborted(error) {
7822
+ return Boolean(error) && error.message == ERR_ABORT_EXPORT;
7823
+ }
7824
+
7825
+ function aggregateEntryErrors(errors) {
7826
+ const [error] = errors;
7827
+ const otherErrors = errors
7828
+ .slice(1)
7829
+ .flatMap(otherError => [otherError, ...(otherError && otherError.entryErrors || [])])
7830
+ .filter(otherError => otherError !== error);
7831
+ if (otherErrors.length) {
7832
+ try {
7833
+ error.entryErrors = [...(error.entryErrors || []), ...otherErrors];
7834
+ } catch {
7835
+ // ignored
7836
+ }
7837
+ }
7838
+ return error;
7839
+ }
7840
+
8110
7841
  function getUserExtraField(extraField) {
8111
7842
  if (extraField) {
8112
7843
  const userExtraField = new Map();
@@ -8124,111 +7855,110 @@
8124
7855
  async function exportZip(zipWriter, entry, totalSize, options, readers) {
8125
7856
  const selectedEntry = entry;
8126
7857
  const entryOffsets = new Map();
8127
- await process(zipWriter, entry);
7858
+ if (options.bufferedWrite) {
7859
+ await processChildren(entry);
7860
+ } else {
7861
+ for (const child of entry.getChildren({ recursive: true })) {
7862
+ await addChild(child);
7863
+ }
7864
+ }
8128
7865
 
8129
- async function process(zipWriter, entry) {
8130
- await exportChild();
7866
+ async function processChildren(entry) {
7867
+ const results = await Promise.allSettled(entry.children.map(async child => {
7868
+ await addChild(child);
7869
+ await processChildren(child);
7870
+ }));
7871
+ const errorResult = results.find(result => result.status == "rejected");
7872
+ if (errorResult) {
7873
+ throw errorResult.reason;
7874
+ }
7875
+ }
8131
7876
 
8132
- async function exportChild() {
8133
- if (options.bufferedWrite) {
8134
- const results = await Promise.allSettled(entry.children.map(processChild));
8135
- const errorResult = results.find(result => result.status == "rejected");
8136
- if (errorResult) {
8137
- throw errorResult.reason;
7877
+ async function addChild(child) {
7878
+ const name = options.relativePath ? child.getRelativeName(selectedEntry) : child.getFullname();
7879
+ const childOptions = child.options || {};
7880
+ let zipEntryOptions = {};
7881
+ if (child.data instanceof Entry) {
7882
+ const {
7883
+ externalFileAttributes,
7884
+ versionMadeBy,
7885
+ comment,
7886
+ lastModDate,
7887
+ creationDate,
7888
+ lastAccessDate,
7889
+ uncompressedSize,
7890
+ encrypted,
7891
+ zipCrypto,
7892
+ crc32,
7893
+ compressionMethod,
7894
+ extraFieldAES,
7895
+ internalFileAttributes,
7896
+ extraField,
7897
+ uid,
7898
+ gid
7899
+ } = child.data;
7900
+ zipEntryOptions = {
7901
+ externalFileAttributes,
7902
+ versionMadeBy,
7903
+ comment,
7904
+ lastModDate,
7905
+ creationDate,
7906
+ lastAccessDate,
7907
+ internalFileAttributes
7908
+ };
7909
+ const userExtraField = getUserExtraField(extraField);
7910
+ if (userExtraField) {
7911
+ zipEntryOptions.extraField = userExtraField;
7912
+ }
7913
+ if (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE) {
7914
+ Object.assign(zipEntryOptions, {
7915
+ uid,
7916
+ gid,
7917
+ unixExtraFieldType: options.unixExtraFieldType || INFOZIP_EXTRA_FIELD_TYPE
7918
+ });
7919
+ }
7920
+ if (child.passThrough) {
7921
+ let level, encryptionStrength;
7922
+ if (compressionMethod === 0) {
7923
+ level = 0;
8138
7924
  }
8139
- } else {
8140
- for (const child of entry.children) {
8141
- await processChild(child);
7925
+ if (extraFieldAES) {
7926
+ encryptionStrength = extraFieldAES.strength;
8142
7927
  }
8143
- }
8144
- }
8145
-
8146
- async function processChild(child) {
8147
- const name = options.relativePath ? child.getRelativeName(selectedEntry) : child.getFullname();
8148
- const childOptions = child.options || {};
8149
- let zipEntryOptions = {};
8150
- if (child.data instanceof Entry) {
8151
- const {
8152
- externalFileAttributes,
8153
- versionMadeBy,
8154
- comment,
8155
- lastModDate,
8156
- creationDate,
8157
- lastAccessDate,
8158
- uncompressedSize,
7928
+ zipEntryOptions = Object.assign(zipEntryOptions, {
7929
+ passThrough: true,
8159
7930
  encrypted,
8160
7931
  zipCrypto,
8161
7932
  crc32,
8162
- compressionMethod,
8163
- extraFieldAES,
8164
- internalFileAttributes,
8165
- extraField,
8166
- uid,
8167
- gid
8168
- } = child.data;
8169
- zipEntryOptions = {
8170
- externalFileAttributes,
8171
- versionMadeBy,
8172
- comment,
8173
- lastModDate,
8174
- creationDate,
8175
- lastAccessDate,
8176
- internalFileAttributes
8177
- };
8178
- const userExtraField = getUserExtraField(extraField);
8179
- if (userExtraField) {
8180
- zipEntryOptions.extraField = userExtraField;
8181
- }
8182
- if (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE) {
8183
- Object.assign(zipEntryOptions, {
8184
- uid,
8185
- gid,
8186
- unixExtraFieldType: options.unixExtraFieldType || INFOZIP_EXTRA_FIELD_TYPE
8187
- });
8188
- }
8189
- if (child.passThrough) {
8190
- let level, encryptionStrength;
8191
- if (compressionMethod === 0) {
8192
- level = 0;
8193
- }
8194
- if (extraFieldAES) {
8195
- encryptionStrength = extraFieldAES.strength;
8196
- }
8197
- zipEntryOptions = Object.assign(zipEntryOptions, {
8198
- passThrough: true,
8199
- encrypted,
8200
- zipCrypto,
8201
- crc32,
8202
- uncompressedSize,
8203
- level,
8204
- encryptionStrength,
8205
- compressionMethod
8206
- });
8207
- }
7933
+ uncompressedSize,
7934
+ level,
7935
+ encryptionStrength,
7936
+ compressionMethod
7937
+ });
8208
7938
  }
8209
- await zipWriter.add(name, readers.get(child), Object.assign({}, options, zipEntryOptions, childOptions, {
8210
- directory: child.directory,
8211
- onprogress: async indexProgress => {
8212
- if (options.onprogress) {
8213
- entryOffsets.set(name, indexProgress);
8214
- try {
8215
- await options.onprogress(Array.from(entryOffsets.values()).reduce((previousValue, currentValue) => previousValue + currentValue), totalSize);
8216
- } catch {
8217
- // ignored
8218
- }
7939
+ }
7940
+ await zipWriter.add(name, readers.get(child), Object.assign({}, options, zipEntryOptions, childOptions, {
7941
+ directory: child.directory,
7942
+ onprogress: async indexProgress => {
7943
+ if (options.onprogress) {
7944
+ entryOffsets.set(name, indexProgress);
7945
+ try {
7946
+ await options.onprogress(Array.from(entryOffsets.values()).reduce((previousValue, currentValue) => previousValue + currentValue), totalSize);
7947
+ } catch {
7948
+ // ignored
8219
7949
  }
8220
7950
  }
8221
- }));
8222
- await process(zipWriter, child);
8223
- }
7951
+ }
7952
+ }));
8224
7953
  }
8225
7954
  }
8226
7955
 
8227
7956
  function addFileSystemHandle(zipEntry, handle, options) {
8228
7957
  return addFile(zipEntry, handle, []);
8229
7958
 
8230
- async function addFile(parentEntry, handle, addedEntries) {
7959
+ async function addFile(parentEntry, handle, addedEntries, parentName = "") {
8231
7960
  if (handle) {
7961
+ const entryName = parentName ? parentName + "/" + handle.name : handle.name;
8232
7962
  try {
8233
7963
  if (handle.isFile || handle.isDirectory) {
8234
7964
  handle = await transformToFileSystemhandle(handle);
@@ -8250,12 +7980,18 @@
8250
7980
  const directoryEntry = parentEntry.addDirectory(handle.name);
8251
7981
  addedEntries.push(directoryEntry);
8252
7982
  for await (const childHandle of handle.values()) {
8253
- await addFile(directoryEntry, childHandle, addedEntries);
7983
+ await addFile(directoryEntry, childHandle, addedEntries, entryName);
8254
7984
  }
8255
7985
  }
8256
7986
  } catch (error) {
8257
- const message = error.message + (handle ? " (" + handle.name + ")" : "");
8258
- throw new Error(message, { cause: error });
7987
+ try {
7988
+ if (error.entryName === UNDEFINED_VALUE) {
7989
+ error.entryName = entryName;
7990
+ }
7991
+ } catch {
7992
+ // ignored
7993
+ }
7994
+ throw error;
8259
7995
  }
8260
7996
  }
8261
7997
  return addedEntries;
@@ -8265,15 +8001,33 @@
8265
8001
  async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
8266
8002
  const totalSize = getTotalSize([zipEntry], "uncompressedSize");
8267
8003
  const writtenSizes = new Map();
8268
- await exportChildren(zipEntry, directoryHandle);
8004
+ const abortController = new AbortController();
8005
+ const { signal } = abortController;
8006
+ const releaseSignal = forwardAbort(options.signal, abortController);
8007
+ const exportedEntryNames = [];
8008
+ let exportAborted = false;
8009
+ try {
8010
+ await exportChildren(zipEntry, directoryHandle);
8011
+ } catch (error) {
8012
+ try {
8013
+ error.exportedEntryNames = exportedEntryNames;
8014
+ } catch {
8015
+ // ignored
8016
+ }
8017
+ throw error;
8018
+ } finally {
8019
+ releaseSignal();
8020
+ }
8269
8021
  return directoryHandle;
8270
8022
 
8271
8023
  async function exportChildren(entry, parentHandle) {
8272
8024
  if (options.concurrent) {
8273
8025
  const results = await Promise.allSettled(entry.children.map(child => exportChild(child, parentHandle)));
8274
- const failedResult = results.find(result => result.status == "rejected");
8275
- if (failedResult) {
8276
- throw failedResult.reason;
8026
+ const rejectedResults = results.filter(result => result.status == "rejected");
8027
+ if (rejectedResults.length) {
8028
+ const failedResults = rejectedResults.filter(result => !isExportAborted(result.reason));
8029
+ const reportedResults = failedResults.length ? failedResults : rejectedResults;
8030
+ throw aggregateEntryErrors(reportedResults.map(result => result.reason));
8277
8031
  }
8278
8032
  } else {
8279
8033
  for (const child of entry.children) {
@@ -8283,6 +8037,12 @@
8283
8037
  }
8284
8038
 
8285
8039
  async function exportChild(child, parentHandle) {
8040
+ if (signal.aborted) {
8041
+ if (exportAborted || isExportAborted(signal.reason)) {
8042
+ return;
8043
+ }
8044
+ throw signal.reason === UNDEFINED_VALUE ? new DOMException(ERR_ABORTED, ABORT_ERROR_NAME) : signal.reason;
8045
+ }
8286
8046
  try {
8287
8047
  if (child.directory) {
8288
8048
  const childDirectoryHandle = await parentHandle.getDirectoryHandle(child.name, { create: true });
@@ -8290,21 +8050,37 @@
8290
8050
  } else {
8291
8051
  const fileHandle = await parentHandle.getFileHandle(child.name, { create: true });
8292
8052
  const writable = await fileHandle.createWritable();
8293
- await child.getData({ writable }, Object.assign({}, options, {
8294
- onprogress: async progress => {
8295
- if (options.onprogress) {
8296
- writtenSizes.set(child.id, progress);
8297
- try {
8298
- await options.onprogress(Array.from(writtenSizes.values()).reduce((previousValue, currentValue) => previousValue + currentValue, 0), totalSize);
8299
- } catch {
8300
- // ignored
8053
+ try {
8054
+ await child.getData({ writable }, Object.assign({}, options, {
8055
+ signal,
8056
+ onprogress: async progress => {
8057
+ if (options.onprogress) {
8058
+ writtenSizes.set(child.id, progress);
8059
+ try {
8060
+ await options.onprogress(Array.from(writtenSizes.values()).reduce((previousValue, currentValue) => previousValue + currentValue, 0), totalSize);
8061
+ } catch {
8062
+ // ignored
8063
+ }
8301
8064
  }
8302
8065
  }
8303
- }
8304
- }));
8066
+ }));
8067
+ } catch (error) {
8068
+ throw exportAborted ? new Error(ERR_ABORT_EXPORT) : error;
8069
+ }
8070
+ exportedEntryNames.push(child.getRelativeName(zipEntry));
8305
8071
  }
8306
8072
  } catch (error) {
8307
- throw new Error(error.message + (child ? " (" + child.name + ")" : ""), { cause: error });
8073
+ exportAborted = true;
8074
+ abortController.abort(new Error(ERR_ABORT_EXPORT));
8075
+ try {
8076
+ if (error.entryName === UNDEFINED_VALUE) {
8077
+ error.entryName = child.getRelativeName(zipEntry);
8078
+ error.entryId = child.id;
8079
+ }
8080
+ } catch {
8081
+ // ignored
8082
+ }
8083
+ throw error;
8308
8084
  }
8309
8085
  }
8310
8086
  }
@@ -8366,6 +8142,21 @@
8366
8142
  fs.root = new ZipDirectoryEntry(fs);
8367
8143
  }
8368
8144
 
8145
+ function collectChildren(directory, recursive) {
8146
+ const children = [];
8147
+ const pendingDirectories = [directory];
8148
+ let directoryIndex = 0;
8149
+ while (directoryIndex < pendingDirectories.length) {
8150
+ for (const child of pendingDirectories[directoryIndex++].children) {
8151
+ children.push(child);
8152
+ if (recursive) {
8153
+ pendingDirectories.push(child);
8154
+ }
8155
+ }
8156
+ }
8157
+ return children;
8158
+ }
8159
+
8369
8160
  function registerEntries(fs, entry) {
8370
8161
  const pendingEntries = [entry];
8371
8162
  while (pendingEntries.length) {
@@ -8385,6 +8176,7 @@
8385
8176
  }
8386
8177
  }
8387
8178
 
8179
+ exports.ERR_ABORTED = ERR_ABORTED;
8388
8180
  exports.ERR_ENTRY_EXISTS = ERR_ENTRY_EXISTS;
8389
8181
  exports.ERR_READABLE_CONSUMED = ERR_READABLE_CONSUMED;
8390
8182
  exports.fs = fs;