superslate 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +664 -473
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,102 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  // @bun
3
- var __create = Object.create;
4
- var __getProtoOf = Object.getPrototypeOf;
5
- var __defProp = Object.defineProperty;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
- var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
- target = mod != null ? __create(__getProtoOf(mod)) : {};
22
- const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
- if (mod && typeof mod === "object" || typeof mod === "function") {
24
- for (let key of __getOwnPropNames(mod))
25
- if (!__hasOwnProp.call(to, key))
26
- __defProp(to, key, {
27
- get: __accessProp.bind(mod, key),
28
- enumerable: true
29
- });
30
- }
31
- if (canCache)
32
- cache.set(mod, to);
33
- return to;
34
- };
35
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
36
-
37
- // node_modules/sisteransi/src/index.js
38
- var require_src = __commonJS(function(exports, module) {
39
- var ESC = "\x1B";
40
- var CSI = `${ESC}[`;
41
- var beep = "\x07";
42
- var cursor = {
43
- to(x, y) {
44
- if (!y)
45
- return `${CSI}${x + 1}G`;
46
- return `${CSI}${y + 1};${x + 1}H`;
47
- },
48
- move(x, y) {
49
- let ret = "";
50
- if (x < 0)
51
- ret += `${CSI}${-x}D`;
52
- else if (x > 0)
53
- ret += `${CSI}${x}C`;
54
- if (y < 0)
55
- ret += `${CSI}${-y}A`;
56
- else if (y > 0)
57
- ret += `${CSI}${y}B`;
58
- return ret;
59
- },
60
- up: (count = 1) => `${CSI}${count}A`,
61
- down: (count = 1) => `${CSI}${count}B`,
62
- forward: (count = 1) => `${CSI}${count}C`,
63
- backward: (count = 1) => `${CSI}${count}D`,
64
- nextLine: (count = 1) => `${CSI}E`.repeat(count),
65
- prevLine: (count = 1) => `${CSI}F`.repeat(count),
66
- left: `${CSI}G`,
67
- hide: `${CSI}?25l`,
68
- show: `${CSI}?25h`,
69
- save: `${ESC}7`,
70
- restore: `${ESC}8`
71
- };
72
- var scroll = {
73
- up: (count = 1) => `${CSI}S`.repeat(count),
74
- down: (count = 1) => `${CSI}T`.repeat(count)
75
- };
76
- var erase = {
77
- screen: `${CSI}2J`,
78
- up: (count = 1) => `${CSI}1J`.repeat(count),
79
- down: (count = 1) => `${CSI}J`.repeat(count),
80
- line: `${CSI}2K`,
81
- lineEnd: `${CSI}K`,
82
- lineStart: `${CSI}1K`,
83
- lines(count) {
84
- let clear = "";
85
- for (let i = 0;i < count; i++)
86
- clear += this.line + (i < count - 1 ? cursor.up() : "");
87
- if (count)
88
- clear += cursor.left;
89
- return clear;
90
- }
91
- };
92
- module.exports = { cursor, scroll, erase, beep };
93
- });
94
3
 
95
4
  // node_modules/@clack/core/dist/index.mjs
96
5
  import { styleText } from "node:util";
97
6
  import { stdout, stdin } from "node:process";
98
- import * as l from "node:readline";
99
- import l__default from "node:readline";
7
+ import * as f$1 from "node:readline";
8
+ import f__default from "node:readline";
100
9
 
101
10
  // node_modules/fast-string-truncated-width/dist/utils.js
