grab-url 1.0.7 → 1.0.8

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.
@@ -0,0 +1,3715 @@
1
+ #!/usr/bin/env node
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { pipeline } from "stream/promises";
5
+ import { Readable } from "stream";
6
+ import require$$0 from "readline";
7
+ import grab from "./grab-api.es.js";
8
+ import { pathToFileURL } from "url";
9
+ import require$$4 from "events";
10
+ import { log } from "./log.es.js";
11
+ const spinners = {
12
+ dots: [
13
+ "⠋",
14
+ "⠙",
15
+ "⠹",
16
+ "⠸",
17
+ "⠼",
18
+ "⠴",
19
+ "⠦",
20
+ "⠧",
21
+ "⠇",
22
+ "⠏"
23
+ ],
24
+ dots2: [
25
+ "⣾",
26
+ "⣽",
27
+ "⣻",
28
+ "⢿",
29
+ "⡿",
30
+ "⣟",
31
+ "⣯",
32
+ "⣷"
33
+ ],
34
+ dots3: [
35
+ "⠋",
36
+ "⠙",
37
+ "⠚",
38
+ "⠞",
39
+ "⠖",
40
+ "⠦",
41
+ "⠴",
42
+ "⠲",
43
+ "⠳",
44
+ "⠓"
45
+ ],
46
+ dots4: [
47
+ "⠄",
48
+ "⠆",
49
+ "⠇",
50
+ "⠋",
51
+ "⠙",
52
+ "⠸",
53
+ "⠰",
54
+ "⠠",
55
+ "⠰",
56
+ "⠸",
57
+ "⠙",
58
+ "⠋",
59
+ "⠇",
60
+ "⠆"
61
+ ],
62
+ dots5: [
63
+ "⠋",
64
+ "⠙",
65
+ "⠚",
66
+ "⠒",
67
+ "⠂",
68
+ "⠂",
69
+ "⠒",
70
+ "⠲",
71
+ "⠴",
72
+ "⠦",
73
+ "⠖",
74
+ "⠒",
75
+ "⠐",
76
+ "⠐",
77
+ "⠒",
78
+ "⠓",
79
+ "⠋"
80
+ ],
81
+ dots6: [
82
+ "⠁",
83
+ "⠉",
84
+ "⠙",
85
+ "⠚",
86
+ "⠒",
87
+ "⠂",
88
+ "⠂",
89
+ "⠒",
90
+ "⠲",
91
+ "⠴",
92
+ "⠤",
93
+ "⠄",
94
+ "⠄",
95
+ "⠤",
96
+ "⠴",
97
+ "⠲",
98
+ "⠒",
99
+ "⠂",
100
+ "⠂",
101
+ "⠒",
102
+ "⠚",
103
+ "⠙",
104
+ "⠉",
105
+ "⠁"
106
+ ],
107
+ dots7: [
108
+ "⠈",
109
+ "⠉",
110
+ "⠋",
111
+ "⠓",
112
+ "⠒",
113
+ "⠐",
114
+ "⠐",
115
+ "⠒",
116
+ "⠖",
117
+ "⠦",
118
+ "⠤",
119
+ "⠠",
120
+ "⠠",
121
+ "⠤",
122
+ "⠦",
123
+ "⠖",
124
+ "⠒",
125
+ "⠐",
126
+ "⠐",
127
+ "⠒",
128
+ "⠓",
129
+ "⠋",
130
+ "⠉",
131
+ "⠈"
132
+ ],
133
+ dots8: [
134
+ "⠁",
135
+ "⠁",
136
+ "⠉",
137
+ "⠙",
138
+ "⠚",
139
+ "⠒",
140
+ "⠂",
141
+ "⠂",
142
+ "⠒",
143
+ "⠲",
144
+ "⠴",
145
+ "⠤",
146
+ "⠄",
147
+ "⠄",
148
+ "⠤",
149
+ "⠠",
150
+ "⠠",
151
+ "⠤",
152
+ "⠦",
153
+ "⠖",
154
+ "⠒",
155
+ "⠐",
156
+ "⠐",
157
+ "⠒",
158
+ "⠓",
159
+ "⠋",
160
+ "⠉",
161
+ "⠈",
162
+ "⠈"
163
+ ],
164
+ dots9: [
165
+ "⢹",
166
+ "⢺",
167
+ "⢼",
168
+ "⣸",
169
+ "⣇",
170
+ "⡧",
171
+ "⡗",
172
+ "⡏"
173
+ ],
174
+ dots10: [
175
+ "⢄",
176
+ "⢂",
177
+ "⢁",
178
+ "⡁",
179
+ "⡈",
180
+ "⡐",
181
+ "⡠"
182
+ ],
183
+ dots11: [
184
+ "⠁",
185
+ "⠂",
186
+ "⠄",
187
+ "⡀",
188
+ "⢀",
189
+ "⠠",
190
+ "⠐",
191
+ "⠈"
192
+ ],
193
+ dots12: [
194
+ "⢀⠀",
195
+ "⡀⠀",
196
+ "⠄⠀",
197
+ "⢂⠀",
198
+ "⡂⠀",
199
+ "⠅⠀",
200
+ "⢃⠀",
201
+ "⡃⠀",
202
+ "⠍⠀",
203
+ "⢋⠀",
204
+ "⡋⠀",
205
+ "⠍⠁",
206
+ "⢋⠁",
207
+ "⡋⠁",
208
+ "⠍⠉",
209
+ "⠋⠉",
210
+ "⠋⠉",
211
+ "⠉⠙",
212
+ "⠉⠙",
213
+ "⠉⠩",
214
+ "⠈⢙",
215
+ "⠈⡙",
216
+ "⢈⠩",
217
+ "⡀⢙",
218
+ "⠄⡙",
219
+ "⢂⠩",
220
+ "⡂⢘",
221
+ "⠅⡘",
222
+ "⢃⠨",
223
+ "⡃⢐",
224
+ "⠍⡐",
225
+ "⢋⠠",
226
+ "⡋⢀",
227
+ "⠍⡁",
228
+ "⢋⠁",
229
+ "⡋⠁",
230
+ "⠍⠉",
231
+ "⠋⠉",
232
+ "⠋⠉",
233
+ "⠉⠙",
234
+ "⠉⠙",
235
+ "⠉⠩",
236
+ "⠈⢙",
237
+ "⠈⡙",
238
+ "⠈⠩",
239
+ "⠀⢙",
240
+ "⠀⡙",
241
+ "⠀⠩",
242
+ "⠀⢘",
243
+ "⠀⡘",
244
+ "⠀⠨",
245
+ "⠀⢐",
246
+ "⠀⡐",
247
+ "⠀⠠",
248
+ "⠀⢀",
249
+ "⠀⡀"
250
+ ],
251
+ dots13: [
252
+ "⣼",
253
+ "⣹",
254
+ "⢻",
255
+ "⠿",
256
+ "⡟",
257
+ "⣏",
258
+ "⣧",
259
+ "⣶"
260
+ ],
261
+ dots14: [
262
+ "⠉⠉",
263
+ "⠈⠙",
264
+ "⠀⠹",
265
+ "⠀⢸",
266
+ "⠀⣰",
267
+ "⢀⣠",
268
+ "⣀⣀",
269
+ "⣄⡀",
270
+ "⣆⠀",
271
+ "⡇⠀",
272
+ "⠏⠀",
273
+ "⠋⠁"
274
+ ],
275
+ dotsCircle: [
276
+ "⢎ ",
277
+ "⠎⠁",
278
+ "⠊⠑",
279
+ "⠈⠱",
280
+ " ⡱",
281
+ "⢀⡰",
282
+ "⢄⡠",
283
+ "⢆⡀"
284
+ ],
285
+ sand: [
286
+ "⠁",
287
+ "⠂",
288
+ "⠄",
289
+ "⡀",
290
+ "⡈",
291
+ "⡐",
292
+ "⡠",
293
+ "⣀",
294
+ "⣁",
295
+ "⣂",
296
+ "⣄",
297
+ "⣌",
298
+ "⣔",
299
+ "⣤",
300
+ "⣥",
301
+ "⣦",
302
+ "⣮",
303
+ "⣶",
304
+ "⣷",
305
+ "⣿",
306
+ "⡿",
307
+ "⠿",
308
+ "⢟",
309
+ "⠟",
310
+ "⡛",
311
+ "⠛",
312
+ "⠫",
313
+ "⢋",
314
+ "⠋",
315
+ "⠍",
316
+ "⡉",
317
+ "⠉",
318
+ "⠑",
319
+ "⠡",
320
+ "⢁"
321
+ ],
322
+ line: [
323
+ "-",
324
+ "\\",
325
+ "|",
326
+ "/"
327
+ ],
328
+ line2: [
329
+ "⠂",
330
+ "-",
331
+ "–",
332
+ "—",
333
+ "–",
334
+ "-"
335
+ ],
336
+ pipe: [
337
+ "┤",
338
+ "┘",
339
+ "┴",
340
+ "└",
341
+ "├",
342
+ "┌",
343
+ "┬",
344
+ "┐"
345
+ ],
346
+ simpleDots: [
347
+ ". ",
348
+ ".. ",
349
+ "...",
350
+ " "
351
+ ],
352
+ simpleDotsScrolling: [
353
+ ". ",
354
+ ".. ",
355
+ "...",
356
+ " ..",
357
+ " .",
358
+ " "
359
+ ],
360
+ star: [
361
+ "✶",
362
+ "✸",
363
+ "✹",
364
+ "✺",
365
+ "✹",
366
+ "✷"
367
+ ],
368
+ star2: [
369
+ "+",
370
+ "x",
371
+ "*"
372
+ ],
373
+ flip: [
374
+ "_",
375
+ "_",
376
+ "_",
377
+ "-",
378
+ "`",
379
+ "`",
380
+ "'",
381
+ "´",
382
+ "-",
383
+ "_",
384
+ "_",
385
+ "_"
386
+ ],
387
+ hamburger: [
388
+ "☱",
389
+ "☲",
390
+ "☴"
391
+ ],
392
+ growVertical: [
393
+ "▁",
394
+ "▃",
395
+ "▄",
396
+ "▅",
397
+ "▆",
398
+ "▇",
399
+ "▆",
400
+ "▅",
401
+ "▄",
402
+ "▃"
403
+ ],
404
+ growHorizontal: [
405
+ "▏",
406
+ "▎",
407
+ "▍",
408
+ "▌",
409
+ "▋",
410
+ "▊",
411
+ "▉",
412
+ "▊",
413
+ "▋",
414
+ "▌",
415
+ "▍",
416
+ "▎"
417
+ ],
418
+ balloon: [
419
+ " ",
420
+ ".",
421
+ "o",
422
+ "O",
423
+ "@",
424
+ "*",
425
+ " "
426
+ ],
427
+ balloon2: [
428
+ ".",
429
+ "o",
430
+ "O",
431
+ "°",
432
+ "O",
433
+ "o",
434
+ "."
435
+ ],
436
+ noise: [
437
+ "▓",
438
+ "▒",
439
+ "░"
440
+ ],
441
+ bounce: [
442
+ "⠁",
443
+ "⠂",
444
+ "⠄",
445
+ "⠂"
446
+ ],
447
+ boxBounce: [
448
+ "▖",
449
+ "▘",
450
+ "▝",
451
+ "▗"
452
+ ],
453
+ boxBounce2: [
454
+ "▌",
455
+ "▀",
456
+ "▐",
457
+ "▄"
458
+ ],
459
+ triangle: [
460
+ "◢",
461
+ "◣",
462
+ "◤",
463
+ "◥"
464
+ ],
465
+ binary: [
466
+ "010010",
467
+ "001100",
468
+ "100101",
469
+ "111010",
470
+ "111101",
471
+ "010111",
472
+ "101011",
473
+ "111000",
474
+ "110011",
475
+ "110101"
476
+ ],
477
+ arc: [
478
+ "◜",
479
+ "◠",
480
+ "◝",
481
+ "◞",
482
+ "◡",
483
+ "◟"
484
+ ],
485
+ circle: [
486
+ "◡",
487
+ "⊙",
488
+ "◠"
489
+ ],
490
+ squareCorners: [
491
+ "◰",
492
+ "◳",
493
+ "◲",
494
+ "◱"
495
+ ],
496
+ circleQuarters: [
497
+ "◴",
498
+ "◷",
499
+ "◶",
500
+ "◵"
501
+ ],
502
+ circleHalves: [
503
+ "◐",
504
+ "◓",
505
+ "◑",
506
+ "◒"
507
+ ],
508
+ squish: [
509
+ "╫",
510
+ "╪"
511
+ ],
512
+ toggle: [
513
+ "⊶",
514
+ "⊷"
515
+ ],
516
+ toggle2: [
517
+ "▫",
518
+ "▪"
519
+ ],
520
+ toggle3: [
521
+ "□",
522
+ "■"
523
+ ],
524
+ toggle4: [
525
+ "■",
526
+ "□",
527
+ "▪",
528
+ "▫"
529
+ ],
530
+ toggle5: [
531
+ "▮",
532
+ "▯"
533
+ ],
534
+ toggle6: [
535
+ "ဝ",
536
+ "၀"
537
+ ],
538
+ toggle7: [
539
+ "⦾",
540
+ "⦿"
541
+ ],
542
+ toggle8: [
543
+ "◍",
544
+ "◌"
545
+ ],
546
+ toggle9: [
547
+ "◉",
548
+ "◎"
549
+ ],
550
+ toggle10: [
551
+ "㊂",
552
+ "㊀",
553
+ "㊁"
554
+ ],
555
+ toggle11: [
556
+ "⧇",
557
+ "⧆"
558
+ ],
559
+ toggle12: [
560
+ "☗",
561
+ "☖"
562
+ ],
563
+ toggle13: [
564
+ "=",
565
+ "*",
566
+ "-"
567
+ ],
568
+ arrow: [
569
+ "←",
570
+ "↖",
571
+ "↑",
572
+ "↗",
573
+ "→",
574
+ "↘",
575
+ "↓",
576
+ "↙"
577
+ ],
578
+ arrow2: [
579
+ "⬆️ ",
580
+ "↗️ ",
581
+ "➡️ ",
582
+ "↘️ ",
583
+ "⬇️ ",
584
+ "↙️ ",
585
+ "⬅️ ",
586
+ "↖️ "
587
+ ],
588
+ arrow3: [
589
+ "▹▹▹▹▹",
590
+ "▸▹▹▹▹",
591
+ "▹▸▹▹▹",
592
+ "▹▹▸▹▹",
593
+ "▹▹▹▸▹",
594
+ "▹▹▹▹▸"
595
+ ],
596
+ bouncingBar: [
597
+ "[ ]",
598
+ "[= ]",
599
+ "[== ]",
600
+ "[=== ]",
601
+ "[====]",
602
+ "[ ===]",
603
+ "[ ==]",
604
+ "[ =]",
605
+ "[ ]",
606
+ "[ =]",
607
+ "[ ==]",
608
+ "[ ===]",
609
+ "[====]",
610
+ "[=== ]",
611
+ "[== ]",
612
+ "[= ]"
613
+ ],
614
+ bouncingBall: [
615
+ "( ● )",
616
+ "( ● )",
617
+ "( ● )",
618
+ "( ● )",
619
+ "( ●)",
620
+ "( ● )",
621
+ "( ● )",
622
+ "( ● )",
623
+ "( ● )",
624
+ "(● )"
625
+ ],
626
+ smiley: [
627
+ "😄 ",
628
+ "😝 "
629
+ ],
630
+ monkey: [
631
+ "🙈 ",
632
+ "🙈 ",
633
+ "🙉 ",
634
+ "🙊 "
635
+ ],
636
+ hearts: [
637
+ "💛 ",
638
+ "💙 ",
639
+ "💜 ",
640
+ "💚 ",
641
+ "❤️ "
642
+ ],
643
+ clock: [
644
+ "🕛 ",
645
+ "🕐 ",
646
+ "🕑 ",
647
+ "🕒 ",
648
+ "🕓 ",
649
+ "🕔 ",
650
+ "🕕 ",
651
+ "🕖 ",
652
+ "🕗 ",
653
+ "🕘 ",
654
+ "🕙 ",
655
+ "🕚 "
656
+ ],
657
+ earth: [
658
+ "🌍 ",
659
+ "🌎 ",
660
+ "🌏 "
661
+ ],
662
+ moon: [
663
+ "🌑 ",
664
+ "🌒 ",
665
+ "🌓 ",
666
+ "🌔 ",
667
+ "🌕 ",
668
+ "🌖 ",
669
+ "🌗 ",
670
+ "🌘 "
671
+ ],
672
+ runner: [
673
+ "🚶 ",
674
+ "🏃 "
675
+ ],
676
+ dqpb: [
677
+ "d",
678
+ "q",
679
+ "p",
680
+ "b"
681
+ ],
682
+ weather: [
683
+ "☀️ ",
684
+ "☀️ ",
685
+ "☀️ ",
686
+ "🌤 ",
687
+ "⛅️ ",
688
+ "🌥 ",
689
+ "☁️ ",
690
+ "🌧 ",
691
+ "🌨 ",
692
+ "🌧 ",
693
+ "🌨 ",
694
+ "🌧 ",
695
+ "🌨 ",
696
+ "⛈ ",
697
+ "🌨 ",
698
+ "🌧 ",
699
+ "🌨 ",
700
+ "☁️ ",
701
+ "🌥 ",
702
+ "⛅️ ",
703
+ "🌤 ",
704
+ "☀️ ",
705
+ "☀️ "
706
+ ],
707
+ christmas: [
708
+ "🌲",
709
+ "🎄"
710
+ ],
711
+ grenade: [
712
+ "، ",
713
+ "′ ",
714
+ " ´ ",
715
+ " ‾ ",
716
+ " ⸌",
717
+ " ⸊",
718
+ " |",
719
+ " ⁎",
720
+ " ⁕",
721
+ " ෴ ",
722
+ " ⁓",
723
+ " ",
724
+ " ",
725
+ " "
726
+ ],
727
+ point: [
728
+ "∙∙∙",
729
+ "●∙∙",
730
+ "∙●∙",
731
+ "∙∙●",
732
+ "∙∙∙"
733
+ ],
734
+ layer: [
735
+ "-",
736
+ "=",
737
+ "≡"
738
+ ],
739
+ betaWave: [
740
+ "ρββββββ",
741
+ "βρβββββ",
742
+ "ββρββββ",
743
+ "βββρβββ",
744
+ "ββββρββ",
745
+ "βββββρβ",
746
+ "ββββββρ"
747
+ ],
748
+ fingerDance: [
749
+ "🤘 ",
750
+ "🤟 ",
751
+ "🖖 ",
752
+ "✋ ",
753
+ "🤚 ",
754
+ "👆 "
755
+ ],
756
+ mindblown: [
757
+ "😐 ",
758
+ "😐 ",
759
+ "😮 ",
760
+ "😮 ",
761
+ "😦 ",
762
+ "😦 ",
763
+ "😧 ",
764
+ "😧 ",
765
+ "🤯 ",
766
+ "💥 ",
767
+ "✨ ",
768
+ "  ",
769
+ "  ",
770
+ "  "
771
+ ],
772
+ speaker: [
773
+ "🔈 ",
774
+ "🔉 ",
775
+ "🔊 ",
776
+ "🔉 "
777
+ ],
778
+ orangePulse: [
779
+ "🔸 ",
780
+ "🔶 ",
781
+ "🟠 ",
782
+ "🟠 ",
783
+ "🔶 "
784
+ ],
785
+ bluePulse: [
786
+ "🔹 ",
787
+ "🔷 ",
788
+ "🔵 ",
789
+ "🔵 ",
790
+ "🔷 "
791
+ ],
792
+ orangeBluePulse: [
793
+ "🔸 ",
794
+ "🔶 ",
795
+ "🟠 ",
796
+ "🟠 ",
797
+ "🔶 ",
798
+ "🔹 ",
799
+ "🔷 ",
800
+ "🔵 ",
801
+ "🔵 ",
802
+ "🔷 "
803
+ ],
804
+ timeTravel: [
805
+ "🕛 ",
806
+ "🕚 ",
807
+ "🕙 ",
808
+ "🕘 ",
809
+ "🕗 ",
810
+ "🕖 ",
811
+ "🕕 ",
812
+ "🕔 ",
813
+ "🕓 ",
814
+ "🕒 ",
815
+ "🕑 ",
816
+ "🕐 "
817
+ ],
818
+ aesthetic: [
819
+ "▰▱▱▱▱▱▱",
820
+ "▰▰▱▱▱▱▱",
821
+ "▰▰▰▱▱▱▱",
822
+ "▰▰▰▰▱▱▱",
823
+ "▰▰▰▰▰▱▱",
824
+ "▰▰▰▰▰▰▱",
825
+ "▰▰▰▰▰▰▰",
826
+ "▰▱▱▱▱▱▱"
827
+ ]
828
+ };
829
+ function getDefaultExportFromCjs(x) {
830
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
831
+ }
832
+ var eta;
833
+ var hasRequiredEta;
834
+ function requireEta() {
835
+ if (hasRequiredEta) return eta;
836
+ hasRequiredEta = 1;
837
+ class ETA {
838
+ constructor(length, initTime, initValue) {
839
+ this.etaBufferLength = length || 100;
840
+ this.valueBuffer = [initValue];
841
+ this.timeBuffer = [initTime];
842
+ this.eta = "0";
843
+ }
844
+ // add new values to calculation buffer
845
+ update(time, value, total) {
846
+ this.valueBuffer.push(value);
847
+ this.timeBuffer.push(time);
848
+ this.calculate(total - value);
849
+ }
850
+ // fetch estimated time
851
+ getTime() {
852
+ return this.eta;
853
+ }
854
+ // eta calculation - request number of remaining events
855
+ calculate(remaining) {
856
+ const currentBufferSize = this.valueBuffer.length;
857
+ const buffer = Math.min(this.etaBufferLength, currentBufferSize);
858
+ const v_diff = this.valueBuffer[currentBufferSize - 1] - this.valueBuffer[currentBufferSize - buffer];
859
+ const t_diff = this.timeBuffer[currentBufferSize - 1] - this.timeBuffer[currentBufferSize - buffer];
860
+ const vt_rate = v_diff / t_diff;
861
+ this.valueBuffer = this.valueBuffer.slice(-this.etaBufferLength);
862
+ this.timeBuffer = this.timeBuffer.slice(-this.etaBufferLength);
863
+ const eta2 = Math.ceil(remaining / vt_rate / 1e3);
864
+ if (isNaN(eta2)) {
865
+ this.eta = "NULL";
866
+ } else if (!isFinite(eta2)) {
867
+ this.eta = "INF";
868
+ } else if (eta2 > 1e7) {
869
+ this.eta = "INF";
870
+ } else if (eta2 < 0) {
871
+ this.eta = 0;
872
+ } else {
873
+ this.eta = eta2;
874
+ }
875
+ }
876
+ }
877
+ eta = ETA;
878
+ return eta;
879
+ }
880
+ var terminal;
881
+ var hasRequiredTerminal;
882
+ function requireTerminal() {
883
+ if (hasRequiredTerminal) return terminal;
884
+ hasRequiredTerminal = 1;
885
+ const _readline = require$$0;
886
+ class Terminal {
887
+ constructor(outputStream) {
888
+ this.stream = outputStream;
889
+ this.linewrap = true;
890
+ this.dy = 0;
891
+ }
892
+ // save cursor position + settings
893
+ cursorSave() {
894
+ if (!this.stream.isTTY) {
895
+ return;
896
+ }
897
+ this.stream.write("\x1B7");
898
+ }
899
+ // restore last cursor position + settings
900
+ cursorRestore() {
901
+ if (!this.stream.isTTY) {
902
+ return;
903
+ }
904
+ this.stream.write("\x1B8");
905
+ }
906
+ // show/hide cursor
907
+ cursor(enabled) {
908
+ if (!this.stream.isTTY) {
909
+ return;
910
+ }
911
+ if (enabled) {
912
+ this.stream.write("\x1B[?25h");
913
+ } else {
914
+ this.stream.write("\x1B[?25l");
915
+ }
916
+ }
917
+ // change cursor positionn
918
+ cursorTo(x = null, y = null) {
919
+ if (!this.stream.isTTY) {
920
+ return;
921
+ }
922
+ _readline.cursorTo(this.stream, x, y);
923
+ }
924
+ // change relative cursor position
925
+ cursorRelative(dx = null, dy = null) {
926
+ if (!this.stream.isTTY) {
927
+ return;
928
+ }
929
+ this.dy = this.dy + dy;
930
+ _readline.moveCursor(this.stream, dx, dy);
931
+ }
932
+ // relative reset
933
+ cursorRelativeReset() {
934
+ if (!this.stream.isTTY) {
935
+ return;
936
+ }
937
+ _readline.moveCursor(this.stream, 0, -this.dy);
938
+ _readline.cursorTo(this.stream, 0, null);
939
+ this.dy = 0;
940
+ }
941
+ // clear to the right from cursor
942
+ clearRight() {
943
+ if (!this.stream.isTTY) {
944
+ return;
945
+ }
946
+ _readline.clearLine(this.stream, 1);
947
+ }
948
+ // clear the full line
949
+ clearLine() {
950
+ if (!this.stream.isTTY) {
951
+ return;
952
+ }
953
+ _readline.clearLine(this.stream, 0);
954
+ }
955
+ // clear everyting beyond the current line
956
+ clearBottom() {
957
+ if (!this.stream.isTTY) {
958
+ return;
959
+ }
960
+ _readline.clearScreenDown(this.stream);
961
+ }
962
+ // add new line; increment counter
963
+ newline() {
964
+ this.stream.write("\n");
965
+ this.dy++;
966
+ }
967
+ // write content to output stream
968
+ // @TODO use string-width to strip length
969
+ write(s, rawWrite = false) {
970
+ if (this.linewrap === true && rawWrite === false) {
971
+ this.stream.write(s.substr(0, this.getWidth()));
972
+ } else {
973
+ this.stream.write(s);
974
+ }
975
+ }
976
+ // control line wrapping
977
+ lineWrapping(enabled) {
978
+ if (!this.stream.isTTY) {
979
+ return;
980
+ }
981
+ this.linewrap = enabled;
982
+ if (enabled) {
983
+ this.stream.write("\x1B[?7h");
984
+ } else {
985
+ this.stream.write("\x1B[?7l");
986
+ }
987
+ }
988
+ // tty environment ?
989
+ isTTY() {
990
+ return this.stream.isTTY === true;
991
+ }
992
+ // get terminal width
993
+ getWidth() {
994
+ return this.stream.columns || (this.stream.isTTY ? 80 : 200);
995
+ }
996
+ }
997
+ terminal = Terminal;
998
+ return terminal;
999
+ }
1000
+ var stringWidth = { exports: {} };
1001
+ var ansiRegex;
1002
+ var hasRequiredAnsiRegex;
1003
+ function requireAnsiRegex() {
1004
+ if (hasRequiredAnsiRegex) return ansiRegex;
1005
+ hasRequiredAnsiRegex = 1;
1006
+ ansiRegex = ({ onlyFirst = false } = {}) => {
1007
+ const pattern = [
1008
+ "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
1009
+ "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"
1010
+ ].join("|");
1011
+ return new RegExp(pattern, onlyFirst ? void 0 : "g");
1012
+ };
1013
+ return ansiRegex;
1014
+ }
1015
+ var stripAnsi;
1016
+ var hasRequiredStripAnsi;
1017
+ function requireStripAnsi() {
1018
+ if (hasRequiredStripAnsi) return stripAnsi;
1019
+ hasRequiredStripAnsi = 1;
1020
+ const ansiRegex2 = requireAnsiRegex();
1021
+ stripAnsi = (string) => typeof string === "string" ? string.replace(ansiRegex2(), "") : string;
1022
+ return stripAnsi;
1023
+ }
1024
+ var isFullwidthCodePoint = { exports: {} };
1025
+ var hasRequiredIsFullwidthCodePoint;
1026
+ function requireIsFullwidthCodePoint() {
1027
+ if (hasRequiredIsFullwidthCodePoint) return isFullwidthCodePoint.exports;
1028
+ hasRequiredIsFullwidthCodePoint = 1;
1029
+ const isFullwidthCodePoint$1 = (codePoint) => {
1030
+ if (Number.isNaN(codePoint)) {
1031
+ return false;
1032
+ }
1033
+ if (codePoint >= 4352 && (codePoint <= 4447 || // Hangul Jamo
1034
+ codePoint === 9001 || // LEFT-POINTING ANGLE BRACKET
1035
+ codePoint === 9002 || // RIGHT-POINTING ANGLE BRACKET
1036
+ // CJK Radicals Supplement .. Enclosed CJK Letters and Months
1037
+ 11904 <= codePoint && codePoint <= 12871 && codePoint !== 12351 || // Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
1038
+ 12880 <= codePoint && codePoint <= 19903 || // CJK Unified Ideographs .. Yi Radicals
1039
+ 19968 <= codePoint && codePoint <= 42182 || // Hangul Jamo Extended-A
1040
+ 43360 <= codePoint && codePoint <= 43388 || // Hangul Syllables
1041
+ 44032 <= codePoint && codePoint <= 55203 || // CJK Compatibility Ideographs
1042
+ 63744 <= codePoint && codePoint <= 64255 || // Vertical Forms
1043
+ 65040 <= codePoint && codePoint <= 65049 || // CJK Compatibility Forms .. Small Form Variants
1044
+ 65072 <= codePoint && codePoint <= 65131 || // Halfwidth and Fullwidth Forms
1045
+ 65281 <= codePoint && codePoint <= 65376 || 65504 <= codePoint && codePoint <= 65510 || // Kana Supplement
1046
+ 110592 <= codePoint && codePoint <= 110593 || // Enclosed Ideographic Supplement
1047
+ 127488 <= codePoint && codePoint <= 127569 || // CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
1048
+ 131072 <= codePoint && codePoint <= 262141)) {
1049
+ return true;
1050
+ }
1051
+ return false;
1052
+ };
1053
+ isFullwidthCodePoint.exports = isFullwidthCodePoint$1;
1054
+ isFullwidthCodePoint.exports.default = isFullwidthCodePoint$1;
1055
+ return isFullwidthCodePoint.exports;
1056
+ }
1057
+ var emojiRegex;
1058
+ var hasRequiredEmojiRegex;
1059
+ function requireEmojiRegex() {
1060
+ if (hasRequiredEmojiRegex) return emojiRegex;
1061
+ hasRequiredEmojiRegex = 1;
1062
+ emojiRegex = function() {
1063
+ return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB-\uDFFD])|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)\uFE0F|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83C\uDFF4\u200D\u2620)\uFE0F|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF4\uD83C\uDDF2|\uD83C\uDDF6\uD83C\uDDE6|[#\*0-9]\uFE0F\u20E3|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC70\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD36\uDDB5\uDDB6\uDDBB\uDDD2-\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5\uDEEB\uDEEC\uDEF4-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g;
1064
+ };
1065
+ return emojiRegex;
1066
+ }
1067
+ var hasRequiredStringWidth;
1068
+ function requireStringWidth() {
1069
+ if (hasRequiredStringWidth) return stringWidth.exports;
1070
+ hasRequiredStringWidth = 1;
1071
+ const stripAnsi2 = requireStripAnsi();
1072
+ const isFullwidthCodePoint2 = requireIsFullwidthCodePoint();
1073
+ const emojiRegex2 = requireEmojiRegex();
1074
+ const stringWidth$1 = (string) => {
1075
+ if (typeof string !== "string" || string.length === 0) {
1076
+ return 0;
1077
+ }
1078
+ string = stripAnsi2(string);
1079
+ if (string.length === 0) {
1080
+ return 0;
1081
+ }
1082
+ string = string.replace(emojiRegex2(), " ");
1083
+ let width = 0;
1084
+ for (let i = 0; i < string.length; i++) {
1085
+ const code = string.codePointAt(i);
1086
+ if (code <= 31 || code >= 127 && code <= 159) {
1087
+ continue;
1088
+ }
1089
+ if (code >= 768 && code <= 879) {
1090
+ continue;
1091
+ }
1092
+ if (code > 65535) {
1093
+ i++;
1094
+ }
1095
+ width += isFullwidthCodePoint2(code) ? 2 : 1;
1096
+ }
1097
+ return width;
1098
+ };
1099
+ stringWidth.exports = stringWidth$1;
1100
+ stringWidth.exports.default = stringWidth$1;
1101
+ return stringWidth.exports;
1102
+ }
1103
+ var formatValue;
1104
+ var hasRequiredFormatValue;
1105
+ function requireFormatValue() {
1106
+ if (hasRequiredFormatValue) return formatValue;
1107
+ hasRequiredFormatValue = 1;
1108
+ formatValue = function formatValue2(v, options2, type) {
1109
+ if (options2.autopadding !== true) {
1110
+ return v;
1111
+ }
1112
+ function autopadding(value, length) {
1113
+ return (options2.autopaddingChar + value).slice(-3);
1114
+ }
1115
+ switch (type) {
1116
+ case "percentage":
1117
+ return autopadding(v);
1118
+ default:
1119
+ return v;
1120
+ }
1121
+ };
1122
+ return formatValue;
1123
+ }
1124
+ var formatBar;
1125
+ var hasRequiredFormatBar;
1126
+ function requireFormatBar() {
1127
+ if (hasRequiredFormatBar) return formatBar;
1128
+ hasRequiredFormatBar = 1;
1129
+ formatBar = function formatBar2(progress, options2) {
1130
+ const completeSize = Math.round(progress * options2.barsize);
1131
+ const incompleteSize = options2.barsize - completeSize;
1132
+ return options2.barCompleteString.substr(0, completeSize) + options2.barGlue + options2.barIncompleteString.substr(0, incompleteSize);
1133
+ };
1134
+ return formatBar;
1135
+ }
1136
+ var formatTime;
1137
+ var hasRequiredFormatTime;
1138
+ function requireFormatTime() {
1139
+ if (hasRequiredFormatTime) return formatTime;
1140
+ hasRequiredFormatTime = 1;
1141
+ formatTime = function formatTime2(t, options2, roundToMultipleOf) {
1142
+ function round(input) {
1143
+ if (roundToMultipleOf) {
1144
+ return roundToMultipleOf * Math.round(input / roundToMultipleOf);
1145
+ } else {
1146
+ return input;
1147
+ }
1148
+ }
1149
+ function autopadding(v) {
1150
+ return (options2.autopaddingChar + v).slice(-2);
1151
+ }
1152
+ if (t > 3600) {
1153
+ return autopadding(Math.floor(t / 3600)) + "h" + autopadding(round(t % 3600 / 60)) + "m";
1154
+ } else if (t > 60) {
1155
+ return autopadding(Math.floor(t / 60)) + "m" + autopadding(round(t % 60)) + "s";
1156
+ } else if (t > 10) {
1157
+ return autopadding(round(t)) + "s";
1158
+ } else {
1159
+ return autopadding(t) + "s";
1160
+ }
1161
+ };
1162
+ return formatTime;
1163
+ }
1164
+ var formatter;
1165
+ var hasRequiredFormatter;
1166
+ function requireFormatter() {
1167
+ if (hasRequiredFormatter) return formatter;
1168
+ hasRequiredFormatter = 1;
1169
+ const _stringWidth = requireStringWidth();
1170
+ const _defaultFormatValue = requireFormatValue();
1171
+ const _defaultFormatBar = requireFormatBar();
1172
+ const _defaultFormatTime = requireFormatTime();
1173
+ formatter = function defaultFormatter(options2, params, payload) {
1174
+ let s = options2.format;
1175
+ const formatTime2 = options2.formatTime || _defaultFormatTime;
1176
+ const formatValue2 = options2.formatValue || _defaultFormatValue;
1177
+ const formatBar2 = options2.formatBar || _defaultFormatBar;
1178
+ const percentage = Math.floor(params.progress * 100) + "";
1179
+ const stopTime = params.stopTime || Date.now();
1180
+ const elapsedTime = Math.round((stopTime - params.startTime) / 1e3);
1181
+ const context = Object.assign({}, payload, {
1182
+ bar: formatBar2(params.progress, options2),
1183
+ percentage: formatValue2(percentage, options2, "percentage"),
1184
+ total: formatValue2(params.total, options2, "total"),
1185
+ value: formatValue2(params.value, options2, "value"),
1186
+ eta: formatValue2(params.eta, options2, "eta"),
1187
+ eta_formatted: formatTime2(params.eta, options2, 5),
1188
+ duration: formatValue2(elapsedTime, options2, "duration"),
1189
+ duration_formatted: formatTime2(elapsedTime, options2, 1)
1190
+ });
1191
+ s = s.replace(/\{(\w+)\}/g, function(match, key) {
1192
+ if (typeof context[key] !== "undefined") {
1193
+ return context[key];
1194
+ }
1195
+ return match;
1196
+ });
1197
+ const fullMargin = Math.max(0, params.maxWidth - _stringWidth(s) - 2);
1198
+ const halfMargin = Math.floor(fullMargin / 2);
1199
+ switch (options2.align) {
1200
+ // fill start-of-line with whitespaces
1201
+ case "right":
1202
+ s = fullMargin > 0 ? " ".repeat(fullMargin) + s : s;
1203
+ break;
1204
+ // distribute whitespaces to left+right
1205
+ case "center":
1206
+ s = halfMargin > 0 ? " ".repeat(halfMargin) + s : s;
1207
+ break;
1208
+ }
1209
+ return s;
1210
+ };
1211
+ return formatter;
1212
+ }
1213
+ var options;
1214
+ var hasRequiredOptions;
1215
+ function requireOptions() {
1216
+ if (hasRequiredOptions) return options;
1217
+ hasRequiredOptions = 1;
1218
+ function mergeOption(v, defaultValue) {
1219
+ if (typeof v === "undefined" || v === null) {
1220
+ return defaultValue;
1221
+ } else {
1222
+ return v;
1223
+ }
1224
+ }
1225
+ options = {
1226
+ // set global options
1227
+ parse: function parse(rawOptions, preset) {
1228
+ const options2 = {};
1229
+ const opt = Object.assign({}, preset, rawOptions);
1230
+ options2.throttleTime = 1e3 / mergeOption(opt.fps, 10);
1231
+ options2.stream = mergeOption(opt.stream, process.stderr);
1232
+ options2.terminal = mergeOption(opt.terminal, null);
1233
+ options2.clearOnComplete = mergeOption(opt.clearOnComplete, false);
1234
+ options2.stopOnComplete = mergeOption(opt.stopOnComplete, false);
1235
+ options2.barsize = mergeOption(opt.barsize, 40);
1236
+ options2.align = mergeOption(opt.align, "left");
1237
+ options2.hideCursor = mergeOption(opt.hideCursor, false);
1238
+ options2.linewrap = mergeOption(opt.linewrap, false);
1239
+ options2.barGlue = mergeOption(opt.barGlue, "");
1240
+ options2.barCompleteChar = mergeOption(opt.barCompleteChar, "=");
1241
+ options2.barIncompleteChar = mergeOption(opt.barIncompleteChar, "-");
1242
+ options2.format = mergeOption(opt.format, "progress [{bar}] {percentage}% | ETA: {eta}s | {value}/{total}");
1243
+ options2.formatTime = mergeOption(opt.formatTime, null);
1244
+ options2.formatValue = mergeOption(opt.formatValue, null);
1245
+ options2.formatBar = mergeOption(opt.formatBar, null);
1246
+ options2.etaBufferLength = mergeOption(opt.etaBuffer, 10);
1247
+ options2.etaAsynchronousUpdate = mergeOption(opt.etaAsynchronousUpdate, false);
1248
+ options2.progressCalculationRelative = mergeOption(opt.progressCalculationRelative, false);
1249
+ options2.synchronousUpdate = mergeOption(opt.synchronousUpdate, true);
1250
+ options2.noTTYOutput = mergeOption(opt.noTTYOutput, false);
1251
+ options2.notTTYSchedule = mergeOption(opt.notTTYSchedule, 2e3);
1252
+ options2.emptyOnZero = mergeOption(opt.emptyOnZero, false);
1253
+ options2.forceRedraw = mergeOption(opt.forceRedraw, false);
1254
+ options2.autopadding = mergeOption(opt.autopadding, false);
1255
+ options2.gracefulExit = mergeOption(opt.gracefulExit, false);
1256
+ return options2;
1257
+ },
1258
+ // derived options: instance specific, has to be created for every bar element
1259
+ assignDerivedOptions: function assignDerivedOptions(options2) {
1260
+ options2.barCompleteString = options2.barCompleteChar.repeat(options2.barsize + 1);
1261
+ options2.barIncompleteString = options2.barIncompleteChar.repeat(options2.barsize + 1);
1262
+ options2.autopaddingChar = options2.autopadding ? mergeOption(options2.autopaddingChar, " ") : "";
1263
+ return options2;
1264
+ }
1265
+ };
1266
+ return options;
1267
+ }
1268
+ var genericBar;
1269
+ var hasRequiredGenericBar;
1270
+ function requireGenericBar() {
1271
+ if (hasRequiredGenericBar) return genericBar;
1272
+ hasRequiredGenericBar = 1;
1273
+ const _ETA = requireEta();
1274
+ const _Terminal = requireTerminal();
1275
+ const _formatter = requireFormatter();
1276
+ const _options = requireOptions();
1277
+ const _EventEmitter = require$$4;
1278
+ genericBar = class GenericBar extends _EventEmitter {
1279
+ constructor(options2) {
1280
+ super();
1281
+ this.options = _options.assignDerivedOptions(options2);
1282
+ this.terminal = this.options.terminal ? this.options.terminal : new _Terminal(this.options.stream);
1283
+ this.value = 0;
1284
+ this.startValue = 0;
1285
+ this.total = 100;
1286
+ this.lastDrawnString = null;
1287
+ this.startTime = null;
1288
+ this.stopTime = null;
1289
+ this.lastRedraw = Date.now();
1290
+ this.eta = new _ETA(this.options.etaBufferLength, 0, 0);
1291
+ this.payload = {};
1292
+ this.isActive = false;
1293
+ this.formatter = typeof this.options.format === "function" ? this.options.format : _formatter;
1294
+ }
1295
+ // internal render function
1296
+ render(forceRendering = false) {
1297
+ const params = {
1298
+ progress: this.getProgress(),
1299
+ eta: this.eta.getTime(),
1300
+ startTime: this.startTime,
1301
+ stopTime: this.stopTime,
1302
+ total: this.total,
1303
+ value: this.value,
1304
+ maxWidth: this.terminal.getWidth()
1305
+ };
1306
+ if (this.options.etaAsynchronousUpdate) {
1307
+ this.updateETA();
1308
+ }
1309
+ const s = this.formatter(this.options, params, this.payload);
1310
+ const forceRedraw = forceRendering || this.options.forceRedraw || this.options.noTTYOutput && !this.terminal.isTTY();
1311
+ if (forceRedraw || this.lastDrawnString != s) {
1312
+ this.emit("redraw-pre");
1313
+ this.terminal.cursorTo(0, null);
1314
+ this.terminal.write(s);
1315
+ this.terminal.clearRight();
1316
+ this.lastDrawnString = s;
1317
+ this.lastRedraw = Date.now();
1318
+ this.emit("redraw-post");
1319
+ }
1320
+ }
1321
+ // start the progress bar
1322
+ start(total, startValue, payload) {
1323
+ this.value = startValue || 0;
1324
+ this.total = typeof total !== "undefined" && total >= 0 ? total : 100;
1325
+ this.startValue = startValue || 0;
1326
+ this.payload = payload || {};
1327
+ this.startTime = Date.now();
1328
+ this.stopTime = null;
1329
+ this.lastDrawnString = "";
1330
+ this.eta = new _ETA(this.options.etaBufferLength, this.startTime, this.value);
1331
+ this.isActive = true;
1332
+ this.emit("start", total, startValue);
1333
+ }
1334
+ // stop the bar
1335
+ stop() {
1336
+ this.isActive = false;
1337
+ this.stopTime = Date.now();
1338
+ this.emit("stop", this.total, this.value);
1339
+ }
1340
+ // update the bar value
1341
+ // update(value, payload)
1342
+ // update(payload)
1343
+ update(arg0, arg1 = {}) {
1344
+ if (typeof arg0 === "number") {
1345
+ this.value = arg0;
1346
+ this.eta.update(Date.now(), arg0, this.total);
1347
+ }
1348
+ const payloadData = (typeof arg0 === "object" ? arg0 : arg1) || {};
1349
+ this.emit("update", this.total, this.value);
1350
+ for (const key in payloadData) {
1351
+ this.payload[key] = payloadData[key];
1352
+ }
1353
+ if (this.value >= this.getTotal() && this.options.stopOnComplete) {
1354
+ this.stop();
1355
+ }
1356
+ }
1357
+ // calculate the actual progress value
1358
+ getProgress() {
1359
+ let progress = this.value / this.total;
1360
+ if (this.options.progressCalculationRelative) {
1361
+ progress = (this.value - this.startValue) / (this.total - this.startValue);
1362
+ }
1363
+ if (isNaN(progress)) {
1364
+ progress = this.options && this.options.emptyOnZero ? 0 : 1;
1365
+ }
1366
+ progress = Math.min(Math.max(progress, 0), 1);
1367
+ return progress;
1368
+ }
1369
+ // update the bar value
1370
+ // increment(delta, payload)
1371
+ // increment(payload)
1372
+ increment(arg0 = 1, arg1 = {}) {
1373
+ if (typeof arg0 === "object") {
1374
+ this.update(this.value + 1, arg0);
1375
+ } else {
1376
+ this.update(this.value + arg0, arg1);
1377
+ }
1378
+ }
1379
+ // get the total (limit) value
1380
+ getTotal() {
1381
+ return this.total;
1382
+ }
1383
+ // set the total (limit) value
1384
+ setTotal(total) {
1385
+ if (typeof total !== "undefined" && total >= 0) {
1386
+ this.total = total;
1387
+ }
1388
+ }
1389
+ // force eta calculation update (long running processes)
1390
+ updateETA() {
1391
+ this.eta.update(Date.now(), this.value, this.total);
1392
+ }
1393
+ };
1394
+ return genericBar;
1395
+ }
1396
+ var singleBar;
1397
+ var hasRequiredSingleBar;
1398
+ function requireSingleBar() {
1399
+ if (hasRequiredSingleBar) return singleBar;
1400
+ hasRequiredSingleBar = 1;
1401
+ const _GenericBar = requireGenericBar();
1402
+ const _options = requireOptions();
1403
+ singleBar = class SingleBar extends _GenericBar {
1404
+ constructor(options2, preset) {
1405
+ super(_options.parse(options2, preset));
1406
+ this.timer = null;
1407
+ if (this.options.noTTYOutput && this.terminal.isTTY() === false) {
1408
+ this.options.synchronousUpdate = false;
1409
+ }
1410
+ this.schedulingRate = this.terminal.isTTY() ? this.options.throttleTime : this.options.notTTYSchedule;
1411
+ this.sigintCallback = null;
1412
+ }
1413
+ // internal render function
1414
+ render() {
1415
+ if (this.timer) {
1416
+ clearTimeout(this.timer);
1417
+ this.timer = null;
1418
+ }
1419
+ super.render();
1420
+ if (this.options.noTTYOutput && this.terminal.isTTY() === false) {
1421
+ this.terminal.newline();
1422
+ }
1423
+ this.timer = setTimeout(this.render.bind(this), this.schedulingRate);
1424
+ }
1425
+ update(current, payload) {
1426
+ if (!this.timer) {
1427
+ return;
1428
+ }
1429
+ super.update(current, payload);
1430
+ if (this.options.synchronousUpdate && this.lastRedraw + this.options.throttleTime * 2 < Date.now()) {
1431
+ this.render();
1432
+ }
1433
+ }
1434
+ // start the progress bar
1435
+ start(total, startValue, payload) {
1436
+ if (this.options.noTTYOutput === false && this.terminal.isTTY() === false) {
1437
+ return;
1438
+ }
1439
+ if (this.sigintCallback === null && this.options.gracefulExit) {
1440
+ this.sigintCallback = this.stop.bind(this);
1441
+ process.once("SIGINT", this.sigintCallback);
1442
+ process.once("SIGTERM", this.sigintCallback);
1443
+ }
1444
+ this.terminal.cursorSave();
1445
+ if (this.options.hideCursor === true) {
1446
+ this.terminal.cursor(false);
1447
+ }
1448
+ if (this.options.linewrap === false) {
1449
+ this.terminal.lineWrapping(false);
1450
+ }
1451
+ super.start(total, startValue, payload);
1452
+ this.render();
1453
+ }
1454
+ // stop the bar
1455
+ stop() {
1456
+ if (!this.timer) {
1457
+ return;
1458
+ }
1459
+ if (this.sigintCallback) {
1460
+ process.removeListener("SIGINT", this.sigintCallback);
1461
+ process.removeListener("SIGTERM", this.sigintCallback);
1462
+ this.sigintCallback = null;
1463
+ }
1464
+ this.render();
1465
+ super.stop();
1466
+ clearTimeout(this.timer);
1467
+ this.timer = null;
1468
+ if (this.options.hideCursor === true) {
1469
+ this.terminal.cursor(true);
1470
+ }
1471
+ if (this.options.linewrap === false) {
1472
+ this.terminal.lineWrapping(true);
1473
+ }
1474
+ this.terminal.cursorRestore();
1475
+ if (this.options.clearOnComplete) {
1476
+ this.terminal.cursorTo(0, null);
1477
+ this.terminal.clearLine();
1478
+ } else {
1479
+ this.terminal.newline();
1480
+ }
1481
+ }
1482
+ };
1483
+ return singleBar;
1484
+ }
1485
+ var multiBar;
1486
+ var hasRequiredMultiBar;
1487
+ function requireMultiBar() {
1488
+ if (hasRequiredMultiBar) return multiBar;
1489
+ hasRequiredMultiBar = 1;
1490
+ const _Terminal = requireTerminal();
1491
+ const _BarElement = requireGenericBar();
1492
+ const _options = requireOptions();
1493
+ const _EventEmitter = require$$4;
1494
+ multiBar = class MultiBar extends _EventEmitter {
1495
+ constructor(options2, preset) {
1496
+ super();
1497
+ this.bars = [];
1498
+ this.options = _options.parse(options2, preset);
1499
+ this.options.synchronousUpdate = false;
1500
+ this.terminal = this.options.terminal ? this.options.terminal : new _Terminal(this.options.stream);
1501
+ this.timer = null;
1502
+ this.isActive = false;
1503
+ this.schedulingRate = this.terminal.isTTY() ? this.options.throttleTime : this.options.notTTYSchedule;
1504
+ this.loggingBuffer = [];
1505
+ this.sigintCallback = null;
1506
+ }
1507
+ // add a new bar to the stack
1508
+ create(total, startValue, payload, barOptions = {}) {
1509
+ const bar = new _BarElement(Object.assign(
1510
+ {},
1511
+ // global options
1512
+ this.options,
1513
+ // terminal instance
1514
+ {
1515
+ terminal: this.terminal
1516
+ },
1517
+ // overrides
1518
+ barOptions
1519
+ ));
1520
+ this.bars.push(bar);
1521
+ if (this.options.noTTYOutput === false && this.terminal.isTTY() === false) {
1522
+ return bar;
1523
+ }
1524
+ if (this.sigintCallback === null && this.options.gracefulExit) {
1525
+ this.sigintCallback = this.stop.bind(this);
1526
+ process.once("SIGINT", this.sigintCallback);
1527
+ process.once("SIGTERM", this.sigintCallback);
1528
+ }
1529
+ if (!this.isActive) {
1530
+ if (this.options.hideCursor === true) {
1531
+ this.terminal.cursor(false);
1532
+ }
1533
+ if (this.options.linewrap === false) {
1534
+ this.terminal.lineWrapping(false);
1535
+ }
1536
+ this.timer = setTimeout(this.update.bind(this), this.schedulingRate);
1537
+ }
1538
+ this.isActive = true;
1539
+ bar.start(total, startValue, payload);
1540
+ this.emit("start");
1541
+ return bar;
1542
+ }
1543
+ // remove a bar from the stack
1544
+ remove(bar) {
1545
+ const index = this.bars.indexOf(bar);
1546
+ if (index < 0) {
1547
+ return false;
1548
+ }
1549
+ this.bars.splice(index, 1);
1550
+ this.update();
1551
+ this.terminal.newline();
1552
+ this.terminal.clearBottom();
1553
+ return true;
1554
+ }
1555
+ // internal update routine
1556
+ update() {
1557
+ if (this.timer) {
1558
+ clearTimeout(this.timer);
1559
+ this.timer = null;
1560
+ }
1561
+ this.emit("update-pre");
1562
+ this.terminal.cursorRelativeReset();
1563
+ this.emit("redraw-pre");
1564
+ if (this.loggingBuffer.length > 0) {
1565
+ this.terminal.clearLine();
1566
+ while (this.loggingBuffer.length > 0) {
1567
+ this.terminal.write(this.loggingBuffer.shift(), true);
1568
+ }
1569
+ }
1570
+ for (let i = 0; i < this.bars.length; i++) {
1571
+ if (i > 0) {
1572
+ this.terminal.newline();
1573
+ }
1574
+ this.bars[i].render();
1575
+ }
1576
+ this.emit("redraw-post");
1577
+ if (this.options.noTTYOutput && this.terminal.isTTY() === false) {
1578
+ this.terminal.newline();
1579
+ this.terminal.newline();
1580
+ }
1581
+ this.timer = setTimeout(this.update.bind(this), this.schedulingRate);
1582
+ this.emit("update-post");
1583
+ if (this.options.stopOnComplete && !this.bars.find((bar) => bar.isActive)) {
1584
+ this.stop();
1585
+ }
1586
+ }
1587
+ stop() {
1588
+ clearTimeout(this.timer);
1589
+ this.timer = null;
1590
+ if (this.sigintCallback) {
1591
+ process.removeListener("SIGINT", this.sigintCallback);
1592
+ process.removeListener("SIGTERM", this.sigintCallback);
1593
+ this.sigintCallback = null;
1594
+ }
1595
+ this.isActive = false;
1596
+ if (this.options.hideCursor === true) {
1597
+ this.terminal.cursor(true);
1598
+ }
1599
+ if (this.options.linewrap === false) {
1600
+ this.terminal.lineWrapping(true);
1601
+ }
1602
+ this.terminal.cursorRelativeReset();
1603
+ this.emit("stop-pre-clear");
1604
+ if (this.options.clearOnComplete) {
1605
+ this.terminal.clearBottom();
1606
+ } else {
1607
+ for (let i = 0; i < this.bars.length; i++) {
1608
+ if (i > 0) {
1609
+ this.terminal.newline();
1610
+ }
1611
+ this.bars[i].render();
1612
+ this.bars[i].stop();
1613
+ }
1614
+ this.terminal.newline();
1615
+ }
1616
+ this.emit("stop");
1617
+ }
1618
+ log(s) {
1619
+ this.loggingBuffer.push(s);
1620
+ }
1621
+ };
1622
+ return multiBar;
1623
+ }
1624
+ var legacy;
1625
+ var hasRequiredLegacy;
1626
+ function requireLegacy() {
1627
+ if (hasRequiredLegacy) return legacy;
1628
+ hasRequiredLegacy = 1;
1629
+ legacy = {
1630
+ format: "progress [{bar}] {percentage}% | ETA: {eta}s | {value}/{total}",
1631
+ barCompleteChar: "=",
1632
+ barIncompleteChar: "-"
1633
+ };
1634
+ return legacy;
1635
+ }
1636
+ var shadesClassic;
1637
+ var hasRequiredShadesClassic;
1638
+ function requireShadesClassic() {
1639
+ if (hasRequiredShadesClassic) return shadesClassic;
1640
+ hasRequiredShadesClassic = 1;
1641
+ shadesClassic = {
1642
+ format: " {bar} {percentage}% | ETA: {eta}s | {value}/{total}",
1643
+ barCompleteChar: "█",
1644
+ barIncompleteChar: "░"
1645
+ };
1646
+ return shadesClassic;
1647
+ }
1648
+ var shadesGrey;
1649
+ var hasRequiredShadesGrey;
1650
+ function requireShadesGrey() {
1651
+ if (hasRequiredShadesGrey) return shadesGrey;
1652
+ hasRequiredShadesGrey = 1;
1653
+ shadesGrey = {
1654
+ format: " \x1B[90m{bar}\x1B[0m {percentage}% | ETA: {eta}s | {value}/{total}",
1655
+ barCompleteChar: "█",
1656
+ barIncompleteChar: "░"
1657
+ };
1658
+ return shadesGrey;
1659
+ }
1660
+ var rect;
1661
+ var hasRequiredRect;
1662
+ function requireRect() {
1663
+ if (hasRequiredRect) return rect;
1664
+ hasRequiredRect = 1;
1665
+ rect = {
1666
+ format: " {bar}■ {percentage}% | ETA: {eta}s | {value}/{total}",
1667
+ barCompleteChar: "■",
1668
+ barIncompleteChar: " "
1669
+ };
1670
+ return rect;
1671
+ }
1672
+ var presets;
1673
+ var hasRequiredPresets;
1674
+ function requirePresets() {
1675
+ if (hasRequiredPresets) return presets;
1676
+ hasRequiredPresets = 1;
1677
+ const _legacy = requireLegacy();
1678
+ const _shades_classic = requireShadesClassic();
1679
+ const _shades_grey = requireShadesGrey();
1680
+ const _rect = requireRect();
1681
+ presets = {
1682
+ legacy: _legacy,
1683
+ shades_classic: _shades_classic,
1684
+ shades_grey: _shades_grey,
1685
+ rect: _rect
1686
+ };
1687
+ return presets;
1688
+ }
1689
+ var cliProgress$1;
1690
+ var hasRequiredCliProgress;
1691
+ function requireCliProgress() {
1692
+ if (hasRequiredCliProgress) return cliProgress$1;
1693
+ hasRequiredCliProgress = 1;
1694
+ const _SingleBar = requireSingleBar();
1695
+ const _MultiBar = requireMultiBar();
1696
+ const _Presets = requirePresets();
1697
+ const _Formatter = requireFormatter();
1698
+ const _defaultFormatValue = requireFormatValue();
1699
+ const _defaultFormatBar = requireFormatBar();
1700
+ const _defaultFormatTime = requireFormatTime();
1701
+ cliProgress$1 = {
1702
+ Bar: _SingleBar,
1703
+ SingleBar: _SingleBar,
1704
+ MultiBar: _MultiBar,
1705
+ Presets: _Presets,
1706
+ Format: {
1707
+ Formatter: _Formatter,
1708
+ BarFormat: _defaultFormatBar,
1709
+ ValueFormat: _defaultFormatValue,
1710
+ TimeFormat: _defaultFormatTime
1711
+ }
1712
+ };
1713
+ return cliProgress$1;
1714
+ }
1715
+ var cliProgressExports = requireCliProgress();
1716
+ const cliProgress = /* @__PURE__ */ getDefaultExportFromCjs(cliProgressExports);
1717
+ const ANSI_BACKGROUND_OFFSET = 10;
1718
+ const wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
1719
+ const wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
1720
+ const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
1721
+ const styles$1 = {
1722
+ modifier: {
1723
+ reset: [0, 0],
1724
+ // 21 isn't widely supported and 22 does the same thing
1725
+ bold: [1, 22],
1726
+ dim: [2, 22],
1727
+ italic: [3, 23],
1728
+ underline: [4, 24],
1729
+ overline: [53, 55],
1730
+ inverse: [7, 27],
1731
+ hidden: [8, 28],
1732
+ strikethrough: [9, 29]
1733
+ },
1734
+ color: {
1735
+ black: [30, 39],
1736
+ red: [31, 39],
1737
+ green: [32, 39],
1738
+ yellow: [33, 39],
1739
+ blue: [34, 39],
1740
+ magenta: [35, 39],
1741
+ cyan: [36, 39],
1742
+ white: [37, 39],
1743
+ // Bright color
1744
+ blackBright: [90, 39],
1745
+ gray: [90, 39],
1746
+ // Alias of `blackBright`
1747
+ grey: [90, 39],
1748
+ // Alias of `blackBright`
1749
+ redBright: [91, 39],
1750
+ greenBright: [92, 39],
1751
+ yellowBright: [93, 39],
1752
+ blueBright: [94, 39],
1753
+ magentaBright: [95, 39],
1754
+ cyanBright: [96, 39],
1755
+ whiteBright: [97, 39]
1756
+ },
1757
+ bgColor: {
1758
+ bgBlack: [40, 49],
1759
+ bgRed: [41, 49],
1760
+ bgGreen: [42, 49],
1761
+ bgYellow: [43, 49],
1762
+ bgBlue: [44, 49],
1763
+ bgMagenta: [45, 49],
1764
+ bgCyan: [46, 49],
1765
+ bgWhite: [47, 49],
1766
+ // Bright color
1767
+ bgBlackBright: [100, 49],
1768
+ bgGray: [100, 49],
1769
+ // Alias of `bgBlackBright`
1770
+ bgGrey: [100, 49],
1771
+ // Alias of `bgBlackBright`
1772
+ bgRedBright: [101, 49],
1773
+ bgGreenBright: [102, 49],
1774
+ bgYellowBright: [103, 49],
1775
+ bgBlueBright: [104, 49],
1776
+ bgMagentaBright: [105, 49],
1777
+ bgCyanBright: [106, 49],
1778
+ bgWhiteBright: [107, 49]
1779
+ }
1780
+ };
1781
+ Object.keys(styles$1.modifier);
1782
+ const foregroundColorNames = Object.keys(styles$1.color);
1783
+ const backgroundColorNames = Object.keys(styles$1.bgColor);
1784
+ [...foregroundColorNames, ...backgroundColorNames];
1785
+ function assembleStyles() {
1786
+ const codes = /* @__PURE__ */ new Map();
1787
+ for (const [groupName, group] of Object.entries(styles$1)) {
1788
+ for (const [styleName, style] of Object.entries(group)) {
1789
+ styles$1[styleName] = {
1790
+ open: `\x1B[${style[0]}m`,
1791
+ close: `\x1B[${style[1]}m`
1792
+ };
1793
+ group[styleName] = styles$1[styleName];
1794
+ codes.set(style[0], style[1]);
1795
+ }
1796
+ Object.defineProperty(styles$1, groupName, {
1797
+ value: group,
1798
+ enumerable: false
1799
+ });
1800
+ }
1801
+ Object.defineProperty(styles$1, "codes", {
1802
+ value: codes,
1803
+ enumerable: false
1804
+ });
1805
+ styles$1.color.close = "\x1B[39m";
1806
+ styles$1.bgColor.close = "\x1B[49m";
1807
+ styles$1.color.ansi = wrapAnsi16();
1808
+ styles$1.color.ansi256 = wrapAnsi256();
1809
+ styles$1.color.ansi16m = wrapAnsi16m();
1810
+ styles$1.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
1811
+ styles$1.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
1812
+ styles$1.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
1813
+ Object.defineProperties(styles$1, {
1814
+ rgbToAnsi256: {
1815
+ value(red, green, blue) {
1816
+ if (red === green && green === blue) {
1817
+ if (red < 8) {
1818
+ return 16;
1819
+ }
1820
+ if (red > 248) {
1821
+ return 231;
1822
+ }
1823
+ return Math.round((red - 8) / 247 * 24) + 232;
1824
+ }
1825
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
1826
+ },
1827
+ enumerable: false
1828
+ },
1829
+ hexToRgb: {
1830
+ value(hex) {
1831
+ const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
1832
+ if (!matches) {
1833
+ return [0, 0, 0];
1834
+ }
1835
+ let [colorString] = matches;
1836
+ if (colorString.length === 3) {
1837
+ colorString = [...colorString].map((character) => character + character).join("");
1838
+ }
1839
+ const integer = Number.parseInt(colorString, 16);
1840
+ return [
1841
+ /* eslint-disable no-bitwise */
1842
+ integer >> 16 & 255,
1843
+ integer >> 8 & 255,
1844
+ integer & 255
1845
+ /* eslint-enable no-bitwise */
1846
+ ];
1847
+ },
1848
+ enumerable: false
1849
+ },
1850
+ hexToAnsi256: {
1851
+ value: (hex) => styles$1.rgbToAnsi256(...styles$1.hexToRgb(hex)),
1852
+ enumerable: false
1853
+ },
1854
+ ansi256ToAnsi: {
1855
+ value(code) {
1856
+ if (code < 8) {
1857
+ return 30 + code;
1858
+ }
1859
+ if (code < 16) {
1860
+ return 90 + (code - 8);
1861
+ }
1862
+ let red;
1863
+ let green;
1864
+ let blue;
1865
+ if (code >= 232) {
1866
+ red = ((code - 232) * 10 + 8) / 255;
1867
+ green = red;
1868
+ blue = red;
1869
+ } else {
1870
+ code -= 16;
1871
+ const remainder = code % 36;
1872
+ red = Math.floor(code / 36) / 5;
1873
+ green = Math.floor(remainder / 6) / 5;
1874
+ blue = remainder % 6 / 5;
1875
+ }
1876
+ const value = Math.max(red, green, blue) * 2;
1877
+ if (value === 0) {
1878
+ return 30;
1879
+ }
1880
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
1881
+ if (value === 2) {
1882
+ result += 60;
1883
+ }
1884
+ return result;
1885
+ },
1886
+ enumerable: false
1887
+ },
1888
+ rgbToAnsi: {
1889
+ value: (red, green, blue) => styles$1.ansi256ToAnsi(styles$1.rgbToAnsi256(red, green, blue)),
1890
+ enumerable: false
1891
+ },
1892
+ hexToAnsi: {
1893
+ value: (hex) => styles$1.ansi256ToAnsi(styles$1.hexToAnsi256(hex)),
1894
+ enumerable: false
1895
+ }
1896
+ });
1897
+ return styles$1;
1898
+ }
1899
+ const ansiStyles = assembleStyles();
1900
+ const level = (() => {
1901
+ if (!("navigator" in globalThis)) {
1902
+ return 0;
1903
+ }
1904
+ if (globalThis.navigator.userAgentData) {
1905
+ const brand = navigator.userAgentData.brands.find(({ brand: brand2 }) => brand2 === "Chromium");
1906
+ if (brand && brand.version > 93) {
1907
+ return 3;
1908
+ }
1909
+ }
1910
+ if (/\b(Chrome|Chromium)\//.test(globalThis.navigator.userAgent)) {
1911
+ return 1;
1912
+ }
1913
+ return 0;
1914
+ })();
1915
+ const colorSupport = level !== 0 && {
1916
+ level
1917
+ };
1918
+ const supportsColor = {
1919
+ stdout: colorSupport,
1920
+ stderr: colorSupport
1921
+ };
1922
+ function stringReplaceAll(string, substring, replacer) {
1923
+ let index = string.indexOf(substring);
1924
+ if (index === -1) {
1925
+ return string;
1926
+ }
1927
+ const substringLength = substring.length;
1928
+ let endIndex = 0;
1929
+ let returnValue = "";
1930
+ do {
1931
+ returnValue += string.slice(endIndex, index) + substring + replacer;
1932
+ endIndex = index + substringLength;
1933
+ index = string.indexOf(substring, endIndex);
1934
+ } while (index !== -1);
1935
+ returnValue += string.slice(endIndex);
1936
+ return returnValue;
1937
+ }
1938
+ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
1939
+ let endIndex = 0;
1940
+ let returnValue = "";
1941
+ do {
1942
+ const gotCR = string[index - 1] === "\r";
1943
+ returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
1944
+ endIndex = index + 1;
1945
+ index = string.indexOf("\n", endIndex);
1946
+ } while (index !== -1);
1947
+ returnValue += string.slice(endIndex);
1948
+ return returnValue;
1949
+ }
1950
+ const { stdout: stdoutColor, stderr: stderrColor } = supportsColor;
1951
+ const GENERATOR = Symbol("GENERATOR");
1952
+ const STYLER = Symbol("STYLER");
1953
+ const IS_EMPTY = Symbol("IS_EMPTY");
1954
+ const levelMapping = [
1955
+ "ansi",
1956
+ "ansi",
1957
+ "ansi256",
1958
+ "ansi16m"
1959
+ ];
1960
+ const styles = /* @__PURE__ */ Object.create(null);
1961
+ const applyOptions = (object, options2 = {}) => {
1962
+ if (options2.level && !(Number.isInteger(options2.level) && options2.level >= 0 && options2.level <= 3)) {
1963
+ throw new Error("The `level` option should be an integer from 0 to 3");
1964
+ }
1965
+ const colorLevel = stdoutColor ? stdoutColor.level : 0;
1966
+ object.level = options2.level === void 0 ? colorLevel : options2.level;
1967
+ };
1968
+ const chalkFactory = (options2) => {
1969
+ const chalk2 = (...strings) => strings.join(" ");
1970
+ applyOptions(chalk2, options2);
1971
+ Object.setPrototypeOf(chalk2, createChalk.prototype);
1972
+ return chalk2;
1973
+ };
1974
+ function createChalk(options2) {
1975
+ return chalkFactory(options2);
1976
+ }
1977
+ Object.setPrototypeOf(createChalk.prototype, Function.prototype);
1978
+ for (const [styleName, style] of Object.entries(ansiStyles)) {
1979
+ styles[styleName] = {
1980
+ get() {
1981
+ const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
1982
+ Object.defineProperty(this, styleName, { value: builder });
1983
+ return builder;
1984
+ }
1985
+ };
1986
+ }
1987
+ styles.visible = {
1988
+ get() {
1989
+ const builder = createBuilder(this, this[STYLER], true);
1990
+ Object.defineProperty(this, "visible", { value: builder });
1991
+ return builder;
1992
+ }
1993
+ };
1994
+ const getModelAnsi = (model, level2, type, ...arguments_) => {
1995
+ if (model === "rgb") {
1996
+ if (level2 === "ansi16m") {
1997
+ return ansiStyles[type].ansi16m(...arguments_);
1998
+ }
1999
+ if (level2 === "ansi256") {
2000
+ return ansiStyles[type].ansi256(ansiStyles.rgbToAnsi256(...arguments_));
2001
+ }
2002
+ return ansiStyles[type].ansi(ansiStyles.rgbToAnsi(...arguments_));
2003
+ }
2004
+ if (model === "hex") {
2005
+ return getModelAnsi("rgb", level2, type, ...ansiStyles.hexToRgb(...arguments_));
2006
+ }
2007
+ return ansiStyles[type][model](...arguments_);
2008
+ };
2009
+ const usedModels = ["rgb", "hex", "ansi256"];
2010
+ for (const model of usedModels) {
2011
+ styles[model] = {
2012
+ get() {
2013
+ const { level: level2 } = this;
2014
+ return function(...arguments_) {
2015
+ const styler = createStyler(getModelAnsi(model, levelMapping[level2], "color", ...arguments_), ansiStyles.color.close, this[STYLER]);
2016
+ return createBuilder(this, styler, this[IS_EMPTY]);
2017
+ };
2018
+ }
2019
+ };
2020
+ const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
2021
+ styles[bgModel] = {
2022
+ get() {
2023
+ const { level: level2 } = this;
2024
+ return function(...arguments_) {
2025
+ const styler = createStyler(getModelAnsi(model, levelMapping[level2], "bgColor", ...arguments_), ansiStyles.bgColor.close, this[STYLER]);
2026
+ return createBuilder(this, styler, this[IS_EMPTY]);
2027
+ };
2028
+ }
2029
+ };
2030
+ }
2031
+ const proto = Object.defineProperties(() => {
2032
+ }, {
2033
+ ...styles,
2034
+ level: {
2035
+ enumerable: true,
2036
+ get() {
2037
+ return this[GENERATOR].level;
2038
+ },
2039
+ set(level2) {
2040
+ this[GENERATOR].level = level2;
2041
+ }
2042
+ }
2043
+ });
2044
+ const createStyler = (open, close, parent) => {
2045
+ let openAll;
2046
+ let closeAll;
2047
+ if (parent === void 0) {
2048
+ openAll = open;
2049
+ closeAll = close;
2050
+ } else {
2051
+ openAll = parent.openAll + open;
2052
+ closeAll = close + parent.closeAll;
2053
+ }
2054
+ return {
2055
+ open,
2056
+ close,
2057
+ openAll,
2058
+ closeAll,
2059
+ parent
2060
+ };
2061
+ };
2062
+ const createBuilder = (self, _styler, _isEmpty) => {
2063
+ const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
2064
+ Object.setPrototypeOf(builder, proto);
2065
+ builder[GENERATOR] = self;
2066
+ builder[STYLER] = _styler;
2067
+ builder[IS_EMPTY] = _isEmpty;
2068
+ return builder;
2069
+ };
2070
+ const applyStyle = (self, string) => {
2071
+ if (self.level <= 0 || !string) {
2072
+ return self[IS_EMPTY] ? "" : string;
2073
+ }
2074
+ let styler = self[STYLER];
2075
+ if (styler === void 0) {
2076
+ return string;
2077
+ }
2078
+ const { openAll, closeAll } = styler;
2079
+ if (string.includes("\x1B")) {
2080
+ while (styler !== void 0) {
2081
+ string = stringReplaceAll(string, styler.close, styler.open);
2082
+ styler = styler.parent;
2083
+ }
2084
+ }
2085
+ const lfIndex = string.indexOf("\n");
2086
+ if (lfIndex !== -1) {
2087
+ string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
2088
+ }
2089
+ return openAll + string + closeAll;
2090
+ };
2091
+ Object.defineProperties(createChalk.prototype, styles);
2092
+ const chalk = createChalk();
2093
+ createChalk({ level: stderrColor ? stderrColor.level : 0 });
2094
+ dirname(import.meta.url);
2095
+ class ArgParser {
2096
+ constructor() {
2097
+ this.commands = {};
2098
+ this.options = {};
2099
+ this.examples = [];
2100
+ this.helpText = "";
2101
+ this.versionText = "1.0.0";
2102
+ }
2103
+ usage(text) {
2104
+ this.helpText = text;
2105
+ return this;
2106
+ }
2107
+ command(pattern, desc, handler) {
2108
+ const match = pattern.match(/\$0 <(\w+)>/);
2109
+ if (match) this.commands[match[1]] = { desc, handler, required: true };
2110
+ return this;
2111
+ }
2112
+ option(name, opts = {}) {
2113
+ this.options[name] = opts;
2114
+ return this;
2115
+ }
2116
+ example(cmd, desc) {
2117
+ this.examples.push({ cmd, desc });
2118
+ return this;
2119
+ }
2120
+ help() {
2121
+ return this;
2122
+ }
2123
+ alias(short, long) {
2124
+ if (this.options[long]) this.options[long].alias = short;
2125
+ return this;
2126
+ }
2127
+ version(v) {
2128
+ if (v) this.versionText = v;
2129
+ return this;
2130
+ }
2131
+ strict() {
2132
+ return this;
2133
+ }
2134
+ parseSync() {
2135
+ const args = process.argv.slice(2);
2136
+ const result = {};
2137
+ const positional = [];
2138
+ if (args.includes("--help") || args.includes("-h")) {
2139
+ this.showHelp();
2140
+ process.exit(0);
2141
+ }
2142
+ if (args.includes("--version")) {
2143
+ console.log(this.versionText);
2144
+ process.exit(0);
2145
+ }
2146
+ for (let i = 0; i < args.length; i++) {
2147
+ const arg = args[i];
2148
+ if (arg.startsWith("--")) {
2149
+ const [key, value] = arg.split("=");
2150
+ const optName = key.slice(2);
2151
+ if (value !== void 0) {
2152
+ result[optName] = this.coerceValue(optName, value);
2153
+ } else if (this.options[optName]?.type === "boolean") {
2154
+ result[optName] = true;
2155
+ } else {
2156
+ const nextArg = args[i + 1];
2157
+ if (nextArg && !nextArg.startsWith("-")) {
2158
+ result[optName] = this.coerceValue(optName, nextArg);
2159
+ i++;
2160
+ } else {
2161
+ result[optName] = true;
2162
+ }
2163
+ }
2164
+ } else if (arg.startsWith("-") && arg.length === 2) {
2165
+ const shortFlag = arg[1];
2166
+ const longName = this.findLongName(shortFlag);
2167
+ if (longName) {
2168
+ if (this.options[longName]?.type === "boolean") {
2169
+ result[longName] = true;
2170
+ } else {
2171
+ const nextArg = args[i + 1];
2172
+ if (nextArg && !nextArg.startsWith("-")) {
2173
+ result[longName] = this.coerceValue(longName, nextArg);
2174
+ i++;
2175
+ }
2176
+ }
2177
+ }
2178
+ } else {
2179
+ positional.push(arg);
2180
+ }
2181
+ }
2182
+ if (positional.length > 0) result.urls = positional;
2183
+ Object.keys(this.options).forEach((key) => {
2184
+ if (result[key] === void 0 && this.options[key].default !== void 0) {
2185
+ result[key] = this.options[key].default;
2186
+ }
2187
+ });
2188
+ if ((!result.urls || result.urls.length === 0) && this.commands.url?.required) {
2189
+ console.error("Error: Missing required argument: url");
2190
+ this.showHelp();
2191
+ process.exit(1);
2192
+ }
2193
+ return result;
2194
+ }
2195
+ coerceValue(optName, value) {
2196
+ const opt = this.options[optName];
2197
+ if (!opt) return value;
2198
+ if (opt.coerce) return opt.coerce(value);
2199
+ switch (opt.type) {
2200
+ case "number":
2201
+ return Number(value);
2202
+ case "boolean":
2203
+ return value === "true" || value === "1";
2204
+ default:
2205
+ return value;
2206
+ }
2207
+ }
2208
+ findLongName(shortFlag) {
2209
+ return Object.keys(this.options).find((key) => this.options[key].alias === shortFlag);
2210
+ }
2211
+ showHelp() {
2212
+ console.log(this.helpText || "Usage: grab <url> [options]");
2213
+ console.log("\nPositional arguments:");
2214
+ Object.keys(this.commands).forEach((cmd) => {
2215
+ console.log(` ${cmd.padEnd(20)} ${this.commands[cmd].desc}`);
2216
+ });
2217
+ console.log("\nOptions:");
2218
+ Object.keys(this.options).forEach((key) => {
2219
+ const opt = this.options[key];
2220
+ const flags = opt.alias ? `-${opt.alias}, --${key}` : `--${key}`;
2221
+ console.log(` ${flags.padEnd(20)} ${opt.describe || ""}`);
2222
+ });
2223
+ if (this.examples.length > 0) {
2224
+ console.log("\nExamples:");
2225
+ this.examples.forEach((ex) => {
2226
+ console.log(` ${ex.cmd}`);
2227
+ console.log(` ${ex.desc}`);
2228
+ });
2229
+ }
2230
+ }
2231
+ }
2232
+ function isFileUrl(url) {
2233
+ return /\.[a-zA-Z0-9]{1,5}(?:\.[a-zA-Z0-9]{1,5})*$/.test(url.split("?")[0]);
2234
+ }
2235
+ class ColorFileDownloader {
2236
+ constructor() {
2237
+ this.progressBar = null;
2238
+ this.multiBar = null;
2239
+ this.loadingSpinner = null;
2240
+ this.abortController = null;
2241
+ this.COL_FILENAME = 25;
2242
+ this.COL_SPINNER = 2;
2243
+ this.COL_BAR = 15;
2244
+ this.COL_PERCENT = 4;
2245
+ this.COL_DOWNLOADED = 16;
2246
+ this.COL_TOTAL = 10;
2247
+ this.COL_SPEED = 10;
2248
+ this.COL_ETA = 10;
2249
+ this.colors = {
2250
+ primary: chalk.cyan,
2251
+ success: chalk.green,
2252
+ warning: chalk.yellow,
2253
+ error: chalk.red,
2254
+ info: chalk.blue,
2255
+ purple: chalk.magenta,
2256
+ pink: chalk.magentaBright,
2257
+ yellow: chalk.yellowBright,
2258
+ cyan: chalk.cyanBright,
2259
+ green: chalk.green,
2260
+ gradient: [
2261
+ chalk.blue,
2262
+ chalk.magenta,
2263
+ chalk.cyan,
2264
+ chalk.green,
2265
+ chalk.yellow,
2266
+ chalk.red
2267
+ ]
2268
+ };
2269
+ this.barColors = [
2270
+ "\x1B[32m",
2271
+ // green
2272
+ "\x1B[33m",
2273
+ // yellow
2274
+ "\x1B[34m",
2275
+ // blue
2276
+ "\x1B[35m",
2277
+ // magenta
2278
+ "\x1B[36m",
2279
+ // cyan
2280
+ "\x1B[91m",
2281
+ // bright red
2282
+ "\x1B[92m",
2283
+ // bright green
2284
+ "\x1B[93m",
2285
+ // bright yellow
2286
+ "\x1B[94m",
2287
+ // bright blue
2288
+ "\x1B[95m",
2289
+ // bright magenta
2290
+ "\x1B[96m"
2291
+ // bright cyan
2292
+ ];
2293
+ this.barGlueColors = [
2294
+ "\x1B[31m",
2295
+ // red
2296
+ "\x1B[33m",
2297
+ // yellow
2298
+ "\x1B[35m",
2299
+ // magenta
2300
+ "\x1B[37m",
2301
+ // white
2302
+ "\x1B[90m",
2303
+ // gray
2304
+ "\x1B[93m",
2305
+ // bright yellow
2306
+ "\x1B[97m"
2307
+ // bright white
2308
+ ];
2309
+ this.spinnerTypes = Object.keys(spinners.default || spinners);
2310
+ this.stateDir = this.getStateDirectory();
2311
+ this.ensureStateDirectoryExists();
2312
+ this.isPaused = false;
2313
+ this.pauseCallback = null;
2314
+ this.resumeCallback = null;
2315
+ this.abortControllers = [];
2316
+ this.keyboardListener = null;
2317
+ this.isAddingUrl = false;
2318
+ }
2319
+ /**
2320
+ * Get state directory from environment variable or use default
2321
+ * @returns {string} State directory path
2322
+ */
2323
+ getStateDirectory() {
2324
+ return process.env.GRAB_DOWNLOAD_STATE_DIR || path.join(process.cwd(), ".grab-downloads");
2325
+ }
2326
+ /**
2327
+ * Ensure state directory exists
2328
+ */
2329
+ ensureStateDirectoryExists() {
2330
+ try {
2331
+ if (!fs.existsSync(this.stateDir)) {
2332
+ fs.mkdirSync(this.stateDir, { recursive: true });
2333
+ }
2334
+ } catch (error) {
2335
+ console.log(this.colors.warning("⚠️ Could not create state directory, using current directory"));
2336
+ this.stateDir = process.cwd();
2337
+ }
2338
+ }
2339
+ /**
2340
+ * Get state file path for a given output path
2341
+ * @param {string} outputPath - The output file path
2342
+ * @returns {string} State file path
2343
+ */
2344
+ getStateFilePath(outputPath) {
2345
+ const stateFileName = path.basename(outputPath) + ".download-state";
2346
+ return path.join(this.stateDir, stateFileName);
2347
+ }
2348
+ /**
2349
+ * Clean up state file
2350
+ * @param {string} stateFilePath - Path to state file
2351
+ */
2352
+ cleanupStateFile(stateFilePath) {
2353
+ try {
2354
+ if (fs.existsSync(stateFilePath)) {
2355
+ fs.unlinkSync(stateFilePath);
2356
+ }
2357
+ } catch (error) {
2358
+ console.log(this.colors.warning("⚠️ Could not clean up state file"));
2359
+ }
2360
+ }
2361
+ /**
2362
+ * Print aligned header row for progress bars
2363
+ */
2364
+ printHeaderRow() {
2365
+ console.log(
2366
+ this.colors.success("📈 %".padEnd(this.COL_PERCENT)) + this.colors.yellow("📁 Files".padEnd(this.COL_FILENAME)) + this.colors.cyan("🔄".padEnd(this.COL_SPINNER)) + " " + this.colors.green("📊 Progress".padEnd(this.COL_BAR + 1)) + this.colors.info("📥 Downloaded".padEnd(this.COL_DOWNLOADED)) + this.colors.info("📦 Total".padEnd(this.COL_TOTAL)) + this.colors.purple("⚡ Speed".padEnd(this.COL_SPEED)) + this.colors.pink("⏱️ ETA".padEnd(this.COL_ETA))
2367
+ );
2368
+ }
2369
+ /**
2370
+ * Get random ora spinner type (for ora spinners)
2371
+ * @returns {string} Random ora spinner name
2372
+ */
2373
+ getRandomOraSpinner() {
2374
+ return this.spinnerTypes[Math.floor(Math.random() * this.spinnerTypes.length)];
2375
+ }
2376
+ /**
2377
+ * Get random bar color
2378
+ * @returns {string} ANSI color code
2379
+ */
2380
+ getRandomBarColor() {
2381
+ return this.barColors[Math.floor(Math.random() * this.barColors.length)];
2382
+ }
2383
+ /**
2384
+ * Get random bar glue color
2385
+ * @returns {string} ANSI color code
2386
+ */
2387
+ getRandomBarGlueColor() {
2388
+ return this.barGlueColors[Math.floor(Math.random() * this.barGlueColors.length)];
2389
+ }
2390
+ /**
2391
+ * Get random spinner type
2392
+ */
2393
+ getRandomSpinner() {
2394
+ return this.spinnerTypes[Math.floor(Math.random() * this.spinnerTypes.length)];
2395
+ }
2396
+ /**
2397
+ * Get spinner frames for a given spinner type
2398
+ * @param {string} spinnerType - The spinner type name
2399
+ * @returns {array} Array of spinner frame characters
2400
+ */
2401
+ getSpinnerFrames(spinnerType) {
2402
+ const spinnerData = spinners.default || spinners;
2403
+ const spinner = spinnerData[spinnerType];
2404
+ if (spinner && spinner.frames) {
2405
+ return spinner.frames;
2406
+ }
2407
+ return spinnerData.dots?.frames || ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
2408
+ }
2409
+ /**
2410
+ * Get the visual width of a spinner frame (accounting for multi-char emojis)
2411
+ * @param {string} frame - The spinner frame
2412
+ * @returns {number} Visual width
2413
+ */
2414
+ getSpinnerWidth(frame) {
2415
+ let width = 0;
2416
+ for (const char of frame) {
2417
+ const code = char.codePointAt(0);
2418
+ if (code >= 126976 && code <= 128767 || // Miscellaneous Symbols and Pictographs
2419
+ code >= 127744 && code <= 128511 || // Miscellaneous Symbols
2420
+ code >= 128512 && code <= 128591 || // Emoticons
2421
+ code >= 128640 && code <= 128767 || // Transport and Map
2422
+ code >= 128768 && code <= 128895 || // Alchemical Symbols
2423
+ code >= 128896 && code <= 129023 || // Geometric Shapes Extended
2424
+ code >= 129024 && code <= 129279 || // Supplemental Arrows-C
2425
+ code >= 9728 && code <= 9983 || // Miscellaneous Symbols
2426
+ code >= 9984 && code <= 10175) {
2427
+ width += 2;
2428
+ } else {
2429
+ width += 1;
2430
+ }
2431
+ }
2432
+ return width;
2433
+ }
2434
+ /**
2435
+ * Calculate dynamic bar size based on spinner width and terminal width
2436
+ * @param {string} spinnerFrame - Current spinner frame
2437
+ * @param {number} baseBarSize - Base bar size
2438
+ * @returns {number} Adjusted bar size
2439
+ */
2440
+ calculateBarSize(spinnerFrame, baseBarSize = 20) {
2441
+ const terminalWidth = process.stdout.columns || 120;
2442
+ const spinnerWidth = this.getSpinnerWidth(spinnerFrame);
2443
+ const otherElementsWidth = 59;
2444
+ const filenameWidth = 20;
2445
+ const availableWidth = terminalWidth - otherElementsWidth - filenameWidth - spinnerWidth;
2446
+ const adjustedBarSize = Math.max(10, Math.min(baseBarSize, availableWidth));
2447
+ return adjustedBarSize;
2448
+ }
2449
+ /**
2450
+ * Check if server supports resumable downloads
2451
+ * @param {string} url - The URL to check
2452
+ * @returns {Object} - Server support info and headers
2453
+ */
2454
+ async checkServerSupport(url) {
2455
+ try {
2456
+ const response = await fetch(url, {
2457
+ method: "HEAD",
2458
+ signal: this.abortController?.signal
2459
+ });
2460
+ if (!response.ok) {
2461
+ throw new Error(`HTTP error! status: ${response.status}`);
2462
+ }
2463
+ const acceptRanges = response.headers.get("accept-ranges");
2464
+ const contentLength = response.headers.get("content-length");
2465
+ const lastModified = response.headers.get("last-modified");
2466
+ const etag = response.headers.get("etag");
2467
+ return {
2468
+ supportsResume: acceptRanges === "bytes",
2469
+ totalSize: contentLength ? parseInt(contentLength, 10) : 0,
2470
+ lastModified,
2471
+ etag,
2472
+ headers: response.headers
2473
+ };
2474
+ } catch (error) {
2475
+ console.log(this.colors.warning("⚠️ Could not check server resume support, proceeding with regular download"));
2476
+ return {
2477
+ supportsResume: false,
2478
+ totalSize: 0,
2479
+ lastModified: null,
2480
+ etag: null,
2481
+ headers: null
2482
+ };
2483
+ }
2484
+ }
2485
+ /**
2486
+ * Load download state from file
2487
+ * @param {string} stateFilePath - Path to state file
2488
+ * @returns {Object} - Download state
2489
+ */
2490
+ loadDownloadState(stateFilePath) {
2491
+ try {
2492
+ if (fs.existsSync(stateFilePath)) {
2493
+ const stateData = fs.readFileSync(stateFilePath, "utf8");
2494
+ return JSON.parse(stateData);
2495
+ }
2496
+ } catch (error) {
2497
+ console.log(this.colors.warning("⚠️ Could not load download state, starting fresh"));
2498
+ }
2499
+ return null;
2500
+ }
2501
+ /**
2502
+ * Save download state to file
2503
+ * @param {string} stateFilePath - Path to state file
2504
+ * @param {Object} state - Download state
2505
+ */
2506
+ saveDownloadState(stateFilePath, state) {
2507
+ try {
2508
+ fs.writeFileSync(stateFilePath, JSON.stringify(state, null, 2));
2509
+ } catch (error) {
2510
+ console.log(this.colors.warning("⚠️ Could not save download state"));
2511
+ }
2512
+ }
2513
+ /**
2514
+ * Get partial file size
2515
+ * @param {string} filePath - Path to partial file
2516
+ * @returns {number} - Size of partial file
2517
+ */
2518
+ getPartialFileSize(filePath) {
2519
+ try {
2520
+ if (fs.existsSync(filePath)) {
2521
+ const stats = fs.statSync(filePath);
2522
+ return stats.size;
2523
+ }
2524
+ } catch (error) {
2525
+ console.log(this.colors.warning("⚠️ Could not read partial file size"));
2526
+ }
2527
+ return 0;
2528
+ }
2529
+ /**
2530
+ * Get random gradient color
2531
+ */
2532
+ getRandomColor() {
2533
+ return this.colors.gradient[Math.floor(Math.random() * this.colors.gradient.length)];
2534
+ }
2535
+ /**
2536
+ * Format bytes into human readable format with proper MB/GB units using 1024 base
2537
+ * @param {number} bytes - Number of bytes
2538
+ * @param {number} decimals - Number of decimal places
2539
+ * @returns {string} Formatted string
2540
+ */
2541
+ formatBytes(bytes, decimals = 2) {
2542
+ if (bytes === 0) return this.colors.info("0 B");
2543
+ const k = 1024;
2544
+ const dm = decimals < 0 ? 0 : decimals;
2545
+ const sizes = [
2546
+ { unit: "B", color: this.colors.info },
2547
+ { unit: "KB", color: this.colors.cyan },
2548
+ { unit: "MB", color: this.colors.yellow },
2549
+ { unit: "GB", color: this.colors.purple },
2550
+ { unit: "TB", color: this.colors.pink },
2551
+ { unit: "PB", color: this.colors.primary }
2552
+ ];
2553
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2554
+ const value = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
2555
+ const size = sizes[i] || sizes[sizes.length - 1];
2556
+ return size.color.bold(`${value} ${size.unit}`);
2557
+ }
2558
+ /**
2559
+ * Format bytes for progress display (without colors for progress bar)
2560
+ * @param {number} bytes - Number of bytes
2561
+ * @param {number} decimals - Number of decimal places
2562
+ * @returns {string} Formatted string without colors
2563
+ */
2564
+ formatBytesPlain(bytes, decimals = 1) {
2565
+ if (bytes === 0) return "0 B";
2566
+ const k = 1024;
2567
+ const dm = decimals < 0 ? 0 : decimals;
2568
+ const sizes = ["B", "KB", "MB", "GB", "TB", "PB"];
2569
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2570
+ const value = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
2571
+ return `${value} ${sizes[i] || sizes[sizes.length - 1]}`;
2572
+ }
2573
+ /**
2574
+ * Format bytes for progress display (compact version for tight layouts)
2575
+ * @param {number} bytes - Number of bytes
2576
+ * @returns {string} Formatted string in compact format
2577
+ */
2578
+ formatBytesCompact(bytes) {
2579
+ if (bytes === 0) return "0B";
2580
+ const k = 1024;
2581
+ const kb = bytes / k;
2582
+ if (kb < 100) {
2583
+ const value2 = Math.round(kb);
2584
+ return `${value2}KB`;
2585
+ }
2586
+ const mb = bytes / (k * k);
2587
+ const value = mb.toFixed(1);
2588
+ return `${value}`;
2589
+ }
2590
+ /**
2591
+ * Truncate filename for display
2592
+ * @param {string} filename - Original filename
2593
+ * @param {number} maxLength - Maximum length
2594
+ * @returns {string} Truncated filename
2595
+ */
2596
+ truncateFilename(filename, maxLength = 25) {
2597
+ if (filename.length <= maxLength) return filename.padEnd(maxLength);
2598
+ const extension = path.extname(filename);
2599
+ const baseName = path.basename(filename, extension);
2600
+ if (baseName.length <= 3) {
2601
+ return filename.padEnd(maxLength);
2602
+ }
2603
+ const firstPart = Math.ceil((maxLength - extension.length - 3) / 2);
2604
+ const lastPart = Math.floor((maxLength - extension.length - 3) / 2);
2605
+ const truncatedBase = baseName.substring(0, firstPart) + "..." + baseName.substring(baseName.length - lastPart);
2606
+ return `${truncatedBase}${extension}`.padEnd(maxLength);
2607
+ }
2608
+ /**
2609
+ * Format ETA time in hours:minutes:seconds format
2610
+ * @param {number} seconds - ETA in seconds
2611
+ * @returns {string} Formatted ETA string (padded to consistent width)
2612
+ */
2613
+ formatETA(seconds) {
2614
+ if (!seconds || seconds === Infinity || seconds < 0) return " -- ";
2615
+ const hours = Math.floor(seconds / 3600);
2616
+ const mins = Math.floor(seconds % 3600 / 60);
2617
+ const secs = Math.round(seconds % 60);
2618
+ return `${hours}:${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`.padEnd(this.COL_ETA);
2619
+ }
2620
+ /**
2621
+ * Format progress for master bar showing sum of all downloads
2622
+ * @param {number} totalDownloaded - Total downloaded bytes across all files
2623
+ * @param {number} totalSize - Total size bytes across all files
2624
+ * @returns {string} Formatted progress string showing sums in MB
2625
+ */
2626
+ formatMasterProgress(totalDownloaded, totalSize) {
2627
+ const k = 1024;
2628
+ const totalDownloadedMB = totalDownloaded / (k * k);
2629
+ const totalSizeMB = totalSize / (k * k);
2630
+ if (totalSizeMB >= 1024) {
2631
+ const totalDownloadedGB = totalDownloadedMB / 1024;
2632
+ return `${totalDownloadedGB.toFixed(1)}GB`.padEnd(this.COL_DOWNLOADED);
2633
+ }
2634
+ return `${totalDownloadedMB.toFixed(1)}MB`.padEnd(this.COL_DOWNLOADED);
2635
+ }
2636
+ /**
2637
+ * Format progress display with consistent width
2638
+ * @param {number} downloaded - Downloaded bytes
2639
+ * @param {number} total - Total bytes
2640
+ * @returns {string} Formatted progress string
2641
+ */
2642
+ formatProgress(downloaded, total) {
2643
+ const downloadedStr = this.formatBytesCompact(downloaded);
2644
+ return downloadedStr.padEnd(this.COL_DOWNLOADED);
2645
+ }
2646
+ /**
2647
+ * Format downloaded bytes for display
2648
+ * @param {number} downloaded - Downloaded bytes
2649
+ * @returns {string} Formatted downloaded string
2650
+ */
2651
+ formatDownloaded(downloaded) {
2652
+ return this.formatBytesCompact(downloaded).padEnd(this.COL_DOWNLOADED);
2653
+ }
2654
+ /**
2655
+ * Format total bytes for display (separate column)
2656
+ * @param {number} total - Total bytes
2657
+ * @returns {string} Formatted total string
2658
+ */
2659
+ formatTotalDisplay(total) {
2660
+ if (total === 0) return "0MB".padEnd(this.COL_TOTAL);
2661
+ const k = 1024;
2662
+ const mb = total / (k * k);
2663
+ if (mb >= 1024) {
2664
+ const gb = mb / 1024;
2665
+ return `${gb.toFixed(1)}GB`.padEnd(this.COL_TOTAL);
2666
+ }
2667
+ if (mb < 1) {
2668
+ return `${mb.toFixed(2)}MB`.padEnd(this.COL_TOTAL);
2669
+ }
2670
+ return `${mb.toFixed(1)}MB`.padEnd(this.COL_TOTAL);
2671
+ }
2672
+ /**
2673
+ * Format total bytes for display (MB/GB format)
2674
+ * @param {number} total - Total bytes
2675
+ * @returns {string} Formatted total string
2676
+ */
2677
+ formatTotal(total) {
2678
+ if (total === 0) return "0MB".padEnd(this.COL_TOTAL);
2679
+ const k = 1024;
2680
+ const mb = total / (k * k);
2681
+ if (mb >= 1024) {
2682
+ const gb = mb / 1024;
2683
+ return `${gb.toFixed(1)}GB`.padEnd(this.COL_TOTAL);
2684
+ }
2685
+ if (mb < 1) {
2686
+ return `${mb.toFixed(2)}MB`.padEnd(this.COL_TOTAL);
2687
+ }
2688
+ return `${mb.toFixed(1)}MB`.padEnd(this.COL_TOTAL);
2689
+ }
2690
+ /**
2691
+ * Format speed display with consistent width
2692
+ * @param {string} speed - Speed string
2693
+ * @returns {string} Formatted speed string
2694
+ */
2695
+ formatSpeed(speed) {
2696
+ return speed.padEnd(this.COL_SPEED);
2697
+ }
2698
+ /**
2699
+ * Format speed for display (MB/s without "MB" text unless below 100KB/s)
2700
+ * @param {number} bytesPerSecond - Speed in bytes per second
2701
+ * @returns {string} Formatted speed string
2702
+ */
2703
+ formatSpeedDisplay(bytesPerSecond) {
2704
+ if (bytesPerSecond === 0) return "0B";
2705
+ const k = 1024;
2706
+ const kbPerSecond = bytesPerSecond / k;
2707
+ if (kbPerSecond < 100) {
2708
+ const formattedValue2 = Math.round(kbPerSecond);
2709
+ return `${formattedValue2}KB`;
2710
+ }
2711
+ const mbPerSecond = bytesPerSecond / (k * k);
2712
+ const formattedValue = mbPerSecond.toFixed(1);
2713
+ return `${formattedValue}`;
2714
+ }
2715
+ /**
2716
+ * Format speed for total display (MB/s without "MB" text unless below 100KB/s)
2717
+ * @param {number} bytesPerSecond - Speed in bytes per second
2718
+ * @returns {string} Formatted speed string
2719
+ */
2720
+ formatTotalSpeed(bytesPerSecond) {
2721
+ return this.formatSpeedDisplay(bytesPerSecond).padEnd(this.COL_SPEED);
2722
+ }
2723
+ /**
2724
+ * Download multiple files with multibar progress tracking
2725
+ * @param {Array} downloads - Array of {url, outputPath, filename} objects
2726
+ */
2727
+ async downloadMultipleFiles(downloads) {
2728
+ try {
2729
+ this.setupGlobalKeyboardListener();
2730
+ const masterBarColor = this.getRandomBarColor();
2731
+ const masterBarGlue = this.getRandomBarGlueColor();
2732
+ this.multiBar = new cliProgress.MultiBar({
2733
+ format: this.colors.success("{percentage}%") + " " + this.colors.yellow("{filename}") + " " + this.colors.cyan("{spinner}") + " " + masterBarColor + "{bar}\x1B[0m " + this.colors.info("{downloadedDisplay}") + " " + this.colors.info("{totalDisplay}") + " " + this.colors.purple("{speed}") + " " + this.colors.pink("{etaFormatted}"),
2734
+ hideCursor: true,
2735
+ clearOnComplete: false,
2736
+ stopOnComplete: true,
2737
+ autopadding: false,
2738
+ barCompleteChar: "█",
2739
+ barIncompleteChar: "░",
2740
+ barGlue: masterBarGlue,
2741
+ barsize: this.COL_BAR
2742
+ });
2743
+ let totalDownloaded = 0;
2744
+ let totalSize = 0;
2745
+ let individualSpeeds = new Array(downloads.length).fill(0);
2746
+ let individualSizes = new Array(downloads.length).fill(0);
2747
+ let individualDownloaded = new Array(downloads.length).fill(0);
2748
+ let individualStartTimes = new Array(downloads.length).fill(Date.now());
2749
+ let lastSpeedUpdate = Date.now();
2750
+ let lastIndividualDownloaded = new Array(downloads.length).fill(0);
2751
+ let lastTotalUpdate = Date.now();
2752
+ let lastTotalDownloaded = 0;
2753
+ const totalSizeFromDownloads = downloads.reduce((sum, download) => {
2754
+ const estimatedSize = download.estimatedSize || 1024 * 1024 * 100;
2755
+ return sum + estimatedSize;
2756
+ }, 0);
2757
+ totalSize = totalSizeFromDownloads;
2758
+ let actualTotalSize = 0;
2759
+ const speedUpdateInterval = setInterval(() => {
2760
+ const now = Date.now();
2761
+ const timeSinceLastUpdate = (now - lastSpeedUpdate) / 1e3;
2762
+ for (let i = 0; i < downloads.length; i++) {
2763
+ if (timeSinceLastUpdate > 0) {
2764
+ const incrementalDownloaded = individualDownloaded[i] - lastIndividualDownloaded[i];
2765
+ individualSpeeds[i] = incrementalDownloaded / timeSinceLastUpdate;
2766
+ if (fileBars[i] && fileBars[i].bar) {
2767
+ const speed = this.formatSpeed(this.formatSpeedDisplay(individualSpeeds[i]));
2768
+ const eta2 = individualSizes[i] > 0 ? this.formatETA((individualSizes[i] - individualDownloaded[i]) / individualSpeeds[i]) : this.formatETA(0);
2769
+ fileBars[i].bar.update(individualDownloaded[i], {
2770
+ speed,
2771
+ progress: this.formatProgress(individualDownloaded[i], individualSizes[i]),
2772
+ downloadedDisplay: this.formatBytesCompact(individualDownloaded[i]),
2773
+ totalDisplay: this.formatTotalDisplay(individualSizes[i]),
2774
+ etaFormatted: eta2
2775
+ });
2776
+ }
2777
+ }
2778
+ }
2779
+ lastSpeedUpdate = now;
2780
+ lastIndividualDownloaded = [...individualDownloaded];
2781
+ const totalSpeedBps = individualSpeeds.reduce((sum, speed) => sum + speed, 0);
2782
+ const totalDownloadedFromFiles = individualDownloaded.reduce((sum, downloaded) => sum + downloaded, 0);
2783
+ const timeElapsed = (now - individualStartTimes[0]) / 1e3;
2784
+ const totalEta = totalSize > 0 && totalSpeedBps > 0 ? this.formatETA((totalSize - totalDownloadedFromFiles) / totalSpeedBps) : this.formatETA(0);
2785
+ const totalPercentage = totalSize > 0 ? Math.round(totalDownloadedFromFiles / totalSize * 100) : 0;
2786
+ const discoveredTotalSize = individualSizes.reduce((sum, size) => sum + size, 0);
2787
+ const displayTotalSize = discoveredTotalSize > 0 ? discoveredTotalSize : totalSize;
2788
+ masterBar.update(totalDownloadedFromFiles, {
2789
+ speed: this.formatTotalSpeed(totalSpeedBps),
2790
+ progress: this.formatMasterProgress(totalDownloadedFromFiles, displayTotalSize),
2791
+ downloadedDisplay: this.formatBytesCompact(totalDownloadedFromFiles),
2792
+ totalDisplay: this.formatTotalDisplay(displayTotalSize),
2793
+ etaFormatted: this.formatETA(timeElapsed),
2794
+ // Show time elapsed instead of ETA
2795
+ percentage: displayTotalSize > 0 ? Math.round(totalDownloadedFromFiles / displayTotalSize * 100) : 0
2796
+ });
2797
+ }, 1e3);
2798
+ const masterSpinnerWidth = this.getSpinnerWidth("⬇️");
2799
+ const masterMaxFilenameLength = this.COL_FILENAME - masterSpinnerWidth;
2800
+ const masterBarSize = this.calculateBarSize("⬇️", this.COL_BAR);
2801
+ const masterBar = this.multiBar.create(totalSize, 0, {
2802
+ filename: "Total".padEnd(masterMaxFilenameLength),
2803
+ spinner: "⬇️",
2804
+ speed: "0B".padEnd(this.COL_SPEED),
2805
+ progress: this.formatMasterProgress(0, totalSize),
2806
+ downloadedDisplay: this.formatBytesCompact(0),
2807
+ totalDisplay: this.formatTotalDisplay(totalSize),
2808
+ etaFormatted: this.formatETA(0),
2809
+ percentage: " 0".padStart(this.COL_PERCENT - 1)
2810
+ }, {
2811
+ format: this.colors.success("{percentage}%") + " " + this.colors.yellow.bold("{filename}") + " " + this.colors.success("{spinner}") + " \x1B[92m{bar}\x1B[0m " + this.colors.info("{downloadedDisplay}") + " " + this.colors.info("{totalDisplay}") + " " + this.colors.purple("{speed}") + " " + this.colors.pink("{etaFormatted}"),
2812
+ barCompleteChar: "▶",
2813
+ barIncompleteChar: "▷",
2814
+ barGlue: "\x1B[33m",
2815
+ barsize: masterBarSize
2816
+ });
2817
+ const fileBars = downloads.map((download, index) => {
2818
+ const spinnerType = this.getRandomSpinner();
2819
+ const spinnerFrames = this.getSpinnerFrames(spinnerType);
2820
+ const spinnerWidth = this.getSpinnerWidth(spinnerFrames[0]);
2821
+ const maxFilenameLength = this.COL_FILENAME - spinnerWidth;
2822
+ const truncatedName = this.truncateFilename(download.filename, maxFilenameLength);
2823
+ const fileBarColor = this.getRandomBarColor();
2824
+ const fileBarGlue = this.getRandomBarGlueColor();
2825
+ const barSize = this.calculateBarSize(spinnerFrames[0], this.COL_BAR);
2826
+ return {
2827
+ bar: this.multiBar.create(100, 0, {
2828
+ filename: truncatedName,
2829
+ spinner: spinnerFrames[0],
2830
+ speed: this.formatSpeed("0B"),
2831
+ progress: this.formatProgress(0, 0),
2832
+ downloadedDisplay: this.formatBytesCompact(0),
2833
+ totalDisplay: this.formatTotalDisplay(0),
2834
+ etaFormatted: this.formatETA(0),
2835
+ percentage: " 0".padStart(3)
2836
+ }, {
2837
+ format: this.colors.yellow("{filename}") + " " + this.colors.cyan("{spinner}") + " " + fileBarColor + "{bar}\x1B[0m " + this.colors.success("{percentage}%") + " " + this.colors.info("{downloadedDisplay}") + " " + this.colors.info("{totalDisplay}") + " " + this.colors.purple("{speed}") + " " + this.colors.pink("{etaFormatted}"),
2838
+ barCompleteChar: "█",
2839
+ barIncompleteChar: "░",
2840
+ barGlue: fileBarGlue,
2841
+ barsize: barSize
2842
+ }),
2843
+ spinnerFrames,
2844
+ spinnerIndex: 0,
2845
+ lastSpinnerUpdate: Date.now(),
2846
+ lastFrameUpdate: Date.now(),
2847
+ download: { ...download, index }
2848
+ };
2849
+ });
2850
+ const downloadPromises = fileBars.map(async (fileBar, index) => {
2851
+ try {
2852
+ await this.downloadSingleFileWithBar(fileBar, masterBar, downloads.length, {
2853
+ totalDownloaded,
2854
+ totalSize,
2855
+ individualSpeeds,
2856
+ individualSizes,
2857
+ individualDownloaded,
2858
+ individualStartTimes,
2859
+ lastTotalUpdate,
2860
+ lastTotalDownloaded,
2861
+ actualTotalSize
2862
+ });
2863
+ return { success: true, index, filename: fileBar.download.filename };
2864
+ } catch (error) {
2865
+ return { success: false, index, filename: fileBar.download.filename, error };
2866
+ }
2867
+ });
2868
+ const results = await Promise.allSettled(downloadPromises);
2869
+ clearInterval(speedUpdateInterval);
2870
+ this.multiBar.stop();
2871
+ const successful = results.filter((r) => r.status === "fulfilled" && r.value.success).length;
2872
+ const failed = results.length - successful;
2873
+ if (failed > 0) {
2874
+ console.log(this.colors.error(`❌ Failed: ${failed}/${downloads.length}`));
2875
+ results.forEach((result, index) => {
2876
+ if (result.status === "rejected" || !result.value.success) {
2877
+ const filename = downloads[index].filename;
2878
+ const error = result.reason || result.value?.error || "Unknown error";
2879
+ console.log(this.colors.error(` • ${filename}: ${error.message || error}`));
2880
+ }
2881
+ });
2882
+ }
2883
+ const celebrationEmojis = ["🥳", "🎊", "🎈", "🌟", "💯", "🚀", "✨", "🔥"];
2884
+ const randomEmoji = celebrationEmojis[Math.floor(Math.random() * celebrationEmojis.length)];
2885
+ console.log(this.colors.green(`${randomEmoji} Success: ${successful}/${downloads.length}`));
2886
+ this.clearAbortControllers();
2887
+ let pausedMessageShown = false;
2888
+ this.setPauseCallback(() => {
2889
+ if (!pausedMessageShown) {
2890
+ this.multiBar.stop();
2891
+ console.log(this.colors.warning("⏸️ Paused. Press p to resume, a to add URL."));
2892
+ pausedMessageShown = true;
2893
+ }
2894
+ });
2895
+ this.setResumeCallback(() => {
2896
+ if (pausedMessageShown) {
2897
+ console.log(this.colors.success("▶️ Resumed. Press p to pause, a to add URL."));
2898
+ pausedMessageShown = false;
2899
+ }
2900
+ });
2901
+ } catch (error) {
2902
+ if (this.multiBar) {
2903
+ this.multiBar.stop();
2904
+ }
2905
+ console.error(this.colors.error.bold("💥 Batch download failed: ") + this.colors.warning(error.message));
2906
+ throw error;
2907
+ }
2908
+ }
2909
+ /**
2910
+ * Download a single file with multibar integration and resume capability
2911
+ * @param {Object} fileBar - File bar object with progress bar and spinner info
2912
+ * @param {Object} masterBar - Master progress bar
2913
+ * @param {number} totalFiles - Total number of files being downloaded
2914
+ * @param {Object} totalTracking - Object to track total progress
2915
+ */
2916
+ async downloadSingleFileWithBar(fileBar, masterBar, totalFiles, totalTracking) {
2917
+ const { bar, spinnerFrames, download } = fileBar;
2918
+ const { url, outputPath, filename } = download;
2919
+ const stateFilePath = this.getStateFilePath(outputPath);
2920
+ const tempFilePath = outputPath + ".tmp";
2921
+ try {
2922
+ const abortController = new AbortController();
2923
+ this.setAbortController(abortController);
2924
+ const serverInfo = await this.checkServerSupport(url);
2925
+ const previousState = this.loadDownloadState(stateFilePath);
2926
+ const partialSize = this.getPartialFileSize(tempFilePath);
2927
+ let startByte = 0;
2928
+ let resuming = false;
2929
+ if (serverInfo.supportsResume && partialSize > 0 && previousState) {
2930
+ const fileUnchanged = (!serverInfo.lastModified || serverInfo.lastModified === previousState.lastModified) && (!serverInfo.etag || serverInfo.etag === previousState.etag) && serverInfo.totalSize === previousState.totalSize;
2931
+ if (fileUnchanged && partialSize < serverInfo.totalSize) {
2932
+ startByte = partialSize;
2933
+ resuming = true;
2934
+ } else {
2935
+ if (fs.existsSync(tempFilePath)) {
2936
+ fs.unlinkSync(tempFilePath);
2937
+ }
2938
+ this.cleanupStateFile(stateFilePath);
2939
+ }
2940
+ } else if (partialSize > 0) {
2941
+ if (fs.existsSync(tempFilePath)) {
2942
+ fs.unlinkSync(tempFilePath);
2943
+ }
2944
+ }
2945
+ const headers = {};
2946
+ if (resuming && startByte > 0) {
2947
+ headers["Range"] = `bytes=${startByte}-`;
2948
+ }
2949
+ const response = await fetch(url, {
2950
+ headers,
2951
+ signal: abortController.signal
2952
+ });
2953
+ if (!response.ok) {
2954
+ throw new Error(`HTTP error! status: ${response.status}`);
2955
+ }
2956
+ const contentLength = response.headers.get("content-length");
2957
+ const totalSize = resuming ? serverInfo.totalSize : contentLength ? parseInt(contentLength, 10) : 0;
2958
+ const downloadState = {
2959
+ url,
2960
+ outputPath,
2961
+ totalSize,
2962
+ startByte,
2963
+ lastModified: serverInfo.lastModified,
2964
+ etag: serverInfo.etag,
2965
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2966
+ };
2967
+ this.saveDownloadState(stateFilePath, downloadState);
2968
+ bar.setTotal(totalSize || 100);
2969
+ bar.update(startByte, {
2970
+ progress: this.formatProgress(startByte, totalSize),
2971
+ downloadedDisplay: this.formatBytesCompact(startByte),
2972
+ totalDisplay: this.formatTotalDisplay(totalSize)
2973
+ });
2974
+ const writeStream = fs.createWriteStream(tempFilePath, {
2975
+ flags: resuming ? "a" : "w"
2976
+ });
2977
+ let downloaded = startByte;
2978
+ let lastTime = Date.now();
2979
+ let lastDownloaded = downloaded;
2980
+ const progressStream = new Readable({
2981
+ read() {
2982
+ }
2983
+ });
2984
+ const reader = response.body.getReader();
2985
+ const processChunk = async () => {
2986
+ try {
2987
+ while (true) {
2988
+ while (this.isPaused) {
2989
+ await new Promise((resolve) => setTimeout(resolve, 100));
2990
+ }
2991
+ const { done, value } = await reader.read();
2992
+ if (done) {
2993
+ progressStream.push(null);
2994
+ break;
2995
+ }
2996
+ downloaded += value.length;
2997
+ const now = Date.now();
2998
+ const timeDiff = (now - lastTime) / 1e3;
2999
+ if (now - fileBar.lastFrameUpdate >= 150) {
3000
+ fileBar.spinnerIndex = (fileBar.spinnerIndex + 1) % spinnerFrames.length;
3001
+ fileBar.lastFrameUpdate = now;
3002
+ const currentSpinner = spinnerFrames[fileBar.spinnerIndex];
3003
+ const newBarSize = this.calculateBarSize(currentSpinner, this.COL_BAR);
3004
+ bar.options.barsize = newBarSize;
3005
+ }
3006
+ if (now - fileBar.lastSpinnerUpdate >= 45e3) {
3007
+ const newSpinnerType = this.getRandomSpinner();
3008
+ fileBar.spinnerFrames = this.getSpinnerFrames(newSpinnerType);
3009
+ fileBar.spinnerIndex = 0;
3010
+ fileBar.lastSpinnerUpdate = now;
3011
+ }
3012
+ if (timeDiff >= 0.3) {
3013
+ bar.update(downloaded, {
3014
+ spinner: spinnerFrames[fileBar.spinnerIndex],
3015
+ progress: this.formatProgress(downloaded, totalSize),
3016
+ downloadedDisplay: this.formatBytesCompact(downloaded),
3017
+ totalDisplay: this.formatTotalDisplay(totalSize)
3018
+ });
3019
+ if (totalTracking) {
3020
+ const bytesDiff = downloaded - lastDownloaded;
3021
+ totalTracking.totalDownloaded += bytesDiff;
3022
+ const fileIndex = fileBar.download.index || 0;
3023
+ totalTracking.individualDownloaded[fileIndex] = downloaded;
3024
+ totalTracking.individualSizes[fileIndex] = totalSize;
3025
+ totalTracking.totalSize = totalTracking.individualSizes.reduce((sum, size) => sum + size, 0);
3026
+ if (totalTracking.actualTotalSize !== void 0) {
3027
+ totalTracking.actualTotalSize = totalTracking.totalSize;
3028
+ }
3029
+ if (totalSize > 0 && totalTracking.individualSizes[fileIndex] === totalSize) {
3030
+ masterBar.setTotal(totalTracking.totalSize);
3031
+ }
3032
+ }
3033
+ lastTime = now;
3034
+ lastDownloaded = downloaded;
3035
+ } else {
3036
+ bar.update(downloaded, {
3037
+ spinner: spinnerFrames[fileBar.spinnerIndex],
3038
+ progress: this.formatProgress(downloaded, totalSize),
3039
+ downloadedDisplay: this.formatBytesCompact(downloaded),
3040
+ totalDisplay: this.formatTotalDisplay(totalSize)
3041
+ });
3042
+ }
3043
+ progressStream.push(Buffer.from(value));
3044
+ }
3045
+ } catch (error) {
3046
+ progressStream.destroy(error);
3047
+ }
3048
+ };
3049
+ processChunk();
3050
+ await pipeline(progressStream, writeStream);
3051
+ if (fs.existsSync(outputPath)) {
3052
+ fs.unlinkSync(outputPath);
3053
+ }
3054
+ fs.renameSync(tempFilePath, outputPath);
3055
+ this.cleanupStateFile(stateFilePath);
3056
+ const currentCompleted = masterBar.value + 1;
3057
+ const finalTotalSize = totalTracking.actualTotalSize || totalTracking.totalSize;
3058
+ const discoveredTotalSize = totalTracking.individualSizes.reduce((sum, size) => sum + size, 0);
3059
+ const displayTotalSize = discoveredTotalSize > 0 ? discoveredTotalSize : finalTotalSize;
3060
+ masterBar.update(totalTracking.totalDownloaded, {
3061
+ progress: this.formatMasterProgress(totalTracking.totalDownloaded, displayTotalSize),
3062
+ downloadedDisplay: this.formatBytesCompact(totalTracking.totalDownloaded),
3063
+ totalDisplay: this.formatTotalDisplay(displayTotalSize),
3064
+ etaFormatted: this.formatETA((Date.now() - (totalTracking.individualStartTimes?.[0] || Date.now())) / 1e3)
3065
+ // Show time elapsed
3066
+ });
3067
+ } catch (error) {
3068
+ bar.update(bar.total, {
3069
+ spinner: "❌",
3070
+ speed: this.formatSpeed("FAILED"),
3071
+ downloadedDisplay: this.formatBytesCompact(0),
3072
+ totalDisplay: this.formatTotalDisplay(0)
3073
+ });
3074
+ console.log(this.colors.info(`💾 Partial download saved for ${filename}. Restart to resume.`));
3075
+ throw error;
3076
+ }
3077
+ }
3078
+ /**
3079
+ * Download a file with colorful progress tracking and resume capability
3080
+ * @param {string} url - The URL to download
3081
+ * @param {string} outputPath - The local path to save the file
3082
+ */
3083
+ async downloadFile(url, outputPath) {
3084
+ const stateFilePath = this.getStateFilePath(outputPath);
3085
+ const tempFilePath = outputPath + ".tmp";
3086
+ try {
3087
+ this.abortController = new AbortController();
3088
+ const randomOraSpinner = this.getRandomOraSpinner();
3089
+ this.loadingSpinner = ora({
3090
+ text: this.colors.primary("🌐 Checking server capabilities..."),
3091
+ spinner: randomOraSpinner,
3092
+ color: "cyan"
3093
+ }).start();
3094
+ const serverInfo = await this.checkServerSupport(url);
3095
+ const previousState = this.loadDownloadState(stateFilePath);
3096
+ const partialSize = this.getPartialFileSize(tempFilePath);
3097
+ let startByte = 0;
3098
+ let resuming = false;
3099
+ if (serverInfo.supportsResume && partialSize > 0 && previousState) {
3100
+ const fileUnchanged = (!serverInfo.lastModified || serverInfo.lastModified === previousState.lastModified) && (!serverInfo.etag || serverInfo.etag === previousState.etag) && serverInfo.totalSize === previousState.totalSize;
3101
+ if (fileUnchanged && partialSize < serverInfo.totalSize) {
3102
+ startByte = partialSize;
3103
+ resuming = true;
3104
+ this.loadingSpinner.succeed(this.colors.success(`✅ Found partial download: ${this.formatBytes(partialSize)} of ${this.formatTotal(serverInfo.totalSize)}`));
3105
+ console.log(this.colors.info(`🔄 Resuming download from ${this.formatBytes(startByte)}`));
3106
+ } else {
3107
+ this.loadingSpinner.warn(this.colors.warning("⚠️ File changed on server, starting fresh download"));
3108
+ if (fs.existsSync(tempFilePath)) {
3109
+ fs.unlinkSync(tempFilePath);
3110
+ }
3111
+ this.cleanupStateFile(stateFilePath);
3112
+ }
3113
+ } else {
3114
+ this.loadingSpinner.stop();
3115
+ if (partialSize > 0) {
3116
+ console.log(this.colors.warning("⚠️ Server does not support resumable downloads, starting fresh"));
3117
+ if (fs.existsSync(tempFilePath)) {
3118
+ fs.unlinkSync(tempFilePath);
3119
+ }
3120
+ }
3121
+ }
3122
+ const headers = {};
3123
+ if (resuming && startByte > 0) {
3124
+ headers["Range"] = `bytes=${startByte}-`;
3125
+ }
3126
+ const response = await fetch(url, {
3127
+ headers,
3128
+ signal: this.abortController.signal
3129
+ });
3130
+ if (!response.ok) {
3131
+ throw new Error(`HTTP error! status: ${response.status}`);
3132
+ }
3133
+ const contentLength = response.headers.get("content-length");
3134
+ const totalSize = resuming ? serverInfo.totalSize : contentLength ? parseInt(contentLength, 10) : 0;
3135
+ const remainingSize = contentLength ? parseInt(contentLength, 10) : 0;
3136
+ if (!resuming) {
3137
+ if (totalSize === 0) {
3138
+ console.log(this.colors.warning("⚠️ Warning: Content-Length not provided, progress will be estimated"));
3139
+ } else {
3140
+ console.log(this.colors.info(`📦 File size: ${this.formatTotal(totalSize)}`));
3141
+ }
3142
+ }
3143
+ const downloadState = {
3144
+ url,
3145
+ outputPath,
3146
+ totalSize,
3147
+ startByte,
3148
+ lastModified: serverInfo.lastModified,
3149
+ etag: serverInfo.etag,
3150
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3151
+ };
3152
+ this.saveDownloadState(stateFilePath, downloadState);
3153
+ const singleBarColor = this.getRandomBarColor();
3154
+ const singleBarGlue = this.getRandomBarGlueColor();
3155
+ let currentSpinnerType = this.getRandomSpinner();
3156
+ let spinnerFrames = this.getSpinnerFrames(currentSpinnerType);
3157
+ let spinnerFrameIndex = 0;
3158
+ const initialBarSize = this.calculateBarSize(spinnerFrames[0], this.COL_BAR);
3159
+ console.log(
3160
+ this.colors.success("📈 %".padEnd(this.COL_PERCENT)) + this.colors.cyan("🔄".padEnd(this.COL_SPINNER)) + " " + this.colors.green("📊 Progress".padEnd(this.COL_BAR + 1)) + this.colors.info("📥 Downloaded".padEnd(this.COL_DOWNLOADED)) + this.colors.info("📦 Total".padEnd(this.COL_TOTAL)) + this.colors.purple("⚡ Speed".padEnd(this.COL_SPEED)) + this.colors.pink("⏱️ ETA".padEnd(this.COL_ETA))
3161
+ );
3162
+ const keyboardRl = this.setupSingleFileKeyboardListeners(url, outputPath);
3163
+ this.progressBar = new cliProgress.SingleBar({
3164
+ format: this.colors.success("{percentage}%") + " " + this.colors.cyan("{spinner}") + " " + singleBarColor + "{bar}\x1B[0m " + this.colors.info("{downloadedDisplay}") + " " + this.colors.info("{totalDisplay}") + " " + this.colors.purple("{speed}") + " " + this.colors.pink("{etaFormatted}"),
3165
+ barCompleteChar: "█",
3166
+ barIncompleteChar: "░",
3167
+ barGlue: singleBarGlue,
3168
+ hideCursor: true,
3169
+ barsize: initialBarSize,
3170
+ stopOnComplete: true,
3171
+ clearOnComplete: false
3172
+ });
3173
+ this.progressBar.start(totalSize || 100, startByte, {
3174
+ speed: this.formatSpeed("0B/s"),
3175
+ etaFormatted: this.formatETA(0),
3176
+ spinner: spinnerFrames[0],
3177
+ progress: this.formatProgress(startByte, totalSize),
3178
+ downloadedDisplay: this.formatBytesCompact(startByte),
3179
+ totalDisplay: this.formatTotalDisplay(totalSize)
3180
+ });
3181
+ const writeStream = fs.createWriteStream(tempFilePath, {
3182
+ flags: resuming ? "a" : "w"
3183
+ });
3184
+ let downloaded = startByte;
3185
+ let sessionDownloaded = 0;
3186
+ let lastTime = Date.now();
3187
+ let lastDownloaded = downloaded;
3188
+ let lastSpinnerUpdate = Date.now();
3189
+ let lastSpinnerFrameUpdate = Date.now();
3190
+ const progressStream = new Readable({
3191
+ read() {
3192
+ }
3193
+ // No-op, we'll push data manually
3194
+ });
3195
+ const reader = response.body.getReader();
3196
+ const processChunk = async () => {
3197
+ try {
3198
+ while (true) {
3199
+ while (this.isPaused) {
3200
+ await new Promise((resolve) => setTimeout(resolve, 100));
3201
+ }
3202
+ const { done, value } = await reader.read();
3203
+ if (done) {
3204
+ progressStream.push(null);
3205
+ break;
3206
+ }
3207
+ sessionDownloaded += value.length;
3208
+ downloaded += value.length;
3209
+ const now = Date.now();
3210
+ const timeDiff = (now - lastTime) / 1e3;
3211
+ if (now - lastSpinnerUpdate >= 45e3) {
3212
+ currentSpinnerType = this.getRandomSpinner();
3213
+ spinnerFrames = this.getSpinnerFrames(currentSpinnerType);
3214
+ spinnerFrameIndex = 0;
3215
+ lastSpinnerUpdate = now;
3216
+ }
3217
+ if (now - lastSpinnerFrameUpdate >= 120) {
3218
+ spinnerFrameIndex = (spinnerFrameIndex + 1) % spinnerFrames.length;
3219
+ lastSpinnerFrameUpdate = now;
3220
+ const currentSpinner = spinnerFrames[spinnerFrameIndex];
3221
+ const newBarSize = this.calculateBarSize(currentSpinner, this.COL_BAR);
3222
+ this.progressBar.options.barsize = newBarSize;
3223
+ }
3224
+ if (timeDiff >= 0.3) {
3225
+ const bytesDiff = downloaded - lastDownloaded;
3226
+ const speedBps = bytesDiff / timeDiff;
3227
+ const speed = this.formatSpeed(this.formatSpeedDisplay(speedBps));
3228
+ const eta2 = totalSize > 0 ? this.formatETA((totalSize - downloaded) / speedBps) : this.formatETA(0);
3229
+ this.progressBar.update(downloaded, {
3230
+ speed,
3231
+ etaFormatted: eta2,
3232
+ spinner: spinnerFrames[spinnerFrameIndex],
3233
+ progress: this.formatProgress(downloaded, totalSize),
3234
+ downloadedDisplay: this.formatBytesCompact(downloaded),
3235
+ totalDisplay: this.formatTotalDisplay(totalSize)
3236
+ });
3237
+ lastTime = now;
3238
+ lastDownloaded = downloaded;
3239
+ } else {
3240
+ this.progressBar.update(downloaded, {
3241
+ spinner: spinnerFrames[spinnerFrameIndex],
3242
+ progress: this.formatProgress(downloaded, totalSize),
3243
+ downloadedDisplay: this.formatBytesCompact(downloaded),
3244
+ totalDisplay: this.formatTotalDisplay(totalSize)
3245
+ });
3246
+ }
3247
+ progressStream.push(Buffer.from(value));
3248
+ }
3249
+ } catch (error) {
3250
+ progressStream.destroy(error);
3251
+ }
3252
+ };
3253
+ processChunk();
3254
+ await pipeline(progressStream, writeStream);
3255
+ this.progressBar.stop();
3256
+ if (fs.existsSync(outputPath)) {
3257
+ fs.unlinkSync(outputPath);
3258
+ }
3259
+ fs.renameSync(tempFilePath, outputPath);
3260
+ this.cleanupStateFile(stateFilePath);
3261
+ console.log(this.colors.success("✅ Download completed!"));
3262
+ console.log(this.colors.primary("📁 File saved to: ") + chalk.underline(outputPath));
3263
+ console.log(this.colors.purple("📊 Total size: ") + this.formatBytes(downloaded));
3264
+ if (resuming) {
3265
+ console.log(this.colors.info("🔄 Resumed from: ") + this.formatBytes(startByte));
3266
+ console.log(this.colors.info("📥 Downloaded this session: ") + this.formatBytes(sessionDownloaded));
3267
+ }
3268
+ const celebrationEmojis = ["🥳", "🎊", "🎈", "🌟", "💯", "🚀", "✨", "🔥"];
3269
+ const randomEmoji = celebrationEmojis[Math.floor(Math.random() * celebrationEmojis.length)];
3270
+ console.log(this.colors.success(`${randomEmoji} Successfully downloaded! ${randomEmoji}`));
3271
+ } catch (error) {
3272
+ if (this.loadingSpinner && this.loadingSpinner.isSpinning) {
3273
+ this.loadingSpinner.fail(this.colors.error("❌ Connection failed"));
3274
+ }
3275
+ if (this.progressBar) {
3276
+ this.progressBar.stop();
3277
+ }
3278
+ console.error(this.colors.error.bold("💥 Download failed: ") + this.colors.warning(error.message));
3279
+ if (error.name === "AbortError") {
3280
+ console.log(this.colors.info("💾 Download state saved. You can resume later by running the same command."));
3281
+ } else {
3282
+ console.log(this.colors.info("💾 Partial download saved. Restart to resume from where it left off."));
3283
+ }
3284
+ throw error;
3285
+ }
3286
+ }
3287
+ /**
3288
+ * Clean up resources
3289
+ */
3290
+ cleanup() {
3291
+ if (this.loadingSpinner && this.loadingSpinner.isSpinning) {
3292
+ this.loadingSpinner.stop();
3293
+ }
3294
+ if (this.progressBar) {
3295
+ this.progressBar.stop();
3296
+ }
3297
+ if (this.multiBar) {
3298
+ this.multiBar.stop();
3299
+ }
3300
+ if (this.abortController) {
3301
+ this.abortController.abort();
3302
+ }
3303
+ if (this.keyboardListener) {
3304
+ try {
3305
+ this.keyboardListener.kill();
3306
+ } catch (error) {
3307
+ }
3308
+ }
3309
+ if (process.stdin.isTTY) {
3310
+ process.stdin.setRawMode(false);
3311
+ process.stdin.pause();
3312
+ }
3313
+ }
3314
+ /**
3315
+ * Set up global keyboard listener for pause/resume and add URL functionality
3316
+ */
3317
+ setupGlobalKeyboardListener() {
3318
+ this.setupFallbackKeyboardListener();
3319
+ }
3320
+ /**
3321
+ * Handle global key press events
3322
+ * @param {string} keyName - The name of the pressed key
3323
+ */
3324
+ async handleGlobalKeyPress(keyName) {
3325
+ if (keyName === "P") {
3326
+ console.log(this.colors.info("P key pressed - toggling pause/resume"));
3327
+ if (!this.isPaused) {
3328
+ this.pauseAll();
3329
+ } else {
3330
+ this.resumeAll();
3331
+ }
3332
+ } else if (keyName === "A" && !this.isAddingUrl) {
3333
+ console.log(this.colors.info("A key pressed - adding URL"));
3334
+ await this.promptForNewUrl();
3335
+ }
3336
+ }
3337
+ /**
3338
+ * Prompt user for a new URL to download
3339
+ */
3340
+ async promptForNewUrl() {
3341
+ this.isAddingUrl = true;
3342
+ try {
3343
+ console.log(this.colors.cyan("\n📥 Enter URL to add (or press Enter to cancel):"));
3344
+ const rl = require$$0.createInterface({
3345
+ input: process.stdin,
3346
+ output: process.stdout
3347
+ });
3348
+ const newUrl = await new Promise((resolve) => {
3349
+ rl.question("", (answer) => {
3350
+ rl.close();
3351
+ resolve(answer.trim());
3352
+ });
3353
+ });
3354
+ if (newUrl && this.isValidUrl(newUrl)) {
3355
+ console.log(this.colors.success(`✅ Adding URL: ${newUrl}`));
3356
+ const newFilename = this.generateFilename(newUrl);
3357
+ const newOutputPath = path.isAbsolute(newFilename) ? newFilename : path.join(process.cwd(), newFilename);
3358
+ const outputDir = path.dirname(newOutputPath);
3359
+ try {
3360
+ if (!fs.existsSync(outputDir)) {
3361
+ fs.mkdirSync(outputDir, { recursive: true });
3362
+ }
3363
+ } catch (error) {
3364
+ console.error(this.colors.red.bold("❌ Could not create output directory: ") + error.message);
3365
+ return;
3366
+ }
3367
+ if (this.multiBar) {
3368
+ await this.addToMultipleDownloads(newUrl, newOutputPath, newFilename);
3369
+ } else {
3370
+ this.downloadFile(newUrl, newOutputPath).catch((error) => {
3371
+ console.error(this.colors.error(`❌ Failed to download ${newFilename}: ${error.message}`));
3372
+ });
3373
+ }
3374
+ console.log(this.colors.success("🚀 New download started!"));
3375
+ } else if (newUrl) {
3376
+ console.log(this.colors.red("❌ Invalid URL provided."));
3377
+ } else {
3378
+ console.log(this.colors.yellow("⚠️ No URL provided, cancelling."));
3379
+ }
3380
+ } catch (error) {
3381
+ console.error(this.colors.red("❌ Error adding URL: ") + error.message);
3382
+ } finally {
3383
+ this.isAddingUrl = false;
3384
+ if (this.isPaused) {
3385
+ console.log(this.colors.warning("⏸️ Still paused. Press p to resume, a to add URL."));
3386
+ } else {
3387
+ console.log(this.colors.success("▶️ Downloads active. Press p to pause, a to add URL."));
3388
+ }
3389
+ }
3390
+ }
3391
+ /**
3392
+ * Add a new download to the multiple downloads queue
3393
+ * @param {string} url - The URL to download
3394
+ * @param {string} outputPath - The output path
3395
+ * @param {string} filename - The filename
3396
+ */
3397
+ async addToMultipleDownloads(url, outputPath, filename) {
3398
+ const spinnerType = this.getRandomSpinner();
3399
+ const spinnerFrames = this.getSpinnerFrames(spinnerType);
3400
+ const spinnerWidth = this.getSpinnerWidth(spinnerFrames[0]);
3401
+ const maxFilenameLength = this.COL_FILENAME - spinnerWidth;
3402
+ const truncatedName = this.truncateFilename(filename, maxFilenameLength);
3403
+ const fileBarColor = this.getRandomBarColor();
3404
+ const fileBarGlue = this.getRandomBarGlueColor();
3405
+ const barSize = this.calculateBarSize(spinnerFrames[0], this.COL_BAR);
3406
+ const newDownload = {
3407
+ url,
3408
+ outputPath,
3409
+ filename
3410
+ };
3411
+ const newFileBar = {
3412
+ bar: this.multiBar.create(100, 0, {
3413
+ filename: truncatedName,
3414
+ spinner: spinnerFrames[0],
3415
+ speed: this.formatSpeed("0B"),
3416
+ progress: this.formatProgress(0, 0),
3417
+ downloadedDisplay: this.formatBytesCompact(0),
3418
+ totalDisplay: this.formatTotalDisplay(0),
3419
+ etaFormatted: this.formatETA(0),
3420
+ percentage: " 0".padStart(3)
3421
+ }, {
3422
+ format: this.colors.yellow("{filename}") + " " + this.colors.cyan("{spinner}") + " " + fileBarColor + "{bar}\x1B[0m " + this.colors.success("{percentage}%") + " " + this.colors.info("{downloadedDisplay}") + " " + this.colors.info("{totalDisplay}") + " " + this.colors.purple("{speed}") + " " + this.colors.pink("{etaFormatted}"),
3423
+ barCompleteChar: "█",
3424
+ barIncompleteChar: "░",
3425
+ barGlue: fileBarGlue,
3426
+ barsize: barSize
3427
+ }),
3428
+ spinnerFrames,
3429
+ spinnerIndex: 0,
3430
+ lastSpinnerUpdate: Date.now(),
3431
+ lastFrameUpdate: Date.now(),
3432
+ download: { ...newDownload, index: this.getCurrentDownloadCount() }
3433
+ };
3434
+ this.downloadSingleFileWithBar(newFileBar, this.getMasterBar(), this.getCurrentDownloadCount() + 1, {
3435
+ totalDownloaded: 0,
3436
+ totalSize: 0,
3437
+ individualSpeeds: [],
3438
+ individualSizes: [],
3439
+ individualDownloaded: [],
3440
+ individualStartTimes: [],
3441
+ lastTotalUpdate: Date.now(),
3442
+ lastTotalDownloaded: 0,
3443
+ actualTotalSize: 0
3444
+ }).catch((error) => {
3445
+ console.error(this.colors.error(`❌ Failed to download ${newDownload.filename}: ${error.message}`));
3446
+ });
3447
+ }
3448
+ /**
3449
+ * Get current download count (for multiple downloads)
3450
+ * @returns {number} Current number of downloads
3451
+ */
3452
+ getCurrentDownloadCount() {
3453
+ return 1;
3454
+ }
3455
+ /**
3456
+ * Get master bar (for multiple downloads)
3457
+ * @returns {Object} Master progress bar
3458
+ */
3459
+ getMasterBar() {
3460
+ return null;
3461
+ }
3462
+ /**
3463
+ * Set up fallback keyboard listener using readline
3464
+ */
3465
+ setupFallbackKeyboardListener() {
3466
+ if (process.stdin.isTTY) {
3467
+ process.stdin.setRawMode(true);
3468
+ process.stdin.resume();
3469
+ process.stdin.setEncoding("utf8");
3470
+ const handleKeypress = async (str) => {
3471
+ if (str === "") {
3472
+ console.log(this.colors.yellow.bold("\n🛑 Download cancelled by user"));
3473
+ process.exit(0);
3474
+ }
3475
+ if (str.toLowerCase() === "p") {
3476
+ console.log(this.colors.info("P key pressed - toggling pause/resume"));
3477
+ if (!this.isPaused) {
3478
+ this.pauseAll();
3479
+ } else {
3480
+ this.resumeAll();
3481
+ }
3482
+ }
3483
+ if (str.toLowerCase() === "a" && !this.isAddingUrl) {
3484
+ console.log(this.colors.info("A key pressed - adding URL"));
3485
+ await this.promptForNewUrl();
3486
+ }
3487
+ };
3488
+ process.stdin.on("data", handleKeypress);
3489
+ }
3490
+ }
3491
+ /**
3492
+ * Set up keyboard listeners for single file download (legacy method)
3493
+ * @param {string} url - The URL being downloaded
3494
+ * @param {string} outputPath - The output path
3495
+ */
3496
+ setupSingleFileKeyboardListeners(url, outputPath) {
3497
+ this.setupGlobalKeyboardListener();
3498
+ return null;
3499
+ }
3500
+ /**
3501
+ * Validate URL format
3502
+ * @param {string} url - URL to validate
3503
+ * @returns {boolean} - Whether URL is valid
3504
+ */
3505
+ isValidUrl(url) {
3506
+ try {
3507
+ new URL(url);
3508
+ return true;
3509
+ } catch {
3510
+ return false;
3511
+ }
3512
+ }
3513
+ /**
3514
+ * Get file extension from URL
3515
+ * @param {string} url - URL to extract extension from
3516
+ * @returns {string} - File extension or empty string
3517
+ */
3518
+ getFileExtension(url) {
3519
+ try {
3520
+ const pathname = new URL(url).pathname;
3521
+ return path.extname(pathname).toLowerCase();
3522
+ } catch {
3523
+ return "";
3524
+ }
3525
+ }
3526
+ /**
3527
+ * Generate filename from URL
3528
+ * @param {string} url - Download URL
3529
+ * @returns {string} - Generated filename
3530
+ */
3531
+ generateFilename(url) {
3532
+ try {
3533
+ const filename = path.basename(new URL(url).pathname);
3534
+ return filename || "downloaded-file";
3535
+ } catch (error) {
3536
+ return "downloaded-file";
3537
+ }
3538
+ }
3539
+ setPauseCallback(cb) {
3540
+ this.pauseCallback = cb;
3541
+ }
3542
+ setResumeCallback(cb) {
3543
+ this.resumeCallback = cb;
3544
+ }
3545
+ setAbortController(controller) {
3546
+ this.abortControllers.push(controller);
3547
+ }
3548
+ clearAbortControllers() {
3549
+ this.abortControllers = [];
3550
+ }
3551
+ pauseAll() {
3552
+ this.isPaused = true;
3553
+ console.log(this.colors.warning("⏸️ Pausing all downloads..."));
3554
+ if (this.pauseCallback) this.pauseCallback();
3555
+ }
3556
+ resumeAll() {
3557
+ this.isPaused = false;
3558
+ console.log(this.colors.success("▶️ Resuming all downloads..."));
3559
+ if (this.resumeCallback) this.resumeCallback();
3560
+ }
3561
+ }
3562
+ const __isMain = typeof import.meta.main === "boolean" && import.meta.main || (() => {
3563
+ try {
3564
+ const scriptArg = process.argv[1] ? path.resolve(process.argv[1]) : "";
3565
+ if (!scriptArg) return false;
3566
+ const href = pathToFileURL(scriptArg).href;
3567
+ return import.meta.url === href;
3568
+ } catch {
3569
+ return false;
3570
+ }
3571
+ })();
3572
+ if (__isMain) {
3573
+ const argv = new ArgParser().usage("Usage: grab-url <url...> [options]").command("$0 <url>", "Fetch data or download files; pass one or more URLs").option("no-save", {
3574
+ type: "boolean",
3575
+ default: false,
3576
+ describe: "Don't save output to file, just print to console"
3577
+ }).option("output", {
3578
+ alias: "o",
3579
+ type: "string",
3580
+ describe: "Output filename (default: output.json)",
3581
+ default: null
3582
+ }).option("params", {
3583
+ alias: "p",
3584
+ type: "string",
3585
+ describe: `JSON string of query parameters (e.g., '{"key":"value"}')`,
3586
+ coerce: (arg) => {
3587
+ if (!arg) return {};
3588
+ try {
3589
+ return JSON.parse(arg);
3590
+ } catch (e) {
3591
+ throw new Error(`Invalid JSON in params: ${arg}`);
3592
+ }
3593
+ }
3594
+ }).help().alias("h", "help").example("grab-url https://api.example.com/data", "Fetch JSON/text from an API and save to output.json").example("grab-url https://example.com/file1.zip https://example.com/file2.zip", "Download multiple files concurrently").example("grab-url https://example.com/file.iso -o ubuntu.iso", "Save the first URL to a custom filename").version("1.0.0").strict().parseSync();
3595
+ const urls = argv.urls || [];
3596
+ const params = argv.params || {};
3597
+ const outputFile = argv.output;
3598
+ const noSave = argv["no-save"];
3599
+ const anyFileUrl = urls.some(isFileUrl);
3600
+ const isDownloadMode = urls.length > 1 || anyFileUrl;
3601
+ (async () => {
3602
+ if (isDownloadMode) {
3603
+ const downloader = new ColorFileDownloader();
3604
+ const downloads = urls.map((url, i) => {
3605
+ let filename = null;
3606
+ if (i === 0 && outputFile) filename = outputFile;
3607
+ return { url, outputPath: filename };
3608
+ });
3609
+ const downloadObjects = downloads.map((download, index) => {
3610
+ let actualUrl = download.url;
3611
+ let filename = download.outputPath;
3612
+ if (!filename) filename = downloader.generateFilename(actualUrl);
3613
+ const outputPath = path.isAbsolute(filename) ? filename : path.join(process.cwd(), filename);
3614
+ const outputDir = path.dirname(outputPath);
3615
+ try {
3616
+ if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
3617
+ } catch (error) {
3618
+ console.error(chalk.red.bold("❌ Could not create output directory: ") + error.message);
3619
+ process.exit(1);
3620
+ }
3621
+ return {
3622
+ url: actualUrl,
3623
+ outputPath,
3624
+ filename: path.basename(filename)
3625
+ };
3626
+ });
3627
+ try {
3628
+ await downloader.downloadMultipleFiles(downloadObjects);
3629
+ const statsTable = new Table({
3630
+ head: ["Filename", "Size", "Created"],
3631
+ colWidths: [32, 14, 25],
3632
+ colAligns: ["left", "right", "left"],
3633
+ style: { "padding-left": 1, "padding-right": 1, head: [], border: [] }
3634
+ });
3635
+ downloadObjects.forEach((downloadObj) => {
3636
+ try {
3637
+ const stats = fs.statSync(downloadObj.outputPath);
3638
+ statsTable.push([
3639
+ downloadObj.filename,
3640
+ downloader.formatBytes(stats.size),
3641
+ stats.birthtime.toLocaleString()
3642
+ ]);
3643
+ } catch (error) {
3644
+ statsTable.push([
3645
+ downloadObj.filename,
3646
+ "Error",
3647
+ "Could not read"
3648
+ ]);
3649
+ }
3650
+ });
3651
+ console.log(chalk.cyan.bold("\nFile Details:"));
3652
+ console.log(statsTable.toString());
3653
+ } catch (error) {
3654
+ console.error(chalk.red.bold("Failed to download files: ") + chalk.yellow(error.message));
3655
+ process.exit(1);
3656
+ }
3657
+ downloader.cleanup();
3658
+ } else {
3659
+ const url = urls[0];
3660
+ const startTime = process.hrtime();
3661
+ try {
3662
+ const res = await grab(url, params);
3663
+ if (res.error) log(`
3664
+
3665
+ Status: ❌ ${res.error}`);
3666
+ let filePath = null;
3667
+ let outputData;
3668
+ let isTextData = false;
3669
+ if (typeof res.data === "string") {
3670
+ outputData = res.data;
3671
+ isTextData = true;
3672
+ } else if (Buffer.isBuffer(res.data) || res.data instanceof Uint8Array) {
3673
+ outputData = res.data;
3674
+ isTextData = false;
3675
+ } else if (res.data instanceof Blob) {
3676
+ const arrayBuffer = await res.data.arrayBuffer();
3677
+ outputData = Buffer.from(arrayBuffer);
3678
+ isTextData = false;
3679
+ } else if (res.data && typeof res.data === "object") {
3680
+ outputData = JSON.stringify(res.data, null, 2);
3681
+ isTextData = true;
3682
+ } else {
3683
+ outputData = String(res.data);
3684
+ isTextData = true;
3685
+ }
3686
+ if (!noSave) {
3687
+ const urlPath = new URL(url).pathname;
3688
+ const urlExt = path.extname(urlPath);
3689
+ const defaultExt = isTextData ? ".json" : urlExt || ".bin";
3690
+ filePath = outputFile ? path.resolve(outputFile) : path.resolve(process.cwd(), `output${defaultExt}`);
3691
+ if (isTextData) fs.writeFileSync(filePath, outputData, "utf8");
3692
+ else fs.writeFileSync(filePath, outputData);
3693
+ const [seconds, nanoseconds] = process.hrtime(startTime);
3694
+ const elapsedMs = (seconds + nanoseconds / 1e9).toFixed(2);
3695
+ const stats = fs.statSync(filePath);
3696
+ const fileSizeMB = (stats.size / (1024 * 1024)).toFixed(1);
3697
+ log(`⏱️ ${elapsedMs}s 📦 ${fileSizeMB}MB ✅ Saved to: ${filePath}`);
3698
+ } else {
3699
+ if (isTextData) {
3700
+ log(outputData);
3701
+ } else {
3702
+ log(`Binary data received (${outputData.length} bytes). Use --output to save to file.`);
3703
+ }
3704
+ }
3705
+ } catch (error) {
3706
+ log(`Error: ${error.message}`, { color: "red" });
3707
+ process.exit(1);
3708
+ }
3709
+ }
3710
+ })();
3711
+ }
3712
+ export {
3713
+ ColorFileDownloader
3714
+ };
3715
+ //# sourceMappingURL=download.es.js.map