102
11
  var getCodePointsLength = (() => {
@@ -439,14 +348,66 @@ function wrapAnsi(string, columns, options) {
439
348
  `);
440
349
  }
441
350
 
351
+ // node_modules/sisteransi/dist/index.js
352
+ var ESC2 = "\x1B";
353
+ var CSI2 = `${ESC2}[`;
354
+ var cursor = {
355
+ to(x, y) {
356
+ if (!y)
357
+ return `${CSI2}${x + 1}G`;
358
+ return `${CSI2}${y + 1};${x + 1}H`;
359
+ },
360
+ move(x, y) {
361
+ let ret = "";
362
+ if (x < 0)
363
+ ret += `${CSI2}${-x}D`;
364
+ else if (x > 0)
365
+ ret += `${CSI2}${x}C`;
366
+ if (y < 0)
367
+ ret += `${CSI2}${-y}A`;
368
+ else if (y > 0)
369
+ ret += `${CSI2}${y}B`;
370
+ return ret;
371
+ },
372
+ up: (count = 1) => `${CSI2}${count}A`,
373
+ down: (count = 1) => `${CSI2}${count}B`,
374
+ forward: (count = 1) => `${CSI2}${count}C`,
375
+ backward: (count = 1) => `${CSI2}${count}D`,
376
+ nextLine: (count = 1) => `${CSI2}E`.repeat(count),
377
+ prevLine: (count = 1) => `${CSI2}F`.repeat(count),
378
+ left: `${CSI2}G`,
379
+ hide: `${CSI2}?25l`,
380
+ show: `${CSI2}?25h`,
381
+ save: `${ESC2}7`,
382
+ restore: `${ESC2}8`
383
+ };
384
+ var erase = {
385
+ screen: `${CSI2}2J`,
386
+ up: (count = 1) => `${CSI2}1J`.repeat(count),
387
+ down: (count = 1) => `${CSI2}J`.repeat(count),
388
+ line: `${CSI2}2K`,
389
+ lineEnd: `${CSI2}K`,
390
+ lineStart: `${CSI2}1K`,
391
+ lines(count) {
392
+ let clear = "";
393
+ for (let i = 0;i < count; i++)
394
+ clear += this.line + (i < count - 1 ? cursor.up() : "");
395
+ if (count)
396
+ clear += cursor.left;
397
+ return clear;
398
+ }
399
+ };
400
+ var clear = {
401
+ screen: `${ESC2}c`
402
+ };
403
+
442
404
  // node_modules/@clack/core/dist/index.mjs
443
- var import_sisteransi = __toESM(require_src(), 1);
444
405
  import { ReadStream } from "node:tty";
445
- function findCursor(s, o, l) {
406
+ function findCursor(o, s, l) {
446
407
  if (!l.some((r) => !r.disabled))
447
- return s;
448
- const t = s + o, n = Math.max(l.length - 1, 0), e = t < 0 ? n : t > n ? 0 : t;
449
- return l[e]?.disabled ? findCursor(e, o < 0 ? -1 : 1, l) : e;
408
+ return o;
409
+ const t = o + s, n = Math.max(l.length - 1, 0), e = t < 0 ? n : t > n ? 0 : t;
410
+ return l[e]?.disabled ? findCursor(e, s < 0 ? -1 : 1, l) : e;
450
411
  }
451
412
  var a$1 = ["up", "down", "left", "right", "space", "enter", "cancel"];
452
413
  var t = [
@@ -536,43 +497,43 @@ function block({
536
497
  overwrite: o = true,
537
498
  hideCursor: n = true
538
499
  } = {}) {
539
- const s = l.createInterface({
500
+ const s = f$1.createInterface({
540
501
  input: e,
541
502
  output: r,
542
503
  prompt: "",
543
504
  tabSize: 1
544
505
  });
545
- l.emitKeypressEvents(e, s), e instanceof ReadStream && e.isTTY && e.setRawMode(true);
546
- const t = (f, { name: a, sequence: w }) => {
547
- const c = String(f);
548
- if (isActionKey([c, a, w], "cancel")) {
549
- n && r.write(import_sisteransi.cursor.show), process.exit(0);
506
+ f$1.emitKeypressEvents(e, s), e instanceof ReadStream && e.isTTY && e.setRawMode(true);
507
+ const t = (l, { name: i, sequence: p }) => {
508
+ const w = String(l);
509
+ if (isActionKey([w, i, p], "cancel")) {
510
+ n && r.write(cursor.show), process.exit(0);
550
511
  return;
551
512
  }
552
513
  if (!o)
553
514
  return;
554
- const i = a === "return" ? 0 : -1, m = a === "return" ? -1 : 0;
555
- l.moveCursor(r, i, m, () => {
556
- l.clearLine(r, 1, () => {
515
+ const c = i === "return" ? 0 : -1, a = i === "return" ? -1 : 0;
516
+ f$1.moveCursor(r, c, a, () => {
517
+ f$1.clearLine(r, 1, () => {
557
518
  e.once("keypress", t);
558
519
  });
559
520
  });
560
521
  };
561
- return n && r.write(import_sisteransi.cursor.hide), e.once("keypress", t), () => {
562
- e.off("keypress", t), n && r.write(import_sisteransi.cursor.show), e instanceof ReadStream && e.isTTY && !R && e.setRawMode(false), s.terminal = false, s.close();
522
+ return n && r.write(cursor.hide), e.once("keypress", t), () => {
523
+ e.off("keypress", t), n && r.write(cursor.show), e instanceof ReadStream && e.isTTY && !R && e.setRawMode(false), s.terminal = false, s.close();
563
524
  };
564
525
  }
565
526
  var getColumns = (e) => ("columns" in e) && typeof e.columns == "number" ? e.columns : 80;
566
527
  var getRows = (e) => ("rows" in e) && typeof e.rows == "number" ? e.rows : 20;
567
528
  function wrapTextWithPrefix(e, r, o, n = o, s = o, t) {
568
- const f = getColumns(e ?? stdout);
569
- return wrapAnsi(r, f - o.length, {
529
+ const l = getColumns(e ?? stdout), i = dist_default2(o);
530
+ return wrapAnsi(r, l - i, {
570
531
  hard: true,
571
532
  trim: false
572
533
  }).split(`
573
- `).map((c, i, m) => {
574
- const d = t ? t(c, i) : c;
575
- return i === 0 ? `${n}${d}` : i === m.length - 1 ? `${s}${d}` : `${o}${d}`;
534
+ `).map((c, a, g) => {
535
+ const m = t ? t(c, a) : c;
536
+ return a === 0 ? `${n}${m}` : a === g.length - 1 ? `${s}${m}` : `${o}${m}`;
576
537
  }).join(`
577
538
  `);
578
539
  }
@@ -583,8 +544,7 @@ function runValidation(e, a) {
583
544
  }
584
545
  return e(a);
585
546
  }
586
-
587
- class y {
547
+ var C$1 = class C {
588
548
  input;
589
549
  output;
590
550
  _abortSignal;
@@ -635,16 +595,16 @@ class y {
635
595
  this.state = "cancel", this.close();
636
596
  }, { once: true });
637
597
  }
638
- this.rl = l__default.createInterface({
598
+ this.rl = f__default.createInterface({
639
599
  input: this.input,
640
600
  tabSize: 2,
641
601
  prompt: "",
642
602
  escapeCodeTimeout: 50,
643
603
  terminal: true
644
604
  }), this.rl.prompt(), this.opts.initialUserInput !== undefined && this._setUserInput(this.opts.initialUserInput, true), this.input.on("keypress", this.onKeypress), setRawMode(this.input, true), this.output.on("resize", this.render), this.render(), this.once("submit", () => {
645
- this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t(this.value);
605
+ this.output.write(cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t(this.value);
646
606
  }), this.once("cancel", () => {
647
- this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t(CANCEL_SYMBOL);
607
+ this.output.write(cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t(CANCEL_SYMBOL);
648
608
  });
649
609
  });
650
610
  }
@@ -681,18 +641,18 @@ class y {
681
641
  `), setRawMode(this.input, false), this.rl?.close(), this.rl = undefined, this.emit(`${this.state}`, this.value), this.unsubscribe();
682
642
  }
683
643
  restoreCursor() {
684
- const t = wrapAnsi(this._prevFrame, process.stdout.columns, { hard: true, trim: false }).split(`
644
+ const t = wrapAnsi(this._prevFrame, getColumns(this.output), { hard: true, trim: false }).split(`
685
645
  `).length - 1;
686
- this.output.write(import_sisteransi.cursor.move(-999, t * -1));
646
+ this.output.write(cursor.move(-999, t * -1));
687
647
  }
688
648
  render() {
689
- const t = wrapAnsi(this._render(this) ?? "", process.stdout.columns, {
649
+ const t = wrapAnsi(this._render(this) ?? "", getColumns(this.output), {
690
650
  hard: true,
691
651
  trim: false
692
652
  });
693
653
  if (t !== this._prevFrame) {
694
654
  if (this.state === "initial")
695
- this.output.write(import_sisteransi.cursor.hide);
655
+ this.output.write(cursor.hide);
696
656
  else {
697
657
  const e = diffLines(this._prevFrame, t), i = getRows(this.output);
698
658
  if (this.restoreCursor(), e) {
@@ -703,33 +663,33 @@ class y {
703
663
  return;
704
664
  }
705
665
  if (e.lines.length === 1) {
706
- this.output.write(import_sisteransi.cursor.move(0, n - r)), this.output.write(import_sisteransi.erase.lines(1));
666
+ this.output.write(cursor.move(0, n - r)), this.output.write(erase.lines(1));
707
667
  const o = t.split(`
708
668
  `);
709
- this.output.write(o[n]), this._prevFrame = t, this.output.write(import_sisteransi.cursor.move(0, o.length - n - 1));
669
+ this.output.write(o[n]), this._prevFrame = t, this.output.write(cursor.move(0, o.length - n - 1));
710
670
  return;
711
671
  } else if (e.lines.length > 1) {
712
672
  if (s < r)
713
673
  n = s;
714
674
  else {
715
675
  const h = n - r;
716
- h > 0 && this.output.write(import_sisteransi.cursor.move(0, h));
676
+ h > 0 && this.output.write(cursor.move(0, h));
717
677
  }
718
- this.output.write(import_sisteransi.erase.down());
719
- const f = t.split(`
678
+ this.output.write(erase.down());
679
+ const m = t.split(`
720
680
  `).slice(n);
721
- this.output.write(f.join(`
681
+ this.output.write(m.join(`
722
682
  `)), this._prevFrame = t;
723
683
  return;
724
684
  }
725
685
  }
726
- this.output.write(import_sisteransi.erase.down());
686
+ this.output.write(erase.down());
727
687
  }
728
688
  this.output.write(t), this.state === "initial" && (this.state = "active"), this._prevFrame = t;
729
689
  }
730
690
  }
731
- }
732
- class r extends y {
691
+ };
692
+ class r extends C$1 {
733
693
  get cursor() {
734
694
  return this.value ? 0 : 1;
735
695
  }
@@ -740,13 +700,13 @@ class r extends y {
740
700
  super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
741
701
  this.value = this._value;
742
702
  }), this.on("confirm", (i) => {
743
- this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = i, this.state = "submit", this.close();
703
+ this.output.write(cursor.move(0, -1)), this.value = i, this.state = "submit", this.close();
744
704
  }), this.on("cursor", () => {
745
705
  this.value = !this.value;
746
706
  });
747
707
  }
748
708
  }
749
- class a extends y {
709
+ class a extends C$1 {
750
710
  options;
751
711
  cursor = 0;
752
712
  get _value() {
@@ -774,8 +734,8 @@ class a extends y {
774
734
  constructor(e) {
775
735
  super(e, false), this.options = e.options, this.value = [...e.initialValues ?? []];
776
736
  const i = Math.max(this.options.findIndex(({ value: t }) => t === e.cursorAt), 0);
777
- this.cursor = this.options[i]?.disabled ? findCursor(i, 1, this.options) : i, this.on("key", (t, l) => {
778
- l.name === "a" && this.toggleAll(), l.name === "i" && this.toggleInvert();
737
+ this.cursor = this.options[i]?.disabled ? findCursor(i, 1, this.options) : i, this.on("key", (t, s) => {
738
+ s.name === "a" && this.toggleAll(), s.name === "i" && this.toggleInvert(), s.name === undefined && t === " " && this.toggleValue();
779
739
  }), this.on("cursor", (t) => {
780
740
  switch (t) {
781
741
  case "left":
@@ -793,7 +753,8 @@ class a extends y {
793
753
  });
794
754
  }
795
755
  }
796
- var u$1 = class u extends y {
756
+
757
+ class u extends C$1 {
797
758
  _mask = "•";
798
759
  get cursor() {
799
760
  return this._cursor;
@@ -820,8 +781,8 @@ var u$1 = class u extends y {
820
781
  this.value === undefined && (this.value = "");
821
782
  });
822
783
  }
823
- };
824
- var n$1 = class n extends y {
784
+ }
785
+ var n$1 = class n extends C$1 {
825
786
  options;
826
787
  cursor = 0;
827
788
  get _selectedValue() {
@@ -849,7 +810,7 @@ var n$1 = class n extends y {
849
810
  });
850
811
  }
851
812
  };
852
- class n extends y {
813
+ class n extends C$1 {
853
814
  get userInputWithCursor() {
854
815
  if (this.state === "submit")
855
816
  return this.userInput;
@@ -877,7 +838,6 @@ class n extends y {
877
838
  // node_modules/@clack/prompts/dist/index.mjs
878
839
  import { styleText as styleText2, stripVTControlCharacters } from "node:util";
879
840
  import process$1 from "node:process";
880
- var import_sisteransi2 = __toESM(require_src(), 1);
881
841
  function isUnicodeSupported() {
882
842
  if (process$1.platform !== "win32") {
883
843
  return process$1.env.TERM !== "linux";
@@ -886,8 +846,8 @@ function isUnicodeSupported() {
886
846
  }
887
847
  var unicode = isUnicodeSupported();
888
848
  var isCI = () => process.env.CI === "true";
889
- var isTTY = (o) => o.isTTY === true;
890
- var unicodeOr = (o, e) => unicode ? o : e;
849
+ var isTTY = (e) => e.isTTY === true;
850
+ var unicodeOr = (e, o) => unicode ? e : o;
891
851
  var S_STEP_ACTIVE = unicodeOr("◆", "*");
892
852
  var S_STEP_CANCEL = unicodeOr("■", "x");
893
853
  var S_STEP_ERROR = unicodeOr("▲", "x");
@@ -913,8 +873,8 @@ var S_INFO = unicodeOr("●", "•");
913
873
  var S_SUCCESS = unicodeOr("◆", "*");
914
874
  var S_WARN = unicodeOr("▲", "!");
915
875
  var S_ERROR = unicodeOr("■", "x");
916
- var symbol = (o) => {
917
- switch (o) {
876
+ var symbol = (e) => {
877
+ switch (e) {
918
878
  case "initial":
919
879
  case "active":
920
880
  return styleText2("cyan", S_STEP_ACTIVE);
@@ -1028,7 +988,7 @@ ${styleText2("gray", S_BAR_END)} ` : "";
1028
988
 
1029
989
  `);
1030
990
  };
1031
- var password = (e) => new u$1({
991
+ var password = (e) => new u({
1032
992
  validate: e.validate,
1033
993
  mask: e.mask ?? S_PASSWORD_MASK,
1034
994
  signal: e.signal,
@@ -1064,85 +1024,103 @@ ${n}
1064
1024
  }
1065
1025
  }
1066
1026
  }).prompt();
1067
- var W = (l) => styleText2("magenta", l);
1027
+ var Y = 30000;
1028
+ var Z = (c) => styleText2("magenta", c);
1068
1029
  var spinner = ({
1069
- indicator: l = "dots",
1070
- onCancel: h,
1030
+ indicator: c = "dots",
1031
+ onCancel: b,
1071
1032
  output: n = process.stdout,
1072
- cancelMessage: G,
1073
- errorMessage: O,
1074
- frames: E = unicode ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"],
1075
- delay: F = unicode ? 80 : 120,
1076
- signal: m,
1077
- ...I
1033
+ cancelMessage: _,
1034
+ errorMessage: C,
1035
+ frames: M = unicode ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"],
1036
+ delay: O = unicode ? 80 : 120,
1037
+ signal: g,
1038
+ ...E
1078
1039
  } = {}) => {
1079
- const u = isCI();
1080
- let M, T, d = false, S = false, s = "", p, w = performance.now();
1081
- const x = getColumns(n), k = I?.styleFrame ?? W, g = (e) => {
1082
- const r = e > 1 ? O ?? settings.messages.error : G ?? settings.messages.cancel;
1083
- S = e === 1, d && (a(r, e), S && typeof h == "function" && h());
1084
- }, f = () => g(2), i = () => g(1), A = () => {
1085
- process.on("uncaughtExceptionMonitor", f), process.on("unhandledRejection", f), process.on("SIGINT", i), process.on("SIGTERM", i), process.on("exit", g), m && m.addEventListener("abort", i);
1086
- }, H = () => {
1087
- process.removeListener("uncaughtExceptionMonitor", f), process.removeListener("unhandledRejection", f), process.removeListener("SIGINT", i), process.removeListener("SIGTERM", i), process.removeListener("exit", g), m && m.removeEventListener("abort", i);
1088
- }, y = () => {
1089
- if (p === undefined)
1040
+ const u = isCI(), h = isAccessible(E.accessible);
1041
+ let x, I, p = false, l = false, r = "", v, w = performance.now();
1042
+ const y = getColumns(n), F = E?.styleFrame ?? Z, S = (e) => {
1043
+ const s = e > 1 ? C ?? settings.messages.error : _ ?? settings.messages.cancel;
1044
+ l = e === 1, p && (m(s, e), l && typeof b == "function" && b());
1045
+ }, $ = () => S(2), a = () => S(1), H = () => {
1046
+ process.on("uncaughtExceptionMonitor", $), process.on("unhandledRejection", $), process.on("SIGINT", a), process.on("SIGTERM", a), process.on("exit", S), g && g.addEventListener("abort", a);
1047
+ }, B = () => {
1048
+ process.removeListener("uncaughtExceptionMonitor", $), process.removeListener("unhandledRejection", $), process.removeListener("SIGINT", a), process.removeListener("SIGTERM", a), process.removeListener("exit", S), g && g.removeEventListener("abort", a);
1049
+ }, R = () => {
1050
+ if (v === undefined)
1090
1051
  return;
1091
1052
  u && n.write(`
1092
1053
  `);
1093
- const r = wrapAnsi(p, x, {
1054
+ const s = wrapAnsi(v, y, {
1094
1055
  hard: true,
1095
1056
  trim: false
1096
1057
  }).split(`
1097
1058
  `);
1098
- r.length > 1 && n.write(import_sisteransi2.cursor.up(r.length - 1)), n.write(import_sisteransi2.cursor.to(0)), n.write(import_sisteransi2.erase.down());
1099
- }, C = (e) => e.replace(/\.+$/, ""), _ = (e) => {
1100
- const r = (performance.now() - e) / 1000, t = Math.floor(r / 60), o = Math.floor(r % 60);
1101
- return t > 0 ? `[${t}m ${o}s]` : `[${o}s]`;
1102
- }, N = I.withGuide ?? settings.withGuide, P = (e = "") => {
1103
- d = true, M = block({ output: n }), s = C(e), w = performance.now(), N && n.write(`${styleText2("gray", S_BAR)}
1059
+ s.length > 1 && n.write(cursor.up(s.length - 1)), n.write(cursor.to(0)), n.write(erase.down());
1060
+ }, L = (e) => e.replace(/\.+$/, ""), T = (e) => {
1061
+ const s = (performance.now() - e) / 1000, i = Math.floor(s / 60), t = Math.floor(s % 60);
1062
+ return i > 0 ? `[${i}m ${t}s]` : `[${t}s]`;
1063
+ }, N = E.withGuide ?? settings.withGuide, P = (e = "") => {
1064
+ if (p = true, l = false, r = L(e), w = performance.now(), H(), h) {
1065
+ r !== "" && n.write(`${r}
1066
+ `), I = setInterval(() => {
1067
+ n.write(r === "" ? `still working
1068
+ ` : `still working: ${r}
1104
1069
  `);
1105
- let r = 0, t = 0;
1106
- A(), T = setInterval(() => {
1107
- if (u && s === p)
1070
+ }, Y);
1071
+ return;
1072
+ }
1073
+ x = block({ output: n }), N && n.write(`${styleText2("gray", S_BAR)}
1074
+ `);
1075
+ let s = 0, i = 0;
1076
+ I = setInterval(() => {
1077
+ if (u && r === v)
1108
1078
  return;
1109
- y(), p = s;
1110
- const o = k(E[r]);
1111
- let v;
1079
+ R(), v = r;
1080
+ const t = F(M[s]);
1081
+ let o;
1112
1082
  if (u)
1113
- v = `${o} ${s}...`;
1114
- else if (l === "timer")
1115
- v = `${o} ${s} ${_(w)}`;
1083
+ o = `${t} ${r}...`;
1084
+ else if (c === "timer")
1085
+ o = `${t} ${r} ${T(w)}`;
1116
1086
  else {
1117
- const B = ".".repeat(Math.floor(t)).slice(0, 3);
1118
- v = `${o} ${s}${B}`;
1087
+ const j = ".".repeat(Math.floor(i)).slice(0, 3);
1088
+ o = `${t} ${r}${j}`;
1119
1089
  }
1120
- const j = wrapAnsi(v, x, {
1090
+ const D = wrapAnsi(o, y, {
1121
1091
  hard: true,
1122
1092
  trim: false
1123
1093
  });
1124
- n.write(j), r = r + 1 < E.length ? r + 1 : 0, t = t < 4 ? t + 0.125 : 0;
1125
- }, F);
1126
- }, a = (e = "", r = 0, t = false) => {
1127
- if (!d)
1128
- return;
1129
- d = false, clearInterval(T), y();
1130
- const o = r === 0 ? styleText2("green", S_STEP_SUBMIT) : r === 1 ? styleText2("red", S_STEP_CANCEL) : styleText2("red", S_STEP_ERROR);
1131
- s = e ?? s, t || (l === "timer" ? n.write(`${o} ${s} ${_(w)}
1132
- `) : n.write(`${o} ${s}
1133
- `)), H(), M();
1094
+ n.write(D), s = s + 1 < M.length ? s + 1 : 0, i = i < 4 ? i + 0.125 : 0;
1095
+ }, O);
1096
+ }, m = (e = "", s = 0, i = false) => {
1097
+ if (p) {
1098
+ if (p = false, l = s === 1, clearInterval(I), h || R(), r = e ?? r, !i)
1099
+ if (h) {
1100
+ const t = s === 1 ? _ ?? settings.messages.cancel : s === 2 ? C ?? settings.messages.error : "Done", o = r || t;
1101
+ c === "timer" ? n.write(`${o} ${T(w)}
1102
+ `) : n.write(`${o}
1103
+ `);
1104
+ } else {
1105
+ const t = s === 0 ? styleText2("green", S_STEP_SUBMIT) : s === 1 ? styleText2("red", S_STEP_CANCEL) : styleText2("red", S_STEP_ERROR);
1106
+ c === "timer" ? n.write(`${t} ${r} ${T(w)}
1107
+ `) : n.write(`${t} ${r}
1108
+ `);
1109
+ }
1110
+ B(), x?.();
1111
+ }
1134
1112
  };
1135
1113
  return {
1136
1114
  start: P,
1137
- stop: (e = "") => a(e, 0),
1115
+ stop: (e = "") => m(e, 0),
1138
1116
  message: (e = "") => {
1139
- s = C(e ?? s);
1117
+ r = L(e ?? r);
1140
1118
  },
1141
- cancel: (e = "") => a(e, 1),
1142
- error: (e = "") => a(e, 2),
1143
- clear: () => a("", 0, true),
1119
+ cancel: (e = "") => m(e, 1),
1120
+ error: (e = "") => m(e, 2),
1121
+ clear: () => m("", 0, true),
1144
1122
  get isCancelled() {
1145
- return S;
1123
+ return l;
1146
1124
  }
1147
1125
  };
1148
1126
  };
@@ -1187,7 +1165,9 @@ function progress({
1187
1165
  error: r.error,
1188
1166
  clear: r.clear,
1189
1167
  advance: g,
1190
- isCancelled: r.isCancelled,
1168
+ get isCancelled() {
1169
+ return r.isCancelled;
1170
+ },
1191
1171
  message: (t) => g(0, t)
1192
1172
  };
1193
1173
  }
@@ -2081,7 +2061,6 @@ function normalizeSourceVersion(value) {
2081
2061
  // node_modules/@clack/prompts/dist/index.mjs
2082
2062
  import { styleText as styleText3, stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
2083
2063
  import process$12 from "node:process";
2084
- var import_sisteransi3 = __toESM(require_src(), 1);
2085
2064
  function isUnicodeSupported2() {
2086
2065
  if (process$12.platform !== "win32") {
2087
2066
  return process$12.env.TERM !== "linux";
@@ -2090,7 +2069,7 @@ function isUnicodeSupported2() {
2090
2069
  }
2091
2070
  var unicode2 = isUnicodeSupported2();
2092
2071
  var isCI2 = () => process.env.CI === "true";
2093
- var unicodeOr2 = (o, e) => unicode2 ? o : e;
2072
+ var unicodeOr2 = (e, o) => unicode2 ? e : o;
2094
2073
  var S_STEP_ACTIVE2 = unicodeOr2("◆", "*");
2095
2074
  var S_STEP_CANCEL2 = unicodeOr2("■", "x");
2096
2075
  var S_STEP_ERROR2 = unicodeOr2("▲", "x");
@@ -2116,8 +2095,8 @@ var S_INFO2 = unicodeOr2("●", "•");
2116
2095
  var S_SUCCESS2 = unicodeOr2("◆", "*");
2117
2096
  var S_WARN2 = unicodeOr2("▲", "!");
2118
2097
  var S_ERROR2 = unicodeOr2("■", "x");
2119
- var symbol2 = (o) => {
2120
- switch (o) {
2098
+ var symbol2 = (e) => {
2099
+ switch (e) {
2121
2100
  case "initial":
2122
2101
  case "active":
2123
2102
  return styleText3("cyan", S_STEP_ACTIVE2);
@@ -2131,8 +2110,8 @@ var symbol2 = (o) => {
2131
2110
  return styleText3("dim", S_STEP_ACTIVE2);
2132
2111
  }
2133
2112
  };
2134
- var symbolBar = (o) => {
2135
- switch (o) {
2113
+ var symbolBar = (e) => {
2114
+ switch (e) {
2136
2115
  case "initial":
2137
2116
  case "active":
2138
2117
  return styleText3("cyan", S_BAR2);
@@ -2144,11 +2123,12 @@ var symbolBar = (o) => {
2144
2123
  return styleText3("green", S_BAR2);
2145
2124
  }
2146
2125
  };
2147
- function formatInstructionFooter(o, e) {
2148
- const r = [`${e ? `${styleText3("cyan", S_BAR2)} ` : ""}${o.join(" • ")}`];
2149
- return e && r.push(styleText3("cyan", S_BAR_END2)), r;
2126
+ function formatInstructionFooter(e, o, r) {
2127
+ const s = r ? `${styleText3("cyan", S_BAR2)} ` : "", n = wrapTextWithPrefix(e, o.join(" • "), s).split(`
2128
+ `);
2129
+ return r && n.push(styleText3("cyan", S_BAR_END2)), n;
2150
2130
  }
2151
- var I = (l, e, w, p, b, C = false) => {
2131
+ var E$1 = (l, e, w, p, b, C = false) => {
2152
2132
  let r = e, O = 0;
2153
2133
  if (C)
2154
2134
  for (let i = p - 1;i >= w; i--) {
@@ -2177,12 +2157,12 @@ var limitOptions = ({
2177
2157
  let f = 0;
2178
2158
  l >= a - 3 && (f = Math.max(Math.min(l - a + 3, e.length - a), 0));
2179
2159
  let d = a < e.length && f > 0, c = a < e.length && f + a < e.length;
2180
- const W = Math.min(f + a, e.length), s = [];
2160
+ const I = Math.min(f + a, e.length), s = [];
2181
2161
  let g = 0;
2182
2162
  d && g++, c && g++;
2183
- const T = f + (d ? 1 : 0), y = W - (c ? 1 : 0);
2184
- for (let t = T;t < y; t++) {
2185
- const n = e[t], o = n ? w(n, t === l) : "", h = wrapAnsi(o, i, {
2163
+ const T = f + (d ? 1 : 0), W = I - (c ? 1 : 0);
2164
+ for (let t = T;t < W; t++) {
2165
+ const n = e[t], o = n !== undefined ? w(n, t === l) : "", h = wrapAnsi(o, i, {
2186
2166
  hard: true,
2187
2167
  trim: false
2188
2168
  }).split(`
@@ -2193,8 +2173,8 @@ var limitOptions = ({
2193
2173
  let t = 0, n = 0, o = g;
2194
2174
  const h = l - T;
2195
2175
  let u = v;
2196
- const L = () => I(s, o, 0, h, u), E = () => I(s, o, h + 1, s.length, u, true);
2197
- d ? ({ lineCount: o, removals: t } = L(), o > u && (c || (u -= 1), { lineCount: o, removals: n } = E())) : (c || (u -= 1), { lineCount: o, removals: n } = E(), o > u && (u -= 1, { lineCount: o, removals: t } = L())), t > 0 && (d = true, s.splice(0, t)), n > 0 && (c = true, s.splice(s.length - n, n));
2176
+ const L = () => E$1(s, o, 0, h, u), y = () => E$1(s, o, h + 1, s.length, u, true);
2177
+ d ? ({ lineCount: o, removals: t } = L(), o > u && (c || (u -= 1), { lineCount: o, removals: n } = y())) : (c || (u -= 1), { lineCount: o, removals: n } = y(), o > u && (u -= 1, { lineCount: o, removals: t } = L())), t > 0 && (d = true, s.splice(0, t)), n > 0 && (c = true, s.splice(s.length - n, n));
2198
2178
  }
2199
2179
  const x = [];
2200
2180
  d && x.push(M);
@@ -2243,82 +2223,82 @@ var MULTISELECT_INSTRUCTIONS2 = [
2243
2223
  `${styleText3("dim", "Space:")} select`,
2244
2224
  `${styleText3("dim", "Enter:")} confirm`
2245
2225
  ];
2246
- var m = (i, u) => i.split(`
2226
+ var m = (n, u) => n.split(`
2247
2227
  `).map((d) => u(d)).join(`
2248
2228
  `);
2249
- var multiselect = (i) => {
2229
+ var multiselect = (n) => {
2250
2230
  const u = (t, a) => {
2251
2231
  const r = t.label ?? String(t.value);
2252
2232
  return a === "disabled" ? `${styleText3("gray", S_CHECKBOX_INACTIVE2)} ${m(r, (o) => styleText3(["strikethrough", "gray"], o))}${t.hint ? ` ${styleText3("dim", `(${t.hint ?? "disabled"})`)}` : ""}` : a === "active" ? `${styleText3("cyan", S_CHECKBOX_ACTIVE2)} ${r}${t.hint ? ` ${styleText3("dim", `(${t.hint})`)}` : ""}` : a === "selected" ? `${styleText3("green", S_CHECKBOX_SELECTED2)} ${m(r, (o) => styleText3("dim", o))}${t.hint ? ` ${styleText3("dim", `(${t.hint})`)}` : ""}` : a === "cancelled" ? `${m(r, (o) => styleText3(["strikethrough", "dim"], o))}` : a === "active-selected" ? `${styleText3("green", S_CHECKBOX_SELECTED2)} ${r}${t.hint ? ` ${styleText3("dim", `(${t.hint})`)}` : ""}` : a === "submitted" ? `${m(r, (o) => styleText3("dim", o))}` : `${styleText3("dim", S_CHECKBOX_INACTIVE2)} ${m(r, (o) => styleText3("dim", o))}`;
2253
- }, d = i.required ?? true, x = i.showInstructions ?? true;
2233
+ }, d = n.required ?? true, x = n.showInstructions ?? true;
2254
2234
  return new a({
2255
- options: i.options,
2256
- signal: i.signal,
2257
- input: i.input,
2258
- output: i.output,
2259
- initialValues: i.initialValues,
2235
+ options: n.options,
2236
+ signal: n.signal,
2237
+ input: n.input,
2238
+ output: n.output,
2239
+ initialValues: n.initialValues,
2260
2240
  required: d,
2261
- cursorAt: i.cursorAt,
2241
+ cursorAt: n.cursorAt,
2262
2242
  validate(t) {
2263
2243
  if (d && (t === undefined || t.length === 0))
2264
2244
  return `Please select at least one option.
2265
2245
  ${styleText3("reset", styleText3("dim", `Press ${styleText3(["gray", "bgWhite", "inverse"], " space ")} to select, ${styleText3("gray", styleText3("bgWhite", styleText3("inverse", " enter ")))} to submit`))}`;
2266
2246
  },
2267
2247
  render() {
2268
- const t = i.withGuide ?? settings.withGuide, a = wrapTextWithPrefix(i.output, i.message, t ? `${symbolBar(this.state)} ` : "", `${symbol2(this.state)} `), r = `${t ? `${styleText3("gray", S_BAR2)}
2248
+ const t = n.withGuide ?? settings.withGuide, a = wrapTextWithPrefix(n.output, n.message, t ? `${symbolBar(this.state)} ` : "", `${symbol2(this.state)} `), r = `${t ? `${styleText3("gray", S_BAR2)}
2269
2249
  ` : ""}${a}
2270
- `, o = this.value ?? [], g = (n, l) => {
2271
- if (n.disabled)
2272
- return u(n, "disabled");
2273
- const s = o.includes(n.value);
2274
- return l && s ? u(n, "active-selected") : s ? u(n, "selected") : u(n, l ? "active" : "inactive");
2250
+ `, o = this.value ?? [], g = (i, l) => {
2251
+ if (i.disabled)
2252
+ return u(i, "disabled");
2253
+ const s = o.includes(i.value);
2254
+ return l && s ? u(i, "active-selected") : s ? u(i, "selected") : u(i, l ? "active" : "inactive");
2275
2255
  };
2276
2256
  switch (this.state) {
2277
2257
  case "submit": {
2278
- const n = this.options.filter(({ value: s }) => o.includes(s)).map((s) => u(s, "submitted")).join(styleText3("dim", ", ")) || styleText3("dim", "none"), l = wrapTextWithPrefix(i.output, n, t ? `${styleText3("gray", S_BAR2)} ` : "");
2258
+ const i = this.options.filter(({ value: s }) => o.includes(s)).map((s) => u(s, "submitted")).join(styleText3("dim", ", ")) || styleText3("dim", "none"), l = wrapTextWithPrefix(n.output, i, t ? `${styleText3("gray", S_BAR2)} ` : "");
2279
2259
  return `${r}${l}`;
2280
2260
  }
2281
2261
  case "cancel": {
2282
- const n = this.options.filter(({ value: s }) => o.includes(s)).map((s) => u(s, "cancelled")).join(styleText3("dim", ", "));
2283
- if (n.trim() === "")
2262
+ const i = this.options.filter(({ value: s }) => o.includes(s)).map((s) => u(s, "cancelled")).join(styleText3("dim", ", "));
2263
+ if (i.trim() === "")
2284
2264
  return `${r}${styleText3("gray", S_BAR2)}`;
2285
- const l = wrapTextWithPrefix(i.output, n, t ? `${styleText3("gray", S_BAR2)} ` : "");
2265
+ const l = wrapTextWithPrefix(n.output, i, t ? `${styleText3("gray", S_BAR2)} ` : "");
2286
2266
  return `${r}${l}${t ? `
2287
2267
  ${styleText3("gray", S_BAR2)}` : ""}`;
2288
2268
  }
2289
2269
  case "error": {
2290
- const n = t ? `${styleText3("yellow", S_BAR2)} ` : "", l = this.error.split(`
2270
+ const i = t ? `${styleText3("yellow", S_BAR2)} ` : "", l = this.error.split(`
2291
2271
  `).map(($, v) => v === 0 ? `${t ? `${styleText3("yellow", S_BAR_END2)} ` : ""}${styleText3("yellow", $)}` : ` ${$}`).join(`
2292
2272
  `), s = r.split(`
2293
2273
  `).length, h = l.split(`
2294
2274
  `).length + 1;
2295
- return `${r}${n}${limitOptions({
2296
- output: i.output,
2275
+ return `${r}${i}${limitOptions({
2276
+ output: n.output,
2297
2277
  options: this.options,
2298
2278
  cursor: this.cursor,
2299
- maxItems: i.maxItems,
2300
- columnPadding: n.length,
2279
+ maxItems: n.maxItems,
2280
+ columnPadding: i.length,
2301
2281
  rowPadding: s + h,
2302
2282
  style: g
2303
2283
  }).join(`
2304
- ${n}`)}
2284
+ ${i}`)}
2305
2285
  ${l}
2306
2286
  `;
2307
2287
  }
2308
2288
  default: {
2309
- const n = t ? `${styleText3("cyan", S_BAR2)} ` : "", l = r.split(`
2310
- `).length, s = x ? formatInstructionFooter(MULTISELECT_INSTRUCTIONS2, t) : t ? [styleText3("cyan", S_BAR_END2)] : [], h = s.join(`
2289
+ const i = t ? `${styleText3("cyan", S_BAR2)} ` : "", l = r.split(`
2290
+ `).length, s = x ? formatInstructionFooter(n.output, MULTISELECT_INSTRUCTIONS2, t) : t ? [styleText3("cyan", S_BAR_END2)] : [], h = s.join(`
2311
2291
  `), $ = s.length + 1;
2312
- return `${r}${n}${limitOptions({
2313
- output: i.output,
2292
+ return `${r}${i}${limitOptions({
2293
+ output: n.output,
2314
2294
  options: this.options,
2315
2295
  cursor: this.cursor,
2316
- maxItems: i.maxItems,
2317
- columnPadding: n.length,
2296
+ maxItems: n.maxItems,
2297
+ columnPadding: i.length,
2318
2298
  rowPadding: l + $,
2319
2299
  style: g
2320
2300
  }).join(`
2321
- ${n}`)}
2301
+ ${i}`)}
2322
2302
  ${h}
2323
2303
  `;
2324
2304
  }
@@ -2386,85 +2366,103 @@ ${styleText3("gray", S_BAR_END2)} ` : "";
2386
2366
 
2387
2367
  `);
2388
2368
  };
2389
- var W2 = (l) => styleText3("magenta", l);
2369
+ var Y2 = 30000;
2370
+ var Z2 = (c) => styleText3("magenta", c);
2390
2371
  var spinner2 = ({
2391
- indicator: l = "dots",
2392
- onCancel: h,
2372
+ indicator: c = "dots",
2373
+ onCancel: b,
2393
2374
  output: n = process.stdout,
2394
- cancelMessage: G,
2395
- errorMessage: O,
2396
- frames: E = unicode2 ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"],
2397
- delay: F = unicode2 ? 80 : 120,
2398
- signal: m,
2399
- ...I
2375
+ cancelMessage: _,
2376
+ errorMessage: C,
2377
+ frames: M = unicode2 ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"],
2378
+ delay: O = unicode2 ? 80 : 120,
2379
+ signal: g,
2380
+ ...E
2400
2381
  } = {}) => {
2401
- const u = isCI2();
2402
- let M, T, d = false, S = false, s = "", p, w = performance.now();
2403
- const x = getColumns(n), k = I?.styleFrame ?? W2, g = (e) => {
2404
- const r = e > 1 ? O ?? settings.messages.error : G ?? settings.messages.cancel;
2405
- S = e === 1, d && (a(r, e), S && typeof h == "function" && h());
2406
- }, f = () => g(2), i = () => g(1), A = () => {
2407
- process.on("uncaughtExceptionMonitor", f), process.on("unhandledRejection", f), process.on("SIGINT", i), process.on("SIGTERM", i), process.on("exit", g), m && m.addEventListener("abort", i);
2408
- }, H = () => {
2409
- process.removeListener("uncaughtExceptionMonitor", f), process.removeListener("unhandledRejection", f), process.removeListener("SIGINT", i), process.removeListener("SIGTERM", i), process.removeListener("exit", g), m && m.removeEventListener("abort", i);
2410
- }, y = () => {
2411
- if (p === undefined)
2382
+ const u = isCI2(), h = isAccessible(E.accessible);
2383
+ let x, I, p = false, l = false, r = "", v, w = performance.now();
2384
+ const y = getColumns(n), F = E?.styleFrame ?? Z2, S = (e) => {
2385
+ const s = e > 1 ? C ?? settings.messages.error : _ ?? settings.messages.cancel;
2386
+ l = e === 1, p && (m(s, e), l && typeof b == "function" && b());
2387
+ }, $ = () => S(2), a = () => S(1), H = () => {
2388
+ process.on("uncaughtExceptionMonitor", $), process.on("unhandledRejection", $), process.on("SIGINT", a), process.on("SIGTERM", a), process.on("exit", S), g && g.addEventListener("abort", a);
2389
+ }, B = () => {
2390
+ process.removeListener("uncaughtExceptionMonitor", $), process.removeListener("unhandledRejection", $), process.removeListener("SIGINT", a), process.removeListener("SIGTERM", a), process.removeListener("exit", S), g && g.removeEventListener("abort", a);
2391
+ }, R = () => {
2392
+ if (v === undefined)
2412
2393
  return;
2413
2394
  u && n.write(`
2414
2395
  `);
2415
- const r = wrapAnsi(p, x, {
2396
+ const s = wrapAnsi(v, y, {
2416
2397
  hard: true,
2417
2398
  trim: false
2418
2399
  }).split(`
2419
2400
  `);
2420
- r.length > 1 && n.write(import_sisteransi3.cursor.up(r.length - 1)), n.write(import_sisteransi3.cursor.to(0)), n.write(import_sisteransi3.erase.down());
2421
- }, C = (e) => e.replace(/\.+$/, ""), _ = (e) => {
2422
- const r = (performance.now() - e) / 1000, t = Math.floor(r / 60), o = Math.floor(r % 60);
2423
- return t > 0 ? `[${t}m ${o}s]` : `[${o}s]`;
2424
- }, N = I.withGuide ?? settings.withGuide, P = (e = "") => {
2425
- d = true, M = block({ output: n }), s = C(e), w = performance.now(), N && n.write(`${styleText3("gray", S_BAR2)}
2401
+ s.length > 1 && n.write(cursor.up(s.length - 1)), n.write(cursor.to(0)), n.write(erase.down());
2402
+ }, L = (e) => e.replace(/\.+$/, ""), T = (e) => {
2403
+ const s = (performance.now() - e) / 1000, i = Math.floor(s / 60), t = Math.floor(s % 60);
2404
+ return i > 0 ? `[${i}m ${t}s]` : `[${t}s]`;
2405
+ }, N = E.withGuide ?? settings.withGuide, P = (e = "") => {
2406
+ if (p = true, l = false, r = L(e), w = performance.now(), H(), h) {
2407
+ r !== "" && n.write(`${r}
2408
+ `), I = setInterval(() => {
2409
+ n.write(r === "" ? `still working
2410
+ ` : `still working: ${r}
2426
2411
  `);
2427
- let r = 0, t = 0;
2428
- A(), T = setInterval(() => {
2429
- if (u && s === p)
2412
+ }, Y2);
2413
+ return;
2414
+ }
2415
+ x = block({ output: n }), N && n.write(`${styleText3("gray", S_BAR2)}
2416
+ `);
2417
+ let s = 0, i = 0;
2418
+ I = setInterval(() => {
2419
+ if (u && r === v)
2430
2420
  return;
2431
- y(), p = s;
2432
- const o = k(E[r]);
2433
- let v;
2421
+ R(), v = r;
2422
+ const t = F(M[s]);
2423
+ let o;
2434
2424
  if (u)
2435
- v = `${o} ${s}...`;
2436
- else if (l === "timer")
2437
- v = `${o} ${s} ${_(w)}`;
2425
+ o = `${t} ${r}...`;
2426
+ else if (c === "timer")
2427
+ o = `${t} ${r} ${T(w)}`;
2438
2428
  else {
2439
- const B = ".".repeat(Math.floor(t)).slice(0, 3);
2440
- v = `${o} ${s}${B}`;
2429
+ const j = ".".repeat(Math.floor(i)).slice(0, 3);
2430
+ o = `${t} ${r}${j}`;
2441
2431
  }
2442
- const j = wrapAnsi(v, x, {
2432
+ const D = wrapAnsi(o, y, {
2443
2433
  hard: true,
2444
2434
  trim: false
2445
2435
  });
2446
- n.write(j), r = r + 1 < E.length ? r + 1 : 0, t = t < 4 ? t + 0.125 : 0;
2447
- }, F);
2448
- }, a = (e = "", r = 0, t = false) => {
2449
- if (!d)
2450
- return;
2451
- d = false, clearInterval(T), y();
2452
- const o = r === 0 ? styleText3("green", S_STEP_SUBMIT2) : r === 1 ? styleText3("red", S_STEP_CANCEL2) : styleText3("red", S_STEP_ERROR2);
2453
- s = e ?? s, t || (l === "timer" ? n.write(`${o} ${s} ${_(w)}
2454
- `) : n.write(`${o} ${s}
2455
- `)), H(), M();
2436
+ n.write(D), s = s + 1 < M.length ? s + 1 : 0, i = i < 4 ? i + 0.125 : 0;
2437
+ }, O);
2438
+ }, m = (e = "", s = 0, i = false) => {
2439
+ if (p) {
2440
+ if (p = false, l = s === 1, clearInterval(I), h || R(), r = e ?? r, !i)
2441
+ if (h) {
2442
+ const t = s === 1 ? _ ?? settings.messages.cancel : s === 2 ? C ?? settings.messages.error : "Done", o = r || t;
2443
+ c === "timer" ? n.write(`${o} ${T(w)}
2444
+ `) : n.write(`${o}
2445
+ `);
2446
+ } else {
2447
+ const t = s === 0 ? styleText3("green", S_STEP_SUBMIT2) : s === 1 ? styleText3("red", S_STEP_CANCEL2) : styleText3("red", S_STEP_ERROR2);
2448
+ c === "timer" ? n.write(`${t} ${r} ${T(w)}
2449
+ `) : n.write(`${t} ${r}
2450
+ `);
2451
+ }
2452
+ B(), x?.();
2453
+ }
2456
2454
  };
2457
2455
  return {
2458
2456
  start: P,
2459
- stop: (e = "") => a(e, 0),
2457
+ stop: (e = "") => m(e, 0),
2460
2458
  message: (e = "") => {
2461
- s = C(e ?? s);
2459
+ r = L(e ?? r);
2462
2460
  },
2463
- cancel: (e = "") => a(e, 1),
2464
- error: (e = "") => a(e, 2),
2465
- clear: () => a("", 0, true),
2461
+ cancel: (e = "") => m(e, 1),
2462
+ error: (e = "") => m(e, 2),
2463
+ clear: () => m("", 0, true),
2466
2464
  get isCancelled() {
2467
- return S;
2465
+ return l;
2468
2466
  }
2469
2467
  };
2470
2468
  };
@@ -2477,7 +2475,7 @@ var SELECT_INSTRUCTIONS2 = [
2477
2475
  `${styleText3("dim", "↑/↓")} to navigate`,
2478
2476
  `${styleText3("dim", "Enter:")} confirm`
2479
2477
  ];
2480
- var c = (t, o) => t.includes(`
2478
+ var c2 = (t, o) => t.includes(`
2481
2479
  `) ? t.split(`
2482
2480
  `).map((d) => o(d)).join(`
2483
2481
  `) : o(t);
@@ -2488,15 +2486,15 @@ var select = (t) => {
2488
2486
  const s = n.label ?? String(n.value);
2489
2487
  switch (m) {
2490
2488
  case "disabled":
2491
- return `${styleText3("gray", S_RADIO_INACTIVE2)} ${c(s, (i) => styleText3("gray", i))}${n.hint ? ` ${styleText3("dim", `(${n.hint ?? "disabled"})`)}` : ""}`;
2489
+ return `${styleText3("gray", S_RADIO_INACTIVE2)} ${c2(s, (i) => styleText3("gray", i))}${n.hint ? ` ${styleText3("dim", `(${n.hint ?? "disabled"})`)}` : ""}`;
2492
2490
  case "selected":
2493
- return `${c(s, (i) => styleText3("dim", i))}`;
2491
+ return `${c2(s, (i) => styleText3("dim", i))}`;
2494
2492
  case "active":
2495
2493
  return `${styleText3("green", S_RADIO_ACTIVE2)} ${s}${n.hint ? ` ${styleText3("dim", `(${n.hint})`)}` : ""}`;
2496
2494
  case "cancelled":
2497
- return `${c(s, (i) => styleText3(["strikethrough", "dim"], i))}`;
2495
+ return `${c2(s, (i) => styleText3(["strikethrough", "dim"], i))}`;
2498
2496
  default:
2499
- return `${styleText3("dim", S_RADIO_INACTIVE2)} ${c(s, (i) => styleText3("dim", i))}`;
2497
+ return `${styleText3("dim", S_RADIO_INACTIVE2)} ${c2(s, (i) => styleText3("dim", i))}`;
2500
2498
  }
2501
2499
  }, d = t.showInstructions ?? true;
2502
2500
  return new n$1({
@@ -2521,7 +2519,7 @@ ${styleText3("gray", S_BAR2)}` : ""}`;
2521
2519
  }
2522
2520
  default: {
2523
2521
  const r = n ? `${styleText3("cyan", S_BAR2)} ` : "", a = u.split(`
2524
- `).length, p = d ? formatInstructionFooter(SELECT_INSTRUCTIONS2, n) : n ? [styleText3("cyan", S_BAR_END2)] : [], f = p.join(`
2522
+ `).length, p = d ? formatInstructionFooter(t.output, SELECT_INSTRUCTIONS2, n) : n ? [styleText3("cyan", S_BAR_END2)] : [], f = p.join(`
2525
2523
  `), b = p.length + 1;
2526
2524
  return `${u}${r}${limitOptions({
2527
2525
  output: t.output,
@@ -2951,24 +2949,37 @@ import { join as join3 } from "node:path";
2951
2949
  var DEPLOYMENT_ENVIRONMENT_RE = /^[a-z][a-z0-9-]{1,30}$/;
2952
2950
  var REGION_RE = /^[a-z0-9][a-z0-9-]{1,31}$/;
2953
2951
  var DOMAIN_RE = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
2954
- var deploymentClouds = ["aws", "gcp", "azure"];
2955
- var frontendTargets = ["native", "vercel", "cloudflare"];
2952
+ var deploymentClouds = ["aws", "gcp", "railway"];
2953
+ var openTofuClouds = ["aws", "gcp"];
2954
+ var frontendTargets = ["native", "cloudflare"];
2956
2955
  var storageProviders = ["r2", "s3", "gcs"];
2957
2956
  var defaultRegions = {
2958
2957
  aws: "us-east-1",
2959
- gcp: "us-central1",
2960
- azure: "eastus2"
2958
+ gcp: "us-central1"
2961
2959
  };
2962
2960
  var defaultStorageProviders = {
2963
2961
  aws: "s3",
2964
2962
  gcp: "gcs",
2965
- azure: "r2"
2963
+ railway: "r2"
2966
2964
  };
2967
2965
  var storageProvidersByCloud = {
2968
2966
  aws: ["s3", "r2"],
2969
2967
  gcp: ["gcs", "r2"],
2970
- azure: ["r2"]
2968
+ railway: ["r2"]
2969
+ };
2970
+ var frontendTargetsByCloud = {
2971
+ aws: ["native", "cloudflare"],
2972
+ gcp: ["native", "cloudflare"],
2973
+ railway: ["cloudflare"]
2974
+ };
2975
+ var cloudLabels = {
2976
+ aws: "AWS",
2977
+ gcp: "GCP",
2978
+ railway: "Railway"
2971
2979
  };
2980
+ function isOpenTofuCloud(cloud) {
2981
+ return openTofuClouds.some((candidate) => candidate === cloud);
2982
+ }
2972
2983
  function isDeploymentCloud(value) {
2973
2984
  return deploymentClouds.some((cloud) => cloud === value);
2974
2985
  }
@@ -2980,12 +2991,12 @@ function isStorageProvider(value) {
2980
2991
  }
2981
2992
  function parseDeploymentCloud(value) {
2982
2993
  if (!isDeploymentCloud(value))
2983
- throw new Error("Cloud must be aws, gcp, or azure.");
2994
+ throw new Error("Cloud must be aws, gcp, or railway.");
2984
2995
  return value;
2985
2996
  }
2986
2997
  function parseFrontendTarget(value) {
2987
2998
  if (!isFrontendTarget(value)) {
2988
- throw new Error("Frontend target must be native, vercel, or cloudflare.");
2999
+ throw new Error("Frontend target must be native or cloudflare.");
2989
3000
  }
2990
3001
  return value;
2991
3002
  }
@@ -3016,12 +3027,24 @@ function validateDeploymentSelection(selection) {
3016
3027
  if (!storageProvidersByCloud[selection.cloud].includes(selection.storageProvider)) {
3017
3028
  throw new Error(`${selection.storageProvider} storage is not supported for ${selection.cloud} deployments.`);
3018
3029
  }
3030
+ if (!frontendTargetsByCloud[selection.cloud].includes(selection.frontendTarget)) {
3031
+ throw new Error(`The ${selection.frontendTarget} frontend is not supported for ${selection.cloud} deployments.`);
3032
+ }
3033
+ if (isOpenTofuCloud(selection.cloud)) {
3034
+ const regionError = validateRegion(selection.region ?? "");
3035
+ if (regionError)
3036
+ throw new Error(regionError);
3037
+ } else {
3038
+ if (selection.region !== null) {
3039
+ throw new Error("Railway selects its region per service; omit the region.");
3040
+ }
3041
+ if (selection.haEnabled) {
3042
+ throw new Error("The higher-availability profile applies to AWS and GCP only.");
3043
+ }
3044
+ }
3019
3045
  const environmentError = validateDeploymentEnvironment(selection.environment);
3020
3046
  if (environmentError)
3021
3047
  throw new Error(environmentError);
3022
- const regionError = validateRegion(selection.region);
3023
- if (regionError)
3024
- throw new Error(regionError);
3025
3048
  const apiError = validateDomain(selection.apiDomain);
3026
3049
  if (apiError)
3027
3050
  throw new Error(`API domain: ${apiError}`);
@@ -3053,20 +3076,86 @@ function createDeploymentConfig(projectName, selection) {
3053
3076
  ha_enabled: selection.haEnabled
3054
3077
  };
3055
3078
  }
3056
- function deploymentReadme(config) {
3057
- const identity = config.cloud === "aws" ? "`AWS_ROLE_ARN` secret" : config.cloud === "gcp" ? "`GCP_WORKLOAD_IDENTITY_PROVIDER` and `GCP_SERVICE_ACCOUNT` secrets" : "`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, and `AZURE_SUBSCRIPTION_ID` secrets";
3058
- const providerInputs = config.cloud === "aws" ? "Route 53 zone or certificate identifiers and runtime Secrets Manager ARNs" : config.cloud === "gcp" ? "GCP project, Cloud DNS zone, and runtime Secret Manager names" : "Azure DNS zone ID and runtime Key Vault secret identifiers";
3079
+ function lockSummary(config) {
3080
+ return `The deployment target is locked:
3081
+
3082
+ - backend: **${cloudLabels[config.cloud]}**;
3083
+ - storage: **${config.storage_provider}**;
3084
+ - frontend: **${config.frontend_target}**;
3085
+ - environment: **${config.environment}**.`;
3086
+ }
3087
+ function railwayReadme(config) {
3088
+ return `# Deployment setup
3089
+
3090
+ ${lockSummary(config)}
3091
+
3092
+ The production hosts are:
3093
+
3094
+ - API: \`${config.api_domain}\`;
3095
+ - frontend: \`${config.frontend_domain}\`.
3096
+
3097
+ ## Railway API and PostgreSQL
3098
+
3099
+ 1. Create a Railway project with a PostgreSQL service and an API service connected to this
3100
+ repository. Build from the repository root, because the image copies the whole workspace.
3101
+ 2. Set the API service's config-as-code path to \`apps/server/railway.toml\`. It builds
3102
+ \`apps/server/Dockerfile\`, runs \`./server migrate\` before each deploy, starts \`./server serve\`,
3103
+ and admits traffic only after \`/ready\` succeeds.
3104
+ 3. Set these API service variables in the \`${config.environment}\` Railway environment:
3105
+
3106
+ \`\`\`text
3107
+ DATABASE_URL=\${{Postgres.DATABASE_URL}}
3108
+ ENVIRONMENT=production
3109
+ TRUSTED_PROXY_PROFILE=railway
3110
+ DEPLOYMENT_CLOUD=railway
3111
+ FRONTEND_URL=https://${config.frontend_domain}
3112
+ SERVER_URL=https://${config.api_domain}
3113
+ \`\`\`
3114
+
3115
+ Add \`BETTER_AUTH_SECRET\`, \`RESEND_API_KEY\`, \`EMAIL_FROM\`, \`APP_DB_POOL_MAX\`, and
3116
+ \`AUTH_DB_POOL_MAX\`, then the Cloudflare R2 storage variables and every enabled billing,
3117
+ monitoring, and OAuth value. Railway injects \`PORT\`; leave it unset.
3118
+
3119
+ 4. Add the API host as the service's custom domain and publish the DNS records Railway reports. A
3120
+ Cloudflare-managed zone keeps them \`DNS only\`.
3121
+
3122
+ ## Cloudflare frontend
3123
+
3124
+ 1. Create a Cloudflare Workers project rooted at \`apps/web\`. It uses \`apps/web/wrangler.toml\`, which
3125
+ serves \`dist\` as static assets with single-page-application fallback.
3126
+ 2. Build with \`pnpm run build:prod\` and these variables:
3127
+
3128
+ \`\`\`text
3129
+ VITE_APP_ENV=production
3130
+ VITE_API_URL=https://${config.api_domain}
3131
+ \`\`\`
3132
+
3133
+ Add \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL\`, plus \`VITE_MARKETING_URL\` when the product has a
3134
+ public marketing site.
3135
+
3136
+ 3. Attach the frontend host as the Worker's custom domain.
3137
+
3138
+ ## Verify
3139
+
3140
+ \`\`\`sh
3141
+ API_ORIGIN=https://${config.api_domain} \\
3142
+ WEB_ORIGIN=https://${config.frontend_domain} \\
3143
+ bash scripts/verify-public-deployment.sh
3144
+ \`\`\`
3145
+
3146
+ The provider lock is deliberate. Restore the deployment directories from a fresh licensed release
3147
+ before changing providers.
3148
+ `;
3149
+ }
3150
+ function openTofuReadme(config) {
3151
+ const identity = config.cloud === "aws" ? "`AWS_ROLE_ARN` secret" : "`GCP_WORKLOAD_IDENTITY_PROVIDER` and `GCP_SERVICE_ACCOUNT` secrets";
3152
+ const providerInputs = config.cloud === "aws" ? "Route 53 zone or certificate identifiers and runtime Secrets Manager ARNs" : "GCP project, Cloud DNS zone, and runtime Secret Manager names";
3059
3153
  const storageSetup = config.storage_provider === "r2" ? `Create private staging and public Cloudflare R2 buckets, then store the required storage
3060
3154
  variables in the runtime secret container.` : `${config.storage_provider.toUpperCase()} application buckets, lifecycle policy, CORS, public access, and workload permissions are
3061
3155
  managed by the selected OpenTofu root.`;
3062
3156
  return `# Deployment setup
3063
3157
 
3064
- The deployment target is locked:
3065
-
3066
- - backend: **${config.cloud.toUpperCase()}**;
3067
- - storage: **${config.storage_provider}**;
3068
- - frontend: **${config.frontend_target}**;
3069
- - environment: **${config.environment}**.
3158
+ ${lockSummary(config)}
3070
3159
 
3071
3160
  ## Protected GitHub environment
3072
3161
 
@@ -3076,8 +3165,8 @@ Create a GitHub environment named \`${config.environment}\` and configure:
3076
3165
  - a \`BACKEND_HCL\` secret containing the selected remote-state backend configuration;
3077
3166
  - a \`TFVARS_JSON\` secret containing only:
3078
3167
  - ${providerInputs};
3079
- - \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL\`, plus \`VITE_MARKETING_URL\` when the product has a
3080
- public marketing site.
3168
+ - \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL\`, plus \`VITE_MARKETING_URL\` when the product has a public
3169
+ marketing site.
3081
3170
 
3082
3171
  ## Cloud preparation
3083
3172
 
@@ -3098,9 +3187,33 @@ The workflow creates the registry first, pushes an immutable image, provisions t
3098
3187
  traffic disabled, runs migrations, and admits traffic only after migration succeeds.
3099
3188
 
3100
3189
  The provider lock is deliberate. Restore the deployment directories from a fresh licensed release
3101
- before changing cloud providers.
3190
+ before changing providers.
3102
3191
  `;
3103
3192
  }
3193
+ function deploymentReadme(config) {
3194
+ return config.cloud === "railway" ? railwayReadme(config) : openTofuReadme(config);
3195
+ }
3196
+ function openTofuPaths(cloud) {
3197
+ return [
3198
+ `infra/bootstrap/${cloud}`,
3199
+ `infra/modules/${cloud}`,
3200
+ `infra/roots/${cloud}`,
3201
+ `scripts/deploy/release-${cloud}.sh`,
3202
+ `scripts/deploy/frontend-${cloud}.sh`
3203
+ ];
3204
+ }
3205
+ function unusedDeploymentPaths(selection) {
3206
+ const paths = selection.cloud === "railway" ? ["infra", "scripts/deploy", ".github/workflows/deploy-cloud.yml"] : [
3207
+ ...openTofuClouds.filter((cloud) => cloud !== selection.cloud).flatMap(openTofuPaths),
3208
+ "apps/server/railway.toml"
3209
+ ];
3210
+ if (selection.frontendTarget === "native")
3211
+ paths.push("apps/web/wrangler.toml");
3212
+ else if (selection.cloud !== "railway") {
3213
+ paths.push(`scripts/deploy/frontend-${selection.cloud}.sh`);
3214
+ }
3215
+ return paths;
3216
+ }
3104
3217
  async function removeIfPresent(target) {
3105
3218
  if (!await stat2(target).catch(() => {
3106
3219
  return;
@@ -3109,6 +3222,20 @@ async function removeIfPresent(target) {
3109
3222
  await rm(target, { recursive: true, force: true });
3110
3223
  return true;
3111
3224
  }
3225
+ async function removeInfrastructureScript(repositoryRoot) {
3226
+ const packagePath = join3(repositoryRoot, "package.json");
3227
+ const content = await readFile2(packagePath, "utf8").catch(() => {
3228
+ return;
3229
+ });
3230
+ if (!content)
3231
+ return;
3232
+ const rootPackage = JSON.parse(content);
3233
+ if (!rootPackage.scripts?.["verify:infra"])
3234
+ return;
3235
+ delete rootPackage.scripts["verify:infra"];
3236
+ await writeFile2(packagePath, `${JSON.stringify(rootPackage, null, 2)}
3237
+ `);
3238
+ }
3112
3239
  async function applyDeploymentSelection(repositoryRoot, projectName, selection) {
3113
3240
  const config = createDeploymentConfig(projectName, selection);
3114
3241
  const configPath = join3(repositoryRoot, "deployment/config.json");
@@ -3122,40 +3249,12 @@ async function applyDeploymentSelection(repositoryRoot, projectName, selection)
3122
3249
  `);
3123
3250
  await writeFile2(join3(repositoryRoot, "deployment/README.md"), deploymentReadme(config));
3124
3251
  const removed = [];
3125
- for (const cloud of deploymentClouds) {
3126
- if (cloud === selection.cloud)
3127
- continue;
3128
- for (const relativePath of [
3129
- `infra/bootstrap/${cloud}`,
3130
- `infra/modules/${cloud}`,
3131
- `infra/roots/${cloud}`,
3132
- `scripts/deploy/release-${cloud}.sh`,
3133
- `scripts/deploy/frontend-${cloud}.sh`
3134
- ]) {
3135
- if (await removeIfPresent(join3(repositoryRoot, relativePath)))
3136
- removed.push(relativePath);
3137
- }
3138
- }
3139
- const adapters = [
3140
- { target: "vercel", path: "apps/web/vercel.json" },
3141
- { target: "cloudflare", path: "apps/web/wrangler.toml" },
3142
- {
3143
- target: "azure-native",
3144
- path: "apps/web/public/staticwebapp.config.json"
3145
- }
3146
- ];
3147
- const selectedAdapter = selection.frontendTarget === "native" ? selection.cloud === "azure" ? "azure-native" : undefined : selection.frontendTarget;
3148
- for (const adapter of adapters) {
3149
- if (adapter.target === selectedAdapter)
3150
- continue;
3151
- if (await removeIfPresent(join3(repositoryRoot, adapter.path)))
3152
- removed.push(adapter.path);
3153
- }
3154
- if (selection.frontendTarget !== "native") {
3155
- const nativeScript = `scripts/deploy/frontend-${selection.cloud}.sh`;
3156
- if (await removeIfPresent(join3(repositoryRoot, nativeScript)))
3157
- removed.push(nativeScript);
3252
+ for (const relativePath of unusedDeploymentPaths(selection)) {
3253
+ if (await removeIfPresent(join3(repositoryRoot, relativePath)))
3254
+ removed.push(relativePath);
3158
3255
  }
3256
+ if (removed.includes("infra"))
3257
+ await removeInfrastructureScript(repositoryRoot);
3159
3258
  return removed;
3160
3259
  }
3161
3260
 
@@ -3174,37 +3273,31 @@ async function collectDeploymentSelection(defaults = {}, nonInteractive = false)
3174
3273
  if (!defaults.cloud || !defaults.apiDomain || !defaults.frontendDomain) {
3175
3274
  throw new Error("Non-interactive deployment setup requires --cloud, --api-domain, and --frontend-domain.");
3176
3275
  }
3276
+ const cloud = defaults.cloud;
3177
3277
  return {
3178
- cloud: defaults.cloud,
3179
- storageProvider: defaults.storageProvider ?? defaultStorageProviders[defaults.cloud],
3180
- frontendTarget: defaults.frontendTarget ?? "native",
3278
+ cloud,
3279
+ storageProvider: defaults.storageProvider ?? defaultStorageProviders[cloud],
3280
+ frontendTarget: defaults.frontendTarget ?? frontendTargetsByCloud[cloud][0] ?? "cloudflare",
3181
3281
  environment: defaults.environment ?? "production",
3182
- region: defaults.region ?? defaultRegions[defaults.cloud],
3282
+ region: isOpenTofuCloud(cloud) ? defaults.region ?? defaultRegions[cloud] : defaults.region ?? null,
3183
3283
  apiDomain: defaults.apiDomain,
3184
3284
  frontendDomain: defaults.frontendDomain,
3185
3285
  haEnabled: defaults.haEnabled ?? false
3186
3286
  };
3187
3287
  }
3188
3288
  const cloud = defaults.cloud ?? parseDeploymentCloud(await unwrap(await select({
3189
- message: "Backend cloud",
3289
+ message: "Backend host",
3190
3290
  options: [
3191
- { value: "aws", label: "AWS", hint: "ECS Fargate + RDS PostgreSQL" },
3192
- { value: "gcp", label: "Google Cloud", hint: "Cloud Run + Cloud SQL" },
3193
- { value: "azure", label: "Azure", hint: "Container Apps + Flexible Server" }
3291
+ { value: "aws", label: "AWS", hint: "ECS Fargate + RDS PostgreSQL, OpenTofu" },
3292
+ { value: "gcp", label: "Google Cloud", hint: "Cloud Run + Cloud SQL, OpenTofu" },
3293
+ { value: "railway", label: "Railway", hint: "Managed container + PostgreSQL" }
3194
3294
  ]
3195
3295
  })));
3196
- const frontendTarget = defaults.frontendTarget ?? parseFrontendTarget(await unwrap(await select({
3296
+ const frontendOptions = frontendTargetsByCloud[cloud];
3297
+ const frontendTarget = defaults.frontendTarget ?? (frontendOptions.length === 1 ? frontendOptions[0] ?? "cloudflare" : parseFrontendTarget(await unwrap(await select({
3197
3298
  message: "Frontend host",
3198
- options: [
3199
- { value: "native", label: "Cloud native", hint: "Managed with the backend" },
3200
- { value: "vercel", label: "Vercel SPA", hint: "Static frontend only" },
3201
- {
3202
- value: "cloudflare",
3203
- label: "Cloudflare static assets",
3204
- hint: "No API Worker"
3205
- }
3206
- ]
3207
- })));
3299
+ options: frontendOptions.map((target) => target === "native" ? { value: target, label: "Cloud native", hint: "Managed with the backend" } : { value: target, label: "Cloudflare static assets", hint: "No API Worker" })
3300
+ }))));
3208
3301
  const storageProvider = defaults.storageProvider ?? (storageProvidersByCloud[cloud].length === 1 ? storageProvidersByCloud[cloud][0] ?? defaultStorageProviders[cloud] : parseStorageProvider(await unwrap(await select({
3209
3302
  message: "Object storage",
3210
3303
  options: storageProvidersByCloud[cloud].map((provider) => ({
@@ -3219,8 +3312,8 @@ async function collectDeploymentSelection(defaults = {}, nonInteractive = false)
3219
3312
  defaultValue: "production",
3220
3313
  validate: (value) => validateDeploymentEnvironment(value || "production")
3221
3314
  }));
3222
- const region = defaults.region ?? await unwrap(await text2({
3223
- message: `${cloud.toUpperCase()} region`,
3315
+ const region = !isOpenTofuCloud(cloud) ? defaults.region ?? null : defaults.region ?? await unwrap(await text2({
3316
+ message: `${cloudLabels[cloud]} region`,
3224
3317
  placeholder: defaultRegions[cloud],
3225
3318
  defaultValue: defaultRegions[cloud],
3226
3319
  validate: (value) => validateRegion(value || defaultRegions[cloud])
@@ -3235,7 +3328,7 @@ async function collectDeploymentSelection(defaults = {}, nonInteractive = false)
3235
3328
  placeholder: "app.example.com",
3236
3329
  validate: (value) => validateDomain(value ?? "")
3237
3330
  }));
3238
- const haEnabled = defaults.haEnabled ?? await unwrap(await confirm2({
3331
+ const haEnabled = !isOpenTofuCloud(cloud) ? defaults.haEnabled ?? false : defaults.haEnabled ?? await unwrap(await confirm2({
3239
3332
  message: "Enable the higher-availability profile?",
3240
3333
  initialValue: false
3241
3334
  }));
@@ -3297,10 +3390,11 @@ async function runDeploymentConfigure(argv, productName, repositoryRoot = proces
3297
3390
  try {
3298
3391
  const selection = await collectDeploymentSelection(parsed, parsed.yes);
3299
3392
  const removed = await applyDeploymentSelection(root, rootPackage.name, selection);
3300
- log2.success(`${selection.cloud.toUpperCase()} + ${selection.storageProvider} + ${selection.frontendTarget} is locked for ${selection.environment}.`);
3393
+ log2.success(`${cloudLabels[selection.cloud]} + ${selection.storageProvider} + ${selection.frontendTarget} is locked for ${selection.environment}.`);
3301
3394
  log2.info(`${removed.length} unused deployment paths removed.`);
3302
3395
  log2.step("Next");
3303
- log2.message(`Review deployment/config.json
3396
+ log2.message(selection.cloud === "railway" ? `Review deployment/config.json
3397
+ Follow deployment/README.md for the Railway services and Cloudflare frontend` : `Review deployment/config.json
3304
3398
  Configure the protected GitHub environment
3305
3399
  Dispatch .github/workflows/deploy-cloud.yml`);
3306
3400
  outro2("Deployment configuration is ready to commit.");
@@ -3391,37 +3485,31 @@ async function collectDeploymentSelection2(defaults = {}, nonInteractive = false
3391
3485
  if (!defaults.cloud || !defaults.apiDomain || !defaults.frontendDomain) {
3392
3486
  throw new Error("Non-interactive deployment setup requires --cloud, --api-domain, and --frontend-domain.");
3393
3487
  }
3488
+ const cloud = defaults.cloud;
3394
3489
  return {
3395
- cloud: defaults.cloud,
3396
- storageProvider: defaults.storageProvider ?? defaultStorageProviders[defaults.cloud],
3397
- frontendTarget: defaults.frontendTarget ?? "native",
3490
+ cloud,
3491
+ storageProvider: defaults.storageProvider ?? defaultStorageProviders[cloud],
3492
+ frontendTarget: defaults.frontendTarget ?? frontendTargetsByCloud[cloud][0] ?? "cloudflare",
3398
3493
  environment: defaults.environment ?? "production",
3399
- region: defaults.region ?? defaultRegions[defaults.cloud],
3494
+ region: isOpenTofuCloud(cloud) ? defaults.region ?? defaultRegions[cloud] : defaults.region ?? null,
3400
3495
  apiDomain: defaults.apiDomain,
3401
3496
  frontendDomain: defaults.frontendDomain,
3402
3497
  haEnabled: defaults.haEnabled ?? false
3403
3498
  };
3404
3499
  }
3405
3500
  const cloud = defaults.cloud ?? parseDeploymentCloud(await unwrap2(await select({
3406
- message: "Backend cloud",
3501
+ message: "Backend host",
3407
3502
  options: [
3408
- { value: "aws", label: "AWS", hint: "ECS Fargate + RDS PostgreSQL" },
3409
- { value: "gcp", label: "Google Cloud", hint: "Cloud Run + Cloud SQL" },
3410
- { value: "azure", label: "Azure", hint: "Container Apps + Flexible Server" }
3503
+ { value: "aws", label: "AWS", hint: "ECS Fargate + RDS PostgreSQL, OpenTofu" },
3504
+ { value: "gcp", label: "Google Cloud", hint: "Cloud Run + Cloud SQL, OpenTofu" },
3505
+ { value: "railway", label: "Railway", hint: "Managed container + PostgreSQL" }
3411
3506
  ]
3412
3507
  })));
3413
- const frontendTarget = defaults.frontendTarget ?? parseFrontendTarget(await unwrap2(await select({
3508
+ const frontendOptions = frontendTargetsByCloud[cloud];
3509
+ const frontendTarget = defaults.frontendTarget ?? (frontendOptions.length === 1 ? frontendOptions[0] ?? "cloudflare" : parseFrontendTarget(await unwrap2(await select({
3414
3510
  message: "Frontend host",
3415
- options: [
3416
- { value: "native", label: "Cloud native", hint: "Managed with the backend" },
3417
- { value: "vercel", label: "Vercel SPA", hint: "Static frontend only" },
3418
- {
3419
- value: "cloudflare",
3420
- label: "Cloudflare static assets",
3421
- hint: "No API Worker"
3422
- }
3423
- ]
3424
- })));
3511
+ options: frontendOptions.map((target) => target === "native" ? { value: target, label: "Cloud native", hint: "Managed with the backend" } : { value: target, label: "Cloudflare static assets", hint: "No API Worker" })
3512
+ }))));
3425
3513
  const storageProvider = defaults.storageProvider ?? (storageProvidersByCloud[cloud].length === 1 ? storageProvidersByCloud[cloud][0] ?? defaultStorageProviders[cloud] : parseStorageProvider(await unwrap2(await select({
3426
3514
  message: "Object storage",
3427
3515
  options: storageProvidersByCloud[cloud].map((provider) => ({
@@ -3436,8 +3524,8 @@ async function collectDeploymentSelection2(defaults = {}, nonInteractive = false
3436
3524
  defaultValue: "production",
3437
3525
  validate: (value) => validateDeploymentEnvironment(value || "production")
3438
3526
  }));
3439
- const region = defaults.region ?? await unwrap2(await text2({
3440
- message: `${cloud.toUpperCase()} region`,
3527
+ const region = !isOpenTofuCloud(cloud) ? defaults.region ?? null : defaults.region ?? await unwrap2(await text2({
3528
+ message: `${cloudLabels[cloud]} region`,
3441
3529
  placeholder: defaultRegions[cloud],
3442
3530
  defaultValue: defaultRegions[cloud],
3443
3531
  validate: (value) => validateRegion(value || defaultRegions[cloud])
@@ -3452,7 +3540,7 @@ async function collectDeploymentSelection2(defaults = {}, nonInteractive = false
3452
3540
  placeholder: "app.example.com",
3453
3541
  validate: (value) => validateDomain(value ?? "")
3454
3542
  }));
3455
- const haEnabled = defaults.haEnabled ?? await unwrap2(await confirm2({
3543
+ const haEnabled = !isOpenTofuCloud(cloud) ? defaults.haEnabled ?? false : defaults.haEnabled ?? await unwrap2(await confirm2({
3456
3544
  message: "Enable the higher-availability profile?",
3457
3545
  initialValue: false
3458
3546
  }));
@@ -3474,14 +3562,28 @@ import { join as join5 } from "node:path";
3474
3562
  var DEPLOYMENT_ENVIRONMENT_RE2 = /^[a-z][a-z0-9-]{1,30}$/;
3475
3563
  var REGION_RE2 = /^[a-z0-9][a-z0-9-]{1,31}$/;
3476
3564
  var DOMAIN_RE2 = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
3477
- var deploymentClouds2 = ["aws", "gcp", "azure"];
3478
- var frontendTargets2 = ["native", "vercel", "cloudflare"];
3565
+ var deploymentClouds2 = ["aws", "gcp", "railway"];
3566
+ var openTofuClouds2 = ["aws", "gcp"];
3567
+ var frontendTargets2 = ["native", "cloudflare"];
3479
3568
  var storageProviders2 = ["r2", "s3", "gcs"];
3480
3569
  var storageProvidersByCloud2 = {
3481
3570
  aws: ["s3", "r2"],
3482
3571
  gcp: ["gcs", "r2"],
3483
- azure: ["r2"]
3572
+ railway: ["r2"]
3573
+ };
3574
+ var frontendTargetsByCloud2 = {
3575
+ aws: ["native", "cloudflare"],
3576
+ gcp: ["native", "cloudflare"],
3577
+ railway: ["cloudflare"]
3484
3578
  };
3579
+ var cloudLabels2 = {
3580
+ aws: "AWS",
3581
+ gcp: "GCP",
3582
+ railway: "Railway"
3583
+ };
3584
+ function isOpenTofuCloud2(cloud) {
3585
+ return openTofuClouds2.some((candidate) => candidate === cloud);
3586
+ }
3485
3587
  function isDeploymentCloud2(value) {
3486
3588
  return deploymentClouds2.some((cloud) => cloud === value);
3487
3589
  }
@@ -3493,12 +3595,12 @@ function isStorageProvider2(value) {
3493
3595
  }
3494
3596
  function parseDeploymentCloud2(value) {
3495
3597
  if (!isDeploymentCloud2(value))
3496
- throw new Error("Cloud must be aws, gcp, or azure.");
3598
+ throw new Error("Cloud must be aws, gcp, or railway.");
3497
3599
  return value;
3498
3600
  }
3499
3601
  function parseFrontendTarget2(value) {
3500
3602
  if (!isFrontendTarget2(value)) {
3501
- throw new Error("Frontend target must be native, vercel, or cloudflare.");
3603
+ throw new Error("Frontend target must be native or cloudflare.");
3502
3604
  }
3503
3605
  return value;
3504
3606
  }
@@ -3529,12 +3631,24 @@ function validateDeploymentSelection2(selection) {
3529
3631
  if (!storageProvidersByCloud2[selection.cloud].includes(selection.storageProvider)) {
3530
3632
  throw new Error(`${selection.storageProvider} storage is not supported for ${selection.cloud} deployments.`);
3531
3633
  }
3634
+ if (!frontendTargetsByCloud2[selection.cloud].includes(selection.frontendTarget)) {
3635
+ throw new Error(`The ${selection.frontendTarget} frontend is not supported for ${selection.cloud} deployments.`);
3636
+ }
3637
+ if (isOpenTofuCloud2(selection.cloud)) {
3638
+ const regionError = validateRegion2(selection.region ?? "");
3639
+ if (regionError)
3640
+ throw new Error(regionError);
3641
+ } else {
3642
+ if (selection.region !== null) {
3643
+ throw new Error("Railway selects its region per service; omit the region.");
3644
+ }
3645
+ if (selection.haEnabled) {
3646
+ throw new Error("The higher-availability profile applies to AWS and GCP only.");
3647
+ }
3648
+ }
3532
3649
  const environmentError = validateDeploymentEnvironment2(selection.environment);
3533
3650
  if (environmentError)
3534
3651
  throw new Error(environmentError);
3535
- const regionError = validateRegion2(selection.region);
3536
- if (regionError)
3537
- throw new Error(regionError);
3538
3652
  const apiError = validateDomain2(selection.apiDomain);
3539
3653
  if (apiError)
3540
3654
  throw new Error(`API domain: ${apiError}`);
@@ -3566,20 +3680,86 @@ function createDeploymentConfig2(projectName, selection) {
3566
3680
  ha_enabled: selection.haEnabled
3567
3681
  };
3568
3682
  }
3569
- function deploymentReadme2(config) {
3570
- const identity = config.cloud === "aws" ? "`AWS_ROLE_ARN` secret" : config.cloud === "gcp" ? "`GCP_WORKLOAD_IDENTITY_PROVIDER` and `GCP_SERVICE_ACCOUNT` secrets" : "`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, and `AZURE_SUBSCRIPTION_ID` secrets";
3571
- const providerInputs = config.cloud === "aws" ? "Route 53 zone or certificate identifiers and runtime Secrets Manager ARNs" : config.cloud === "gcp" ? "GCP project, Cloud DNS zone, and runtime Secret Manager names" : "Azure DNS zone ID and runtime Key Vault secret identifiers";
3683
+ function lockSummary2(config) {
3684
+ return `The deployment target is locked:
3685
+
3686
+ - backend: **${cloudLabels2[config.cloud]}**;
3687
+ - storage: **${config.storage_provider}**;
3688
+ - frontend: **${config.frontend_target}**;
3689
+ - environment: **${config.environment}**.`;
3690
+ }
3691
+ function railwayReadme2(config) {
3692
+ return `# Deployment setup
3693
+
3694
+ ${lockSummary2(config)}
3695
+
3696
+ The production hosts are:
3697
+
3698
+ - API: \`${config.api_domain}\`;
3699
+ - frontend: \`${config.frontend_domain}\`.
3700
+
3701
+ ## Railway API and PostgreSQL
3702
+
3703
+ 1. Create a Railway project with a PostgreSQL service and an API service connected to this
3704
+ repository. Build from the repository root, because the image copies the whole workspace.
3705
+ 2. Set the API service's config-as-code path to \`apps/server/railway.toml\`. It builds
3706
+ \`apps/server/Dockerfile\`, runs \`./server migrate\` before each deploy, starts \`./server serve\`,
3707
+ and admits traffic only after \`/ready\` succeeds.
3708
+ 3. Set these API service variables in the \`${config.environment}\` Railway environment:
3709
+
3710
+ \`\`\`text
3711
+ DATABASE_URL=\${{Postgres.DATABASE_URL}}
3712
+ ENVIRONMENT=production
3713
+ TRUSTED_PROXY_PROFILE=railway
3714
+ DEPLOYMENT_CLOUD=railway
3715
+ FRONTEND_URL=https://${config.frontend_domain}
3716
+ SERVER_URL=https://${config.api_domain}
3717
+ \`\`\`
3718
+
3719
+ Add \`BETTER_AUTH_SECRET\`, \`RESEND_API_KEY\`, \`EMAIL_FROM\`, \`APP_DB_POOL_MAX\`, and
3720
+ \`AUTH_DB_POOL_MAX\`, then the Cloudflare R2 storage variables and every enabled billing,
3721
+ monitoring, and OAuth value. Railway injects \`PORT\`; leave it unset.
3722
+
3723
+ 4. Add the API host as the service's custom domain and publish the DNS records Railway reports. A
3724
+ Cloudflare-managed zone keeps them \`DNS only\`.
3725
+
3726
+ ## Cloudflare frontend
3727
+
3728
+ 1. Create a Cloudflare Workers project rooted at \`apps/web\`. It uses \`apps/web/wrangler.toml\`, which
3729
+ serves \`dist\` as static assets with single-page-application fallback.
3730
+ 2. Build with \`pnpm run build:prod\` and these variables:
3731
+
3732
+ \`\`\`text
3733
+ VITE_APP_ENV=production
3734
+ VITE_API_URL=https://${config.api_domain}
3735
+ \`\`\`
3736
+
3737
+ Add \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL\`, plus \`VITE_MARKETING_URL\` when the product has a
3738
+ public marketing site.
3739
+
3740
+ 3. Attach the frontend host as the Worker's custom domain.
3741
+
3742
+ ## Verify
3743
+
3744
+ \`\`\`sh
3745
+ API_ORIGIN=https://${config.api_domain} \\
3746
+ WEB_ORIGIN=https://${config.frontend_domain} \\
3747
+ bash scripts/verify-public-deployment.sh
3748
+ \`\`\`
3749
+
3750
+ The provider lock is deliberate. Restore the deployment directories from a fresh licensed release
3751
+ before changing providers.
3752
+ `;
3753
+ }
3754
+ function openTofuReadme2(config) {
3755
+ const identity = config.cloud === "aws" ? "`AWS_ROLE_ARN` secret" : "`GCP_WORKLOAD_IDENTITY_PROVIDER` and `GCP_SERVICE_ACCOUNT` secrets";
3756
+ const providerInputs = config.cloud === "aws" ? "Route 53 zone or certificate identifiers and runtime Secrets Manager ARNs" : "GCP project, Cloud DNS zone, and runtime Secret Manager names";
3572
3757
  const storageSetup = config.storage_provider === "r2" ? `Create private staging and public Cloudflare R2 buckets, then store the required storage
3573
3758
  variables in the runtime secret container.` : `${config.storage_provider.toUpperCase()} application buckets, lifecycle policy, CORS, public access, and workload permissions are
3574
3759
  managed by the selected OpenTofu root.`;
3575
3760
  return `# Deployment setup
3576
3761
 
3577
- The deployment target is locked:
3578
-
3579
- - backend: **${config.cloud.toUpperCase()}**;
3580
- - storage: **${config.storage_provider}**;
3581
- - frontend: **${config.frontend_target}**;
3582
- - environment: **${config.environment}**.
3762
+ ${lockSummary2(config)}
3583
3763
 
3584
3764
  ## Protected GitHub environment
3585
3765
 
@@ -3589,8 +3769,8 @@ Create a GitHub environment named \`${config.environment}\` and configure:
3589
3769
  - a \`BACKEND_HCL\` secret containing the selected remote-state backend configuration;
3590
3770
  - a \`TFVARS_JSON\` secret containing only:
3591
3771
  - ${providerInputs};
3592
- - \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL\`, plus \`VITE_MARKETING_URL\` when the product has a
3593
- public marketing site.
3772
+ - \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL\`, plus \`VITE_MARKETING_URL\` when the product has a public
3773
+ marketing site.
3594
3774
 
3595
3775
  ## Cloud preparation
3596
3776
 
@@ -3611,9 +3791,33 @@ The workflow creates the registry first, pushes an immutable image, provisions t
3611
3791
  traffic disabled, runs migrations, and admits traffic only after migration succeeds.
3612
3792
 
3613
3793
  The provider lock is deliberate. Restore the deployment directories from a fresh licensed release
3614
- before changing cloud providers.
3794
+ before changing providers.
3615
3795
  `;
3616
3796
  }
3797
+ function deploymentReadme2(config) {
3798
+ return config.cloud === "railway" ? railwayReadme2(config) : openTofuReadme2(config);
3799
+ }
3800
+ function openTofuPaths2(cloud) {
3801
+ return [
3802
+ `infra/bootstrap/${cloud}`,
3803
+ `infra/modules/${cloud}`,
3804
+ `infra/roots/${cloud}`,
3805
+ `scripts/deploy/release-${cloud}.sh`,
3806
+ `scripts/deploy/frontend-${cloud}.sh`
3807
+ ];
3808
+ }
3809
+ function unusedDeploymentPaths2(selection) {
3810
+ const paths = selection.cloud === "railway" ? ["infra", "scripts/deploy", ".github/workflows/deploy-cloud.yml"] : [
3811
+ ...openTofuClouds2.filter((cloud) => cloud !== selection.cloud).flatMap(openTofuPaths2),
3812
+ "apps/server/railway.toml"
3813
+ ];
3814
+ if (selection.frontendTarget === "native")
3815
+ paths.push("apps/web/wrangler.toml");
3816
+ else if (selection.cloud !== "railway") {
3817
+ paths.push(`scripts/deploy/frontend-${selection.cloud}.sh`);
3818
+ }
3819
+ return paths;
3820
+ }
3617
3821
  async function removeIfPresent2(target) {
3618
3822
  if (!await stat3(target).catch(() => {
3619
3823
  return;
@@ -3622,6 +3826,20 @@ async function removeIfPresent2(target) {
3622
3826
  await rm2(target, { recursive: true, force: true });
3623
3827
  return true;
3624
3828
  }
3829
+ async function removeInfrastructureScript2(repositoryRoot) {
3830
+ const packagePath = join5(repositoryRoot, "package.json");
3831
+ const content = await readFile4(packagePath, "utf8").catch(() => {
3832
+ return;
3833
+ });
3834
+ if (!content)
3835
+ return;
3836
+ const rootPackage = JSON.parse(content);
3837
+ if (!rootPackage.scripts?.["verify:infra"])
3838
+ return;
3839
+ delete rootPackage.scripts["verify:infra"];
3840
+ await writeFile3(packagePath, `${JSON.stringify(rootPackage, null, 2)}
3841
+ `);
3842
+ }
3625
3843
  async function applyDeploymentSelection2(repositoryRoot, projectName, selection) {
3626
3844
  const config = createDeploymentConfig2(projectName, selection);
3627
3845
  const configPath = join5(repositoryRoot, "deployment/config.json");
@@ -3635,40 +3853,12 @@ async function applyDeploymentSelection2(repositoryRoot, projectName, selection)
3635
3853
  `);
3636
3854
  await writeFile3(join5(repositoryRoot, "deployment/README.md"), deploymentReadme2(config));
3637
3855
  const removed = [];
3638
- for (const cloud of deploymentClouds2) {
3639
- if (cloud === selection.cloud)
3640
- continue;
3641
- for (const relativePath of [
3642
- `infra/bootstrap/${cloud}`,
3643
- `infra/modules/${cloud}`,
3644
- `infra/roots/${cloud}`,
3645
- `scripts/deploy/release-${cloud}.sh`,
3646
- `scripts/deploy/frontend-${cloud}.sh`
3647
- ]) {
3648
- if (await removeIfPresent2(join5(repositoryRoot, relativePath)))
3649
- removed.push(relativePath);
3650
- }
3651
- }
3652
- const adapters = [
3653
- { target: "vercel", path: "apps/web/vercel.json" },
3654
- { target: "cloudflare", path: "apps/web/wrangler.toml" },
3655
- {
3656
- target: "azure-native",
3657
- path: "apps/web/public/staticwebapp.config.json"
3658
- }
3659
- ];
3660
- const selectedAdapter = selection.frontendTarget === "native" ? selection.cloud === "azure" ? "azure-native" : undefined : selection.frontendTarget;
3661
- for (const adapter of adapters) {
3662
- if (adapter.target === selectedAdapter)
3663
- continue;
3664
- if (await removeIfPresent2(join5(repositoryRoot, adapter.path)))
3665
- removed.push(adapter.path);
3666
- }
3667
- if (selection.frontendTarget !== "native") {
3668
- const nativeScript = `scripts/deploy/frontend-${selection.cloud}.sh`;
3669
- if (await removeIfPresent2(join5(repositoryRoot, nativeScript)))
3670
- removed.push(nativeScript);
3856
+ for (const relativePath of unusedDeploymentPaths2(selection)) {
3857
+ if (await removeIfPresent2(join5(repositoryRoot, relativePath)))
3858
+ removed.push(relativePath);
3671
3859
  }
3860
+ if (removed.includes("infra"))
3861
+ await removeInfrastructureScript2(repositoryRoot);
3672
3862
  return removed;
3673
3863
  }
3674
3864
 
@@ -4290,15 +4480,15 @@ Options:
4290
4480
  --no-install Skip dependency installation
4291
4481
  --start Start Postgres and development servers after creation
4292
4482
  --no-start Create the application without starting it
4293
- --cloud <provider> Configure AWS, GCP, or Azure deployment during setup
4483
+ --cloud <provider> Configure AWS, GCP, or Railway deployment during setup
4294
4484
  --storage <provider> Select r2, s3, or gcs object storage
4295
- --no-deploy Keep every cloud option for later deployment setup
4296
- --frontend <target> Select native, vercel, or cloudflare frontend hosting
4485
+ --no-deploy Keep every deployment option for later setup
4486
+ --frontend <target> Select native or cloudflare frontend hosting (Railway: cloudflare)
4297
4487
  --deploy-environment <name> Deployment environment (default: production)
4298
- --region <name> Cloud region
4488
+ --region <name> AWS or GCP region
4299
4489
  --api-domain <host> Stable production API hostname
4300
4490
  --frontend-domain <host> Stable production frontend hostname
4301
- --ha / --no-ha Enable or disable the higher-availability profile
4491
+ --ha / --no-ha Enable or disable the AWS or GCP higher-availability profile
4302
4492
  -v, --version Print the CLI version
4303
4493
  -h, --help Show this help
4304
4494
 
@@ -4306,7 +4496,8 @@ Agent tooling:
4306
4496
  ${agentSetupUsage(CLI_NAME)}
4307
4497
 
4308
4498
  Deployment:
4309
- Run pnpm deploy:configure inside a generated app to make a deferred cloud selection.`;
4499
+ Run pnpm deploy:configure inside a generated app to make a deferred deployment selection.
4500
+ The TanStack Start edition always deploys to Cloudflare Workers and D1.`;
4310
4501
  }
4311
4502
  function takeValue3(args, index, flag) {
4312
4503
  const value = args[index + 1];
@@ -4775,7 +4966,7 @@ ${completion.database}`);
4775
4966
  log.info("Mobile pnpm dev:mobile \u2014 run `npx eas init` inside apps/mobile to claim your own\n Expo project before the first build. See docs/mobile.md.");
4776
4967
  }
4777
4968
  if (options.deployment) {
4778
- log.info(`Deployment ${options.deployment.cloud.toUpperCase()} + ${options.deployment.storageProvider} + ${options.deployment.frontendTarget} locked. Review deployment/README.md.`);
4969
+ log.info(`Deployment ${cloudLabels2[options.deployment.cloud]} + ${options.deployment.storageProvider} + ${options.deployment.frontendTarget} locked. Review deployment/README.md.`);
4779
4970
  } else {
4780
4971
  log.info("Deployment deferred. Run `pnpm deploy:configure` when you are ready.");
4781
4972
  }