@todesktop/cli 1.7.7 → 1.8.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.
package/dist/cli.js CHANGED
@@ -20,13 +20,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
20
20
 
21
21
  // src/index.ts
22
22
  var import_commander = require("commander");
23
- var import_ink38 = require("ink");
24
- var import_react24 = require("react");
23
+ var import_ink39 = require("ink");
24
+ var import_react25 = require("react");
25
25
  var import_register = require("source-map-support/register");
26
26
 
27
- // src/components/Build.tsx
27
+ // src/commands/build/components/Build.tsx
28
+ var import_ink13 = require("ink");
28
29
  var import_react6 = require("react");
29
- var import_prop_types9 = __toESM(require("prop-types"));
30
30
 
31
31
  // src/components/BuildProgress.tsx
32
32
  var import_ink4 = require("ink");
@@ -301,7 +301,7 @@ var flush = (callback = () => {
301
301
  // src/utilities/useExit.ts
302
302
  var useExit_default = () => {
303
303
  const { exit } = (0, import_ink.useApp)();
304
- return (error = void 0) => {
304
+ return (error) => {
305
305
  logger_default.debug({ error }, "Exit called");
306
306
  firestore_default.terminate().catch(
307
307
  (e) => logger_default.error(e)
@@ -517,25 +517,47 @@ var BuildProgress_default = BuildProgress;
517
517
 
518
518
  // src/components/ErrorDisplay.tsx
519
519
  var import_ink5 = require("ink");
520
- var import_prop_types4 = __toESM(require("prop-types"));
521
520
  var import_react3 = require("react");
521
+
522
+ // src/utilities/CliError.ts
523
+ var CliError = class extends Error {
524
+ constructor(message2, { cause, type = "error" } = {}) {
525
+ super(message2, { cause });
526
+ this.type = type;
527
+ }
528
+ static from(e, type = "error") {
529
+ if (e instanceof Error) {
530
+ return new CliError(e.message, { cause: e, type });
531
+ } else if (typeof e === "string") {
532
+ return new CliError(e, { type });
533
+ } else {
534
+ return new CliError(JSON.stringify(e), { type });
535
+ }
536
+ }
537
+ };
538
+
539
+ // src/components/ErrorDisplay.tsx
522
540
  var import_jsx_runtime4 = require("react/jsx-runtime");
523
- var ErrorDisplay = ({ commandUsed, error }) => {
541
+ function ErrorDisplay({
542
+ commandUsed,
543
+ error
544
+ }) {
524
545
  const exit = useExit_default();
546
+ const normalizedError = CliError.from(error);
525
547
  logger_default.error({ error });
526
548
  (0, import_react3.useEffect)(() => {
527
549
  if (!error) {
528
550
  return;
529
551
  }
530
- setTimeout(() => exit(error), 10);
531
- }, [exit, error]);
552
+ setTimeout(() => exit(normalizedError), 10);
553
+ }, [exit, error, normalizedError]);
532
554
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
533
555
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { backgroundColor: "red", color: "white", children: [
534
556
  " ",
535
557
  "ERROR",
536
558
  " "
537
559
  ] }),
538
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: error.message || error.stack || error.toString() }),
560
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: normalizedError.message }),
539
561
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginTop: 1, children: [
540
562
  commandUsed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { dimColor: true, color: "gray", children: [
541
563
  "Command: ",
@@ -555,105 +577,29 @@ var ErrorDisplay = ({ commandUsed, error }) => {
555
577
  ] })
556
578
  ] })
557
579
  ] });
558
- };
559
- ErrorDisplay.propTypes = {
560
- commandUsed: import_prop_types4.default.string,
561
- error: import_prop_types4.default.object.isRequired
562
- };
563
- var ErrorDisplay_default = ErrorDisplay;
564
-
565
- // src/components/Preparation.tsx
566
- var import_ink7 = require("ink");
567
-
568
- // src/components/ProgressBar.tsx
569
- var import_prop_types5 = __toESM(require("prop-types"));
570
- var import_ink_gradient = __toESM(require("ink-gradient"));
571
-
572
- // src/libs/ink-progress-bar/index.tsx
573
- var import_ink6 = require("ink");
574
- var import_jsx_runtime5 = require("react/jsx-runtime");
575
- var Bar = ({
576
- percent = 1,
577
- columns = 0,
578
- left = 0,
579
- right = 0,
580
- character = "\u2588",
581
- rightPad = false,
582
- ...rest
583
- }) => {
584
- const getString = () => {
585
- const screen = columns || process.stdout.columns || 80;
586
- const space = screen - right - left;
587
- const max = Math.min(Math.floor(space * percent), space);
588
- const chars = character.repeat(max);
589
- if (!rightPad) {
590
- return chars;
591
- }
592
- return chars + " ".repeat(space - max);
593
- };
594
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { ...rest, children: getString() });
595
- };
596
- var ink_progress_bar_default = Bar;
597
-
598
- // src/components/ProgressBar.tsx
599
- var import_jsx_runtime6 = require("react/jsx-runtime");
600
- var ProgressBar = ({ left, right, percent }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink_gradient.default, { colors: ["gray", "white"], children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ink_progress_bar_default, { left, right, percent }) });
601
- ProgressBar.propTypes = {
602
- left: import_prop_types5.default.number,
603
- right: import_prop_types5.default.number,
604
- percent: import_prop_types5.default.number.isRequired
605
- };
606
- var ProgressBar_default = ProgressBar;
607
-
608
- // src/components/Preparation.tsx
609
- var import_prop_types6 = __toESM(require("prop-types"));
610
- var import_jsx_runtime7 = require("react/jsx-runtime");
611
- var Preparation = ({ progressPercentage, stageLabel, uploadedSize }) => {
612
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { marginBottom: 1, children: [
613
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { children: [
614
- stageLabel,
615
- ": "
616
- ] }),
617
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", children: [
618
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ProgressBar_default, { left: 11, percent: progressPercentage }),
619
- uploadedSize ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { children: uploadedSize }) : void 0
620
- ] })
621
- ] });
622
- };
623
- Preparation.propTypes = {
624
- progressPercentage: import_prop_types6.default.number.isRequired,
625
- stageLabel: import_prop_types6.default.string.isRequired,
626
- uploadedSize: import_prop_types6.default.string
627
- };
628
- var Preparation_default = Preparation;
629
-
630
- // src/components/InitialLoadingState.tsx
631
- var import_ink8 = require("ink");
632
- var import_jsx_runtime8 = require("react/jsx-runtime");
633
- var InitialLoadingState = () => {
634
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { children: "Getting app info..." });
635
- };
636
- var InitialLoadingState_default = InitialLoadingState;
580
+ }
637
581
 
638
582
  // src/components/MainLayout.tsx
639
- var import_ink13 = require("ink");
583
+ var import_ink10 = require("ink");
640
584
  var import_is_ci3 = __toESM(require("is-ci"));
641
585
  var import_react5 = require("react");
642
586
 
643
587
  // src/components/CancelBuild.tsx
644
- var import_ink9 = require("ink");
588
+ var import_ink6 = require("ink");
645
589
  var import_react4 = require("react");
646
590
 
647
591
  // src/utilities/getCallableFirebaseFunction.ts
648
592
  var import_firebase = __toESM(require("firebase"));
649
593
  var import_functions = require("firebase/functions");
650
- if (getEnvironmentVariables_default().TODESKTOP_CLI_ENV === "development") {
651
- import_firebase.default.functions().useFunctionsEmulator("http://localhost:5000");
594
+ var env = getEnvironmentVariables_default();
595
+ if (env.TODESKTOP_CLI_ENV === "development") {
596
+ const firebaseUrl = new URL(env.TODESKTOP_CLI_FIREBASE_FUNCTIONS_BASE);
597
+ import_firebase.default.functions().useFunctionsEmulator(firebaseUrl.origin);
652
598
  }
653
599
  var getCallableFirebaseFunction_default = (functionName) => import_firebase.default.functions().httpsCallable(functionName);
654
600
 
655
601
  // src/components/CancelBuild.tsx
656
- var import_jsx_runtime9 = require("react/jsx-runtime");
602
+ var import_jsx_runtime5 = require("react/jsx-runtime");
657
603
  var cancelBuild = getCallableFirebaseFunction_default("cancelBuild");
658
604
  var CancelBuild = ({
659
605
  appId,
@@ -698,8 +644,8 @@ var CancelBuild = ({
698
644
  if (e.code === "internal") {
699
645
  stateUpdates.error = e;
700
646
  } else {
701
- stateUpdates.arbitraryMessageComponent = /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { children: [
702
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: "red", children: [
647
+ stateUpdates.arbitraryMessageComponent = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { children: [
648
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { color: "red", children: [
703
649
  "Cannot cancel build (",
704
650
  id,
705
651
  ")."
@@ -724,15 +670,15 @@ var CancelBuild = ({
724
670
  }, [postCancelContents, exit, hasCompleted]);
725
671
  if (error) {
726
672
  error.message = `Failed to cancel build (${id}). ${error.message}`;
727
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ErrorDisplay_default, { commandUsed, error });
673
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ErrorDisplay, { commandUsed, error });
728
674
  }
729
675
  if (arbitraryMessageComponent) {
730
676
  return arbitraryMessageComponent;
731
677
  }
732
678
  if (hasCompleted) {
733
- return postCancelContents ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: postCancelContents }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: "Build cancelled" });
679
+ return postCancelContents ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: postCancelContents }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { children: "Build cancelled" });
734
680
  }
735
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { children: [
681
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { children: [
736
682
  "Cancelling build (",
737
683
  id,
738
684
  ")..."
@@ -741,10 +687,10 @@ var CancelBuild = ({
741
687
  var CancelBuild_default = CancelBuild;
742
688
 
743
689
  // src/components/Footer.tsx
744
- var import_ink10 = require("ink");
690
+ var import_ink7 = require("ink");
745
691
  var import_ink_link3 = __toESM(require("ink-link"));
746
- var import_prop_types7 = __toESM(require("prop-types"));
747
- var import_jsx_runtime10 = require("react/jsx-runtime");
692
+ var import_prop_types4 = __toESM(require("prop-types"));
693
+ var import_jsx_runtime6 = require("react/jsx-runtime");
748
694
  var Footer = ({
749
695
  hasBuildEverFailed,
750
696
  shouldShowCancelBuildInstructions,
@@ -755,68 +701,68 @@ var Footer = ({
755
701
  }
756
702
  let buildFailedMessage;
757
703
  if (hasBuildEverFailed) {
758
- buildFailedMessage = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "red", children: "An error has occurred. " });
704
+ buildFailedMessage = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink7.Text, { color: "red", children: "An error has occurred. " });
759
705
  }
760
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
761
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
762
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { bold: true, children: [
706
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ink7.Box, { flexDirection: "column", children: [
707
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ink7.Box, { flexDirection: "column", children: [
708
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ink7.Text, { bold: true, children: [
763
709
  buildFailedMessage,
764
710
  "See web UI for more information: "
765
711
  ] }),
766
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink_link3.default, { fallback: false, url: uiUrl, children: uiUrl }) })
712
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink7.Text, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink_link3.default, { fallback: false, url: uiUrl, children: uiUrl }) })
767
713
  ] }),
768
- shouldShowCancelBuildInstructions ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { color: "gray", children: [
769
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { bold: true, children: "[esc]:" }),
714
+ shouldShowCancelBuildInstructions ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ink7.Text, { color: "gray", children: [
715
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink7.Text, { bold: true, children: "[esc]:" }),
770
716
  " cancel build"
771
717
  ] }) : null
772
718
  ] });
773
719
  };
774
720
  Footer.propTypes = {
775
- hasBuildEverFailed: import_prop_types7.default.bool,
776
- shouldShowCancelBuildInstructions: import_prop_types7.default.bool,
777
- uiUrl: import_prop_types7.default.string
721
+ hasBuildEverFailed: import_prop_types4.default.bool,
722
+ shouldShowCancelBuildInstructions: import_prop_types4.default.bool,
723
+ uiUrl: import_prop_types4.default.string
778
724
  };
779
725
  var Footer_default = Footer;
780
726
 
781
727
  // src/components/Header.tsx
782
- var import_ink11 = require("ink");
783
- var import_prop_types8 = __toESM(require("prop-types"));
784
- var import_jsx_runtime11 = require("react/jsx-runtime");
728
+ var import_ink8 = require("ink");
729
+ var import_prop_types5 = __toESM(require("prop-types"));
730
+ var import_jsx_runtime7 = require("react/jsx-runtime");
785
731
  var getText = (build, name, version) => {
786
732
  const suffix = name + (version ? ` v${version}` : "");
787
733
  if (build && ["cancelled", "succeeded", "failed"].includes(build.status)) {
788
734
  if (build.status === "succeeded") {
789
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { children: [
735
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink8.Text, { children: [
790
736
  "\u2705 ",
791
737
  suffix
792
738
  ] });
793
739
  } else if (build.status === "failed") {
794
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { children: [
740
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink8.Text, { children: [
795
741
  "\u274C ",
796
742
  suffix
797
743
  ] });
798
744
  } else if (build.status === "cancelled") {
799
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { children: [
800
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { bold: true, color: "gray", children: "X" }),
745
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink8.Text, { children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink8.Text, { bold: true, color: "gray", children: "X" }),
801
747
  " ",
802
748
  suffix
803
749
  ] });
804
750
  }
805
751
  } else {
806
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { children: suffix });
752
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink8.Text, { children: suffix });
807
753
  }
808
754
  };
809
- var Header = ({ build, name, version }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginBottom: 1, children: getText(build, name, version) });
755
+ var Header = ({ build, name, version }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink8.Box, { marginBottom: 1, children: getText(build, name, version) });
810
756
  Header.propTypes = {
811
- build: import_prop_types8.default.object,
812
- name: import_prop_types8.default.string.isRequired,
813
- version: import_prop_types8.default.string
757
+ build: import_prop_types5.default.object,
758
+ name: import_prop_types5.default.string.isRequired,
759
+ version: import_prop_types5.default.string
814
760
  };
815
761
  var Header_default = Header;
816
762
 
817
763
  // src/utilities/useInput.ts
818
764
  var import_is_ci2 = __toESM(require("is-ci"));
819
- var import_ink12 = require("ink");
765
+ var import_ink9 = require("ink");
820
766
  var hasSubscribed = false;
821
767
  var ctrlCSubscriptions = [];
822
768
  var onStdin = (data, exit) => {
@@ -833,7 +779,7 @@ var onStdin = (data, exit) => {
833
779
  };
834
780
  var useInput_default = () => {
835
781
  const exit = useExit_default();
836
- const { stdin, isRawModeSupported } = (0, import_ink12.useStdin)();
782
+ const { stdin, isRawModeSupported } = (0, import_ink9.useStdin)();
837
783
  if (import_is_ci2.default || !isRawModeSupported) {
838
784
  return () => () => {
839
785
  };
@@ -847,7 +793,7 @@ var useInput_default = () => {
847
793
  hasSubscribed = true;
848
794
  stdin.on("data", onStdinData);
849
795
  }
850
- const cleanUpInputHook = (0, import_ink12.useInput)(callback, useInputOptions);
796
+ const cleanUpInputHook = (0, import_ink9.useInput)(callback, useInputOptions);
851
797
  return () => {
852
798
  if (hasSubscribed) {
853
799
  stdin.off("data", onStdinData);
@@ -858,7 +804,7 @@ var useInput_default = () => {
858
804
  };
859
805
 
860
806
  // src/components/MainLayout.tsx
861
- var import_jsx_runtime12 = require("react/jsx-runtime");
807
+ var import_jsx_runtime8 = require("react/jsx-runtime");
862
808
  var MainLayout = ({
863
809
  appId,
864
810
  appName,
@@ -870,7 +816,7 @@ var MainLayout = ({
870
816
  }) => {
871
817
  logger_default.debug("MainLayout component: render");
872
818
  const onInput = useInput_default();
873
- const { isRawModeSupported } = (0, import_ink13.useStdin)();
819
+ const { isRawModeSupported } = (0, import_ink10.useStdin)();
874
820
  const [
875
821
  { canCancelBuild, hasKickedOff, isCancellingBuild, wasCtrlCPressed },
876
822
  setState
@@ -912,10 +858,10 @@ var MainLayout = ({
912
858
  }
913
859
  }
914
860
  );
915
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
916
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Header_default, { build, name: appName, version: appVersion }),
917
- isCancellingBuild ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink13.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CancelBuild_default, { appId, commandUsed, id: build.id }) }) : children,
918
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
861
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
862
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Header_default, { build, name: appName, version: appVersion }),
863
+ isCancellingBuild ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CancelBuild_default, { appId, commandUsed, id: build.id }) }) : children,
864
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
919
865
  Footer_default,
920
866
  {
921
867
  hasBuildEverFailed,
@@ -923,9 +869,9 @@ var MainLayout = ({
923
869
  uiUrl: build ? build.url : null
924
870
  }
925
871
  ),
926
- wasCtrlCPressed && hasKickedOff ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
927
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink13.Box, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink13.Text, { color: "gray", children: "The build will continue in the background. To view it, run:" }) }),
928
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink13.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink13.Text, { bold: true, color: "gray", children: [
872
+ wasCtrlCPressed && hasKickedOff ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
873
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink10.Box, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink10.Text, { color: "gray", children: "The build will continue in the background. To view it, run:" }) }),
874
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink10.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink10.Text, { bold: true, color: "gray", children: [
929
875
  "todesktop builds ",
930
876
  build.id
931
877
  ] }) })
@@ -934,30 +880,95 @@ var MainLayout = ({
934
880
  };
935
881
  var MainLayout_default = MainLayout;
936
882
 
937
- // src/utilities/runBuild.ts
883
+ // src/commands/build/components/Preparation.tsx
884
+ var import_ink12 = require("ink");
885
+
886
+ // src/commands/build/components/ProgressBar.tsx
887
+ var import_prop_types6 = __toESM(require("prop-types"));
888
+ var import_ink_gradient = __toESM(require("ink-gradient"));
889
+
890
+ // src/libs/ink-progress-bar/index.tsx
891
+ var import_ink11 = require("ink");
892
+ var import_jsx_runtime9 = require("react/jsx-runtime");
893
+ var Bar = ({
894
+ percent = 1,
895
+ columns = 0,
896
+ left = 0,
897
+ right = 0,
898
+ character = "\u2588",
899
+ rightPad = false,
900
+ ...rest
901
+ }) => {
902
+ const getString = () => {
903
+ const screen = columns || process.stdout.columns || 80;
904
+ const space = screen - right - left;
905
+ const max = Math.min(Math.floor(space * percent), space);
906
+ const chars = character.repeat(max);
907
+ if (!rightPad) {
908
+ return chars;
909
+ }
910
+ return chars + " ".repeat(space - max);
911
+ };
912
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink11.Text, { ...rest, children: getString() });
913
+ };
914
+ var ink_progress_bar_default = Bar;
915
+
916
+ // src/commands/build/components/ProgressBar.tsx
917
+ var import_jsx_runtime10 = require("react/jsx-runtime");
918
+ var ProgressBar = ({ left, right, percent }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink_gradient.default, { colors: ["gray", "white"], children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ink_progress_bar_default, { left, right, percent }) });
919
+ ProgressBar.propTypes = {
920
+ left: import_prop_types6.default.number,
921
+ right: import_prop_types6.default.number,
922
+ percent: import_prop_types6.default.number.isRequired
923
+ };
924
+ var ProgressBar_default = ProgressBar;
925
+
926
+ // src/commands/build/components/Preparation.tsx
927
+ var import_prop_types7 = __toESM(require("prop-types"));
928
+ var import_jsx_runtime11 = require("react/jsx-runtime");
929
+ var Preparation = ({ progressPercentage, stageLabel, uploadedSize }) => {
930
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink12.Box, { marginBottom: 1, children: [
931
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink12.Text, { children: [
932
+ stageLabel,
933
+ ": "
934
+ ] }),
935
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink12.Box, { flexDirection: "column", children: [
936
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ProgressBar_default, { left: 11, percent: progressPercentage }),
937
+ uploadedSize ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink12.Text, { children: uploadedSize }) : void 0
938
+ ] })
939
+ ] });
940
+ };
941
+ Preparation.propTypes = {
942
+ progressPercentage: import_prop_types7.default.number.isRequired,
943
+ stageLabel: import_prop_types7.default.string.isRequired,
944
+ uploadedSize: import_prop_types7.default.string
945
+ };
946
+ var Preparation_default = Preparation;
947
+
948
+ // src/commands/build/utilities/runBuild.ts
938
949
  var import_pretty_bytes = __toESM(require("pretty-bytes"));
939
950
 
940
- // src/utilities/getVersionControlInfo.ts
941
- var gitRevSync = __toESM(require("git-rev-sync"));
942
- var getVersionControlInfo_default = async (directory) => {
943
- let result = {};
951
+ // src/utilities/postToFirebaseFunction.ts
952
+ var import_axios = __toESM(require("axios"));
953
+ var { TODESKTOP_CLI_FIREBASE_FUNCTIONS_BASE } = getEnvironmentVariables_default();
954
+ async function postToFirebaseFunction_default(functionName, body = {}, config2 = {}) {
955
+ logger_default.debug({ functionName, body, config: config2 }, "postToFirebaseFunction");
944
956
  try {
945
- const gitCommitHash = gitRevSync.long(directory);
946
- if (gitCommitHash) {
947
- result = {
948
- branchName: gitRevSync.branch(directory),
949
- commitDate: gitRevSync.date().toISOString(),
950
- commitId: gitCommitHash,
951
- commitMessage: gitRevSync.message(),
952
- hasUncommittedChanges: gitRevSync.isDirty(),
953
- repositoryRemoteUrl: gitRevSync.remoteUrl(),
954
- versionControlSystemName: "git"
955
- };
956
- }
957
+ const response = await import_axios.default.post(
958
+ `${TODESKTOP_CLI_FIREBASE_FUNCTIONS_BASE}${functionName}`,
959
+ body,
960
+ config2
961
+ );
962
+ logger_default.debug(
963
+ { responseData: response.data },
964
+ "postToFirebaseFunction: success"
965
+ );
966
+ return response.data;
957
967
  } catch (e) {
968
+ logger_default.error({ error: e }, "postToFirebaseFunction: error");
969
+ throw e;
958
970
  }
959
- return result;
960
- };
971
+ }
961
972
 
962
973
  // src/utilities/projectConfig/getProjectConfig.ts
963
974
  var import_path5 = require("path");
@@ -1663,6 +1674,16 @@ var full_default = (context) => {
1663
1674
  validSemver: {},
1664
1675
  minLength: 1
1665
1676
  },
1677
+ pnpmVersion: {
1678
+ type: "string",
1679
+ validSemver: {},
1680
+ minLength: 1
1681
+ },
1682
+ yarnVersion: {
1683
+ type: "string",
1684
+ validSemver: {},
1685
+ minLength: 1
1686
+ },
1666
1687
  appBuilderLibVersion: {
1667
1688
  type: "string",
1668
1689
  validSemver: {},
@@ -1834,8 +1855,8 @@ function computeFullProjectConfig(partialConfig, projectRoot) {
1834
1855
  }
1835
1856
 
1836
1857
  // src/utilities/projectConfig/getProjectConfig.ts
1837
- function getProjectConfig(configPath = null) {
1838
- if (configPath === null) {
1858
+ function getProjectConfig(configPath) {
1859
+ if (!configPath) {
1839
1860
  logger_default.debug("No config path provided, searching for one");
1840
1861
  configPath = import_find_up.default.sync("todesktop.json");
1841
1862
  if (!configPath) {
@@ -1844,10 +1865,6 @@ function getProjectConfig(configPath = null) {
1844
1865
  );
1845
1866
  }
1846
1867
  } else {
1847
- if (typeof configPath === "undefined" || configPath === "") {
1848
- logger_default.error("Provided config path is empty");
1849
- throw new Error("No config path provided");
1850
- }
1851
1868
  configPath = (0, import_path5.resolve)(process.cwd(), configPath);
1852
1869
  if (!(0, import_fs.existsSync)(configPath)) {
1853
1870
  logger_default.error("Provided config path does not exist");
@@ -1862,74 +1879,145 @@ function getProjectConfig(configPath = null) {
1862
1879
  return { config: result, unprocessedConfig: config2, projectRoot };
1863
1880
  }
1864
1881
 
1865
- // src/utilities/postToFirebaseFunction.ts
1866
- var import_axios = __toESM(require("axios"));
1867
- var { TODESKTOP_CLI_FIREBASE_FUNCTIONS_BASE } = getEnvironmentVariables_default();
1868
- async function postToFirebaseFunction_default(functionName, body = {}, config2 = {}) {
1869
- logger_default.debug({ functionName, body, config: config2 }, "postToFirebaseFunction");
1870
- try {
1871
- const response = await import_axios.default.post(
1872
- `${TODESKTOP_CLI_FIREBASE_FUNCTIONS_BASE}${functionName}`,
1873
- body,
1874
- config2
1875
- );
1876
- logger_default.debug(
1877
- { responseData: response.data },
1878
- "postToFirebaseFunction: success"
1879
- );
1880
- return response.data;
1881
- } catch (e) {
1882
- logger_default.error({ error: e }, "postToFirebaseFunction: error");
1883
- throw e;
1882
+ // src/utilities/shouldExitOnBuildFailure.ts
1883
+ var shouldExitOnBuildFailure_default = (build) => {
1884
+ const hasBuildSettled = ["linux", "mac", "windows"].every(
1885
+ (platform) => build[platform].shouldSkip || "succeeded" === build[platform].status || "failed" === build[platform].status && build[platform].numberOfAttemptedBuilds === 2
1886
+ );
1887
+ const hasSettledWithError = ["linux", "mac", "windows"].some(
1888
+ (platform) => "failed" === build[platform].status
1889
+ );
1890
+ return hasBuildSettled && hasSettledWithError;
1891
+ };
1892
+
1893
+ // src/commands/build/utilities/getPackageJson.ts
1894
+ var import_lodash3 = __toESM(require("lodash.merge"));
1895
+ var import_path6 = __toESM(require("path"));
1896
+ function deleteNullDeps(dep) {
1897
+ Object.keys(dep).forEach((key) => {
1898
+ if (dep[key] === null) {
1899
+ delete dep[key];
1900
+ }
1901
+ });
1902
+ return dep;
1903
+ }
1904
+ function removeNullDependencies(pkgJson) {
1905
+ const { dependencies, devDependencies } = pkgJson;
1906
+ if (dependencies) {
1907
+ pkgJson.dependencies = deleteNullDeps(dependencies);
1908
+ }
1909
+ if (devDependencies) {
1910
+ pkgJson.devDependencies = deleteNullDeps(devDependencies);
1884
1911
  }
1912
+ return pkgJson;
1913
+ }
1914
+ function getAppPkgJson({ config: config2 }) {
1915
+ const packageJsonFromConfig = config2.packageJson || {};
1916
+ const extendsFrom = packageJsonFromConfig.extends || "package.json";
1917
+ const packageJsonFromFile = readJson(import_path6.default.join(config2.appPath, extendsFrom));
1918
+ return removeNullDependencies(
1919
+ (0, import_lodash3.default)({}, packageJsonFromFile, packageJsonFromConfig)
1920
+ );
1885
1921
  }
1922
+ var getPackageJson_default = getAppPkgJson;
1886
1923
 
1887
- // src/utilities/subscribeToFirebaseDoc.ts
1888
- var subscribeToFirebaseDoc_default = async (key, receiver) => {
1889
- const hasResolved = false;
1890
- return new Promise((resolve4, reject) => {
1891
- logger_default.debug({ key }, "subscribeToFirebaseDoc");
1892
- firestore_default.doc(key).onSnapshot(
1893
- (snapshot) => {
1894
- receiver({
1895
- snapshot,
1896
- data: snapshot.exists ? snapshot.data() : void 0
1897
- });
1898
- if (!hasResolved) {
1899
- resolve4();
1900
- }
1901
- },
1902
- (err) => {
1903
- reject(err);
1904
- }
1905
- );
1906
- });
1907
- };
1908
-
1909
- // src/utilities/subscribeToBuild.ts
1910
- var subscribeToBuild_default = async ({ appId, buildId, onDataReceived, userId }) => {
1924
+ // src/commands/build/utilities/getVersionControlInfo.ts
1925
+ var gitRevSync = __toESM(require("git-rev-sync"));
1926
+ var getVersionControlInfo_default = async (directory) => {
1927
+ let result = {};
1911
1928
  try {
1912
- return await subscribeToFirebaseDoc_default(
1913
- `users/${userId}/applications/${appId}/builds/${buildId}`,
1914
- async ({ data }) => {
1915
- onDataReceived(data);
1916
- }
1917
- );
1929
+ const gitCommitHash = gitRevSync.long(directory);
1930
+ if (gitCommitHash) {
1931
+ result = {
1932
+ branchName: gitRevSync.branch(directory),
1933
+ commitDate: gitRevSync.date().toISOString(),
1934
+ commitId: gitCommitHash,
1935
+ commitMessage: gitRevSync.message(),
1936
+ hasUncommittedChanges: gitRevSync.isDirty(),
1937
+ repositoryRemoteUrl: gitRevSync.remoteUrl(),
1938
+ versionControlSystemName: "git"
1939
+ };
1940
+ }
1918
1941
  } catch (e) {
1919
- e.message = `Failed while subscribing to build; ${e.message}`;
1920
- throw e;
1921
1942
  }
1943
+ return result;
1922
1944
  };
1923
1945
 
1924
- // src/utilities/uploadApplicationSource.ts
1946
+ // src/commands/build/utilities/spyBuild.ts
1947
+ var import_events = __toESM(require("events"));
1948
+ function spyBuild() {
1949
+ const emitter = new import_events.default();
1950
+ let unsubscribeSnapshot;
1951
+ let latestBuildDoc;
1952
+ function when(predicate) {
1953
+ if (latestBuildDoc && predicate(latestBuildDoc)) {
1954
+ return Promise.resolve(latestBuildDoc);
1955
+ }
1956
+ return new Promise((resolve4, reject) => {
1957
+ emitter.on("update", ({ build }) => {
1958
+ if (predicate(build)) {
1959
+ resolve4(build);
1960
+ }
1961
+ }).once("error", (e) => reject(e));
1962
+ });
1963
+ }
1964
+ return {
1965
+ onUpdate(callback) {
1966
+ emitter.on("update", callback);
1967
+ },
1968
+ subscribe({
1969
+ appId,
1970
+ buildId,
1971
+ userId
1972
+ }) {
1973
+ const buildPath = `users/${userId}/applications/${appId}/builds/${buildId}`;
1974
+ unsubscribeSnapshot = firestore_default.doc(buildPath).onSnapshot(
1975
+ (snapshot) => {
1976
+ latestBuildDoc = snapshot.exists ? snapshot.data() : void 0;
1977
+ emitter.emit("update", {
1978
+ build: latestBuildDoc,
1979
+ snapshot
1980
+ });
1981
+ },
1982
+ (err) => {
1983
+ emitter.emit("error", err);
1984
+ }
1985
+ );
1986
+ },
1987
+ unsubscribe() {
1988
+ unsubscribeSnapshot == null ? void 0 : unsubscribeSnapshot();
1989
+ },
1990
+ async whenFirstUpdate() {
1991
+ if (latestBuildDoc) {
1992
+ return Promise.resolve(latestBuildDoc);
1993
+ }
1994
+ return new Promise((resolve4, reject) => {
1995
+ emitter.once("update", (data) => resolve4(data)).once("error", (e) => reject(e));
1996
+ });
1997
+ },
1998
+ async whenSettled() {
1999
+ return when((build) => buildHasSettled(build));
2000
+ }
2001
+ };
2002
+ }
2003
+ function buildHasSettled(build) {
2004
+ return ["linux", "mac", "windows"].every(
2005
+ (platform) => {
2006
+ var _a, _b, _c, _d, _e;
2007
+ return ((_a = build[platform]) == null ? void 0 : _a.shouldSkip) || "succeeded" === ((_b = build[platform]) == null ? void 0 : _b.status) || "cancelled" === ((_c = build[platform]) == null ? void 0 : _c.status) || "failed" === ((_d = build[platform]) == null ? void 0 : _d.status) && ((_e = build[platform]) == null ? void 0 : _e.numberOfAttemptedBuilds) === 2;
2008
+ }
2009
+ );
2010
+ }
2011
+
2012
+ // src/commands/build/utilities/uploadApplicationSource.ts
1925
2013
  var import_fast_glob = __toESM(require("fast-glob"));
1926
2014
  var fs5 = __toESM(require("fs"));
1927
- var path8 = __toESM(require("path"));
2015
+ var path9 = __toESM(require("path"));
1928
2016
 
1929
- // src/utilities/generateS3Key.ts
2017
+ // src/commands/build/utilities/generateS3Key.ts
1930
2018
  var generateS3Key_default = ({ appId, buildId, filenameSuffix }) => `${appId}/sourceArchives/${buildId}--${filenameSuffix}`;
1931
2019
 
1932
- // src/utilities/uploadToS3.ts
2020
+ // src/commands/build/utilities/uploadToS3.ts
1933
2021
  var import_superagent = __toESM(require("superagent"));
1934
2022
  var import_stream_to_array = __toESM(require("stream-to-array"));
1935
2023
  var { TODESKTOP_CLI_S3_BUCKET } = getEnvironmentVariables_default();
@@ -1981,12 +2069,12 @@ var uploadToS3 = async ({
1981
2069
  };
1982
2070
  var uploadToS3_default = uploadToS3WithRetry;
1983
2071
 
1984
- // src/utilities/zip.ts
2072
+ // src/commands/build/utilities/zip.ts
1985
2073
  var import_archiver = __toESM(require("archiver"));
1986
2074
  var import_du = __toESM(require("du"));
1987
2075
  var import_fs2 = __toESM(require("fs"));
1988
2076
  var import_chalk = __toESM(require("chalk"));
1989
- var import_path6 = __toESM(require("path"));
2077
+ var import_path7 = __toESM(require("path"));
1990
2078
  async function zip_default({
1991
2079
  files,
1992
2080
  fileSizeLimit = 20,
@@ -2041,7 +2129,7 @@ Your app is larger than ${fileSizeLimit}MB. Your app is ${import_chalk.default.b
2041
2129
  processedFiles.forEach(({ from, isDirectory, stats, to }) => {
2042
2130
  if (isDirectory) {
2043
2131
  stream.directory(from, to);
2044
- } else if (appPkgJson && to === import_path6.default.join("app", "package.json")) {
2132
+ } else if (appPkgJson && to === import_path7.default.join("app", "package.json")) {
2045
2133
  stream.append(JSON.stringify(appPkgJson), {
2046
2134
  name: to
2047
2135
  });
@@ -2060,28 +2148,28 @@ Your app is larger than ${fileSizeLimit}MB. Your app is ${import_chalk.default.b
2060
2148
  return stream;
2061
2149
  }
2062
2150
 
2063
- // src/utilities/uploadApplicationSource.ts
2151
+ // src/commands/build/utilities/uploadApplicationSource.ts
2064
2152
  var getAppFiles = async (globsInput, appPath, appPkgJson) => {
2065
2153
  const globs = ["!node_modules", "!**/node_modules", "!.git", "!**/.git"];
2066
2154
  if (globsInput && globsInput.length) {
2067
2155
  globs.push(
2068
2156
  ...globsInput,
2069
- path8.join(appPath, "package.json"),
2070
- path8.join(appPath, "package-lock.json"),
2071
- path8.join(appPath, "yarn.lock"),
2072
- path8.join(appPath, "pnpm-lock.yaml"),
2073
- path8.join(appPath, "shrinkwrap.yaml")
2157
+ path9.join(appPath, "package.json"),
2158
+ path9.join(appPath, "package-lock.json"),
2159
+ path9.join(appPath, "yarn.lock"),
2160
+ path9.join(appPath, "pnpm-lock.yaml"),
2161
+ path9.join(appPath, "shrinkwrap.yaml")
2074
2162
  );
2075
2163
  } else {
2076
2164
  globs.push("**");
2077
2165
  }
2078
2166
  for (const hookName of ["todesktop:beforeInstall", "todesktop:afterPack"]) {
2079
2167
  if (appPkgJson.scripts && appPkgJson.scripts[hookName]) {
2080
- globs.push(path8.join(appPath, appPkgJson.scripts[hookName]));
2168
+ globs.push(path9.join(appPath, appPkgJson.scripts[hookName]));
2081
2169
  }
2082
2170
  }
2083
2171
  const normalizedGlobs = globs.map((glob) => {
2084
- const globToUse = path8.isAbsolute(glob) ? path8.relative(appPath, glob) : glob;
2172
+ const globToUse = path9.isAbsolute(glob) ? path9.relative(appPath, glob) : glob;
2085
2173
  return globToUse.replace(/\\/g, "/").replace(/\/+$/, "");
2086
2174
  }).filter((glob) => !glob.startsWith("..") && !glob.startsWith("!.."));
2087
2175
  let absolutePaths = await (0, import_fast_glob.default)(normalizedGlobs, {
@@ -2096,7 +2184,7 @@ var getAppFiles = async (globsInput, appPath, appPkgJson) => {
2096
2184
  });
2097
2185
  if (process.platform === "win32") {
2098
2186
  absolutePaths = absolutePaths.map(
2099
- (absolutePath) => absolutePath.replace(/\//g, path8.sep)
2187
+ (absolutePath) => absolutePath.replace(/\//g, path9.sep)
2100
2188
  );
2101
2189
  }
2102
2190
  if (!absolutePaths || !absolutePaths.length) {
@@ -2108,25 +2196,31 @@ var getAppFiles = async (globsInput, appPath, appPkgJson) => {
2108
2196
  } else {
2109
2197
  let mainFilePath = appPath;
2110
2198
  if (appPkgJson.main) {
2111
- mainFilePath = path8.join(mainFilePath, appPkgJson.main);
2199
+ mainFilePath = path9.join(mainFilePath, appPkgJson.main);
2112
2200
  }
2113
2201
  if (fs5.statSync(mainFilePath).isDirectory()) {
2114
- mainFilePath = path8.join(mainFilePath, "index.js");
2202
+ mainFilePath = path9.join(mainFilePath, "index.js");
2115
2203
  }
2116
2204
  if (!absolutePaths.includes(mainFilePath)) {
2117
2205
  throw new Error(
2118
- `The "main" file specified in your package.json (${appPkgJson.main ? path8.relative(appPath, mainFilePath) : "defaults to index.js"}) is not set to be uploaded to our servers. This is likely due to how you have configured the \`appFiles\` option. Learn more at https://www.npmjs.com/package/@todesktop/cli#appfiles----optional-array-of-glob-patterns. If this is not the case, please contact us.`
2206
+ `The "main" file specified in your package.json (${appPkgJson.main ? path9.relative(appPath, mainFilePath) : "defaults to index.js"}) is not set to be uploaded to our servers. This is likely due to how you have configured the \`appFiles\` option. Learn more at https://www.npmjs.com/package/@todesktop/cli#appfiles----optional-array-of-glob-patterns. If this is not the case, please contact us.`
2119
2207
  );
2120
2208
  }
2121
2209
  }
2122
2210
  return absolutePaths.map((absolutePath) => {
2123
2211
  return {
2124
2212
  from: absolutePath,
2125
- to: path8.join("app", path8.relative(appPath, absolutePath))
2213
+ to: path9.join("app", path9.relative(appPath, absolutePath))
2126
2214
  };
2127
2215
  });
2128
2216
  };
2129
- var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, config: config2, onProgress }) => {
2217
+ async function uploadApplicationSource({
2218
+ appId,
2219
+ appPkgJson,
2220
+ buildId,
2221
+ config: config2,
2222
+ onProgress
2223
+ }) {
2130
2224
  logger_default.debug(
2131
2225
  {
2132
2226
  appId,
@@ -2143,48 +2237,70 @@ var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, confi
2143
2237
  ...(config2.extraContentFiles || []).map(({ from, to = "." }) => {
2144
2238
  return {
2145
2239
  from,
2146
- to: path8.join("extraContentFiles", to, path8.basename(from))
2240
+ to: path9.join("extraContentFiles", to, path9.basename(from))
2147
2241
  };
2148
2242
  }),
2149
2243
  ...(config2.extraResources || []).map(({ from, to = "." }) => {
2150
2244
  return {
2151
2245
  from,
2152
- to: path8.join("extraResources", to, path8.basename(from))
2246
+ to: path9.join("extraResources", to, path9.basename(from))
2153
2247
  };
2154
2248
  }),
2155
2249
  {
2156
2250
  from: config2.icon,
2157
- to: path8.join("icons", "appIcon" + path8.extname(config2.icon))
2251
+ to: path9.join("icons", "appIcon" + path9.extname(config2.icon))
2158
2252
  }
2159
2253
  ];
2160
2254
  if (config2.linux) {
2161
2255
  if (config2.linux.icon) {
2162
2256
  files.push({
2163
2257
  from: config2.linux.icon,
2164
- to: path8.join(
2258
+ to: path9.join(
2165
2259
  "icons",
2166
- "appIcon-linux" + path8.extname(config2.linux.icon)
2260
+ "appIcon-linux" + path9.extname(config2.linux.icon)
2167
2261
  )
2168
2262
  });
2169
2263
  }
2170
2264
  }
2265
+ if (Array.isArray(config2.fileAssociations)) {
2266
+ const possibleIcons = [];
2267
+ for (const fa of config2.fileAssociations) {
2268
+ if (fa.icon) {
2269
+ const icon = path9.parse(fa.icon);
2270
+ possibleIcons.push(
2271
+ { ext: fa.ext, icon: path9.join(icon.dir, icon.name) + ".ico" },
2272
+ { ext: fa.ext, icon: path9.join(icon.dir, icon.name) + ".icns" }
2273
+ );
2274
+ }
2275
+ }
2276
+ await Promise.all(
2277
+ possibleIcons.map(async ({ ext, icon }) => {
2278
+ if (await exists(icon)) {
2279
+ files.push({
2280
+ from: icon,
2281
+ to: path9.join("icons", "association-" + ext + path9.extname(icon))
2282
+ });
2283
+ }
2284
+ })
2285
+ );
2286
+ }
2171
2287
  if (config2.mac) {
2172
2288
  if (config2.mac.entitlements) {
2173
2289
  files.push({
2174
2290
  from: config2.mac.entitlements,
2175
- to: path8.join("other", "mac", "entitlements.mac.plist")
2291
+ to: path9.join("other", "mac", "entitlements.mac.plist")
2176
2292
  });
2177
2293
  }
2178
2294
  if (config2.mac.icon) {
2179
2295
  files.push({
2180
2296
  from: config2.mac.icon,
2181
- to: path8.join("icons", "appIcon-mac" + path8.extname(config2.mac.icon))
2297
+ to: path9.join("icons", "appIcon-mac" + path9.extname(config2.mac.icon))
2182
2298
  });
2183
2299
  }
2184
2300
  if (config2.mac.requirements) {
2185
2301
  files.push({
2186
2302
  from: config2.mac.requirements,
2187
- to: path8.join("other", "mac", "requirements.txt")
2303
+ to: path9.join("other", "mac", "requirements.txt")
2188
2304
  });
2189
2305
  }
2190
2306
  }
@@ -2192,7 +2308,7 @@ var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, confi
2192
2308
  if (config2.dmg.background) {
2193
2309
  files.push({
2194
2310
  from: config2.dmg.background,
2195
- to: path8.join("other", "mac", "dmg-background.tiff")
2311
+ to: path9.join("other", "mac", "dmg-background.tiff")
2196
2312
  });
2197
2313
  }
2198
2314
  }
@@ -2200,16 +2316,16 @@ var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, confi
2200
2316
  if (config2.windows.icon) {
2201
2317
  files.push({
2202
2318
  from: config2.windows.icon,
2203
- to: path8.join(
2319
+ to: path9.join(
2204
2320
  "icons",
2205
- "appIcon-windows" + path8.extname(config2.windows.icon)
2321
+ "appIcon-windows" + path9.extname(config2.windows.icon)
2206
2322
  )
2207
2323
  });
2208
2324
  }
2209
2325
  if (config2.windows.nsisInclude) {
2210
2326
  files.push({
2211
2327
  from: config2.windows.nsisInclude,
2212
- to: path8.join("other", "installer.nsh")
2328
+ to: path9.join("other", "installer.nsh")
2213
2329
  });
2214
2330
  }
2215
2331
  }
@@ -2235,50 +2351,39 @@ var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, confi
2235
2351
  onProgress(loaded / total * 100, loaded);
2236
2352
  }
2237
2353
  });
2238
- };
2239
-
2240
- // src/utilities/getPackageJson.ts
2241
- var import_lodash3 = __toESM(require("lodash.merge"));
2242
- var import_path7 = __toESM(require("path"));
2243
- function deleteNullDeps(dep) {
2244
- Object.keys(dep).forEach((key) => {
2245
- if (dep[key] === null) {
2246
- delete dep[key];
2247
- }
2248
- });
2249
- return dep;
2250
2354
  }
2251
- function removeNullDependencies(pkgJson) {
2252
- const { dependencies, devDependencies } = pkgJson;
2253
- if (dependencies) {
2254
- pkgJson.dependencies = deleteNullDeps(dependencies);
2255
- }
2256
- if (devDependencies) {
2257
- pkgJson.devDependencies = deleteNullDeps(devDependencies);
2355
+ async function exists(filePath) {
2356
+ try {
2357
+ await fs5.promises.access(filePath);
2358
+ return true;
2359
+ } catch (e) {
2360
+ return false;
2258
2361
  }
2259
- return pkgJson;
2260
2362
  }
2261
- function getAppPkgJson({ config: config2 }) {
2262
- const packageJsonFromConfig = config2.packageJson || {};
2263
- const extendsFrom = packageJsonFromConfig.extends || "package.json";
2264
- const packageJsonFromFile = readJson(import_path7.default.join(config2.appPath, extendsFrom));
2265
- return removeNullDependencies(
2266
- (0, import_lodash3.default)({}, packageJsonFromFile, packageJsonFromConfig)
2267
- );
2268
- }
2269
- var getPackageJson_default = getAppPkgJson;
2270
2363
 
2271
- // src/utilities/runBuild.ts
2272
- var runBuild_default = async ({ onEvent, shouldCodeSign = true, configPath }) => {
2364
+ // src/commands/build/utilities/runBuild.ts
2365
+ async function runBuild({
2366
+ configPath,
2367
+ exitAfterUploading,
2368
+ shouldCodeSign = true,
2369
+ onBuildFinishedWebhook,
2370
+ updateState
2371
+ }) {
2372
+ var _a;
2273
2373
  logForCI_default("Getting application information...");
2274
- const primaryUserId = currentUser().uid;
2374
+ const primaryUserId = (_a = currentUser()) == null ? void 0 : _a.uid;
2275
2375
  const { config: config2, unprocessedConfig } = getProjectConfig(configPath);
2276
2376
  const appId = config2.id;
2277
2377
  const appPkgJson = getPackageJson_default({ config: config2 });
2278
- onEvent("progress", {
2378
+ const buildObserver = spyBuild();
2379
+ buildObserver.onUpdate(({ build: build2 }) => updateState({ build: build2 }));
2380
+ updateState({
2279
2381
  appId,
2280
2382
  appPkg: appPkgJson,
2281
- preparationProgress: 0.02
2383
+ onUnmount: () => buildObserver.unsubscribe(),
2384
+ preparationStageLabel: "Preparing",
2385
+ preparationProgress: 0.02,
2386
+ state: "preparing"
2282
2387
  });
2283
2388
  logForCI_default("Preparing...");
2284
2389
  let buildId;
@@ -2288,6 +2393,7 @@ var runBuild_default = async ({ onEvent, shouldCodeSign = true, configPath }) =>
2288
2393
  appPkgProductName: appPkgJson.productName,
2289
2394
  appVersion: appPkgJson.version,
2290
2395
  id: config2.id,
2396
+ onBuildFinishedWebhook,
2291
2397
  projectConfig: unprocessedConfig,
2292
2398
  shouldCodeSign: shouldCodeSign !== false,
2293
2399
  shouldRelease: false,
@@ -2295,46 +2401,38 @@ var runBuild_default = async ({ onEvent, shouldCodeSign = true, configPath }) =>
2295
2401
  versionControlInfo: await getVersionControlInfo_default(config2.appPath)
2296
2402
  });
2297
2403
  buildId = prepareResult.appData.meta.currentBuildProgress.id;
2298
- const firebaseUnsubscribe = await subscribeToBuild_default({
2299
- appId,
2300
- buildId,
2301
- onDataReceived: (data) => onEvent("progress", { build: data }),
2302
- userId: prepareResult.userId
2303
- });
2304
- onEvent("firebase-subscribed", { firebaseUnsubscribe });
2404
+ buildObserver.subscribe({ appId, buildId, userId: prepareResult.userId });
2405
+ await buildObserver.whenFirstUpdate();
2305
2406
  } catch (e) {
2306
- e.message = `Failed while preparing new build; ${e.message}`;
2307
- throw e;
2407
+ throw addErrorMessage(e, "Failed while preparing new build");
2308
2408
  }
2309
- onEvent("progress", {
2409
+ updateState({
2310
2410
  preparationProgress: 0.05,
2311
2411
  preparationStageLabel: "Uploading"
2312
2412
  });
2313
2413
  logForCI_default("Uploading...");
2314
2414
  let sourceArchiveDetails;
2315
2415
  try {
2316
- const uploadAppSourceResult = await uploadApplicationSource_default({
2416
+ sourceArchiveDetails = await uploadApplicationSource({
2317
2417
  appId,
2318
2418
  appPkgJson,
2319
2419
  buildId,
2320
2420
  config: config2,
2321
2421
  onProgress(progress, uploadedSize) {
2322
- const dataToEmit = {
2422
+ const preparationState = {
2323
2423
  preparationProgress: 0.05 + progress / 100 * 0.95,
2324
2424
  preparationUploadedSize: ""
2325
2425
  };
2326
2426
  if (uploadedSize) {
2327
- dataToEmit.preparationUploadedSize = (0, import_pretty_bytes.default)(uploadedSize);
2427
+ preparationState.preparationUploadedSize = (0, import_pretty_bytes.default)(uploadedSize);
2328
2428
  }
2329
- onEvent("progress", dataToEmit);
2429
+ updateState(preparationState);
2330
2430
  }
2331
2431
  });
2332
- sourceArchiveDetails = uploadAppSourceResult;
2333
2432
  } catch (e) {
2334
- e.message = `Failed while uploading application source; ${e.message}`;
2335
- throw e;
2433
+ throw addErrorMessage(e, "Failed while uploading application source");
2336
2434
  }
2337
- onEvent("progress", { isPreparationComplete: true });
2435
+ updateState({ state: "building" });
2338
2436
  logForCI_default("Kicking off build...");
2339
2437
  try {
2340
2438
  await postToFirebaseFunction_default("kickOffBuild", {
@@ -2346,128 +2444,132 @@ var runBuild_default = async ({ onEvent, shouldCodeSign = true, configPath }) =>
2346
2444
  sourceArchiveDetails,
2347
2445
  nodeVersion: config2.nodeVersion,
2348
2446
  npmVersion: config2.npmVersion,
2447
+ pnpmVersion: config2.pnpmVersion,
2448
+ yarnVersion: config2.yarnVersion,
2349
2449
  appBuilderLibVersion: config2.appBuilderLibVersion
2350
2450
  });
2351
2451
  } catch (e) {
2352
- e.message = `Failed while kicking off build; ${e.message}`;
2353
- throw e;
2452
+ throw addErrorMessage(e, "Failed while kicking off build");
2354
2453
  }
2355
- };
2356
-
2357
- // src/utilities/shouldExitOnBuildFailure.ts
2358
- var shouldExitOnBuildFailure_default = (build) => {
2359
- const hasBuildSettled = ["linux", "mac", "windows"].every(
2360
- (platform) => build[platform].shouldSkip || "succeeded" === build[platform].status || "failed" === build[platform].status && build[platform].numberOfAttemptedBuilds === 2
2361
- );
2362
- const hasSettledWithError = ["linux", "mac", "windows"].some(
2363
- (platform) => "failed" === build[platform].status
2364
- );
2365
- return hasBuildSettled && hasSettledWithError;
2366
- };
2454
+ if (exitAfterUploading) {
2455
+ updateState({ state: "exit-after-uploading" });
2456
+ return;
2457
+ }
2458
+ const build = await buildObserver.whenSettled();
2459
+ if ((build == null ? void 0 : build.status) === "failed" && shouldExitOnBuildFailure_default(build)) {
2460
+ updateState({ state: "build-failed" });
2461
+ }
2462
+ }
2463
+ function addErrorMessage(e, message2) {
2464
+ let originalMessage = "";
2465
+ if (e instanceof Error) {
2466
+ originalMessage = e.message;
2467
+ } else if (e && typeof e === "object") {
2468
+ originalMessage = JSON.stringify(e);
2469
+ } else {
2470
+ originalMessage = String(e);
2471
+ }
2472
+ const newError = new Error(`${message2}; ${originalMessage}`);
2473
+ if (e instanceof Error) {
2474
+ newError.stack = e.stack;
2475
+ }
2476
+ return newError;
2477
+ }
2367
2478
 
2368
- // src/components/Build.tsx
2369
- var import_jsx_runtime13 = require("react/jsx-runtime");
2370
- var initalState = {
2371
- appId: null,
2372
- appPkg: null,
2373
- build: null,
2374
- error: null,
2375
- hasBuildEverFailed: false,
2376
- isPreparationComplete: false,
2377
- preparationStageLabel: "Preparing",
2378
- preparationProgress: 0,
2379
- preparationUploadedSize: null
2380
- };
2381
- var Build = ({ commandUsed, shouldCodeSign = true, configPath }) => {
2479
+ // src/commands/build/components/Build.tsx
2480
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2481
+ function Build({
2482
+ commandUsed,
2483
+ configPath,
2484
+ exitAfterUploading,
2485
+ onBuildFinishedWebhook,
2486
+ shouldCodeSign
2487
+ }) {
2488
+ var _a, _b, _c, _d, _e, _f;
2382
2489
  const exit = useExit_default();
2383
- const [
2384
- {
2385
- appId,
2386
- appPkg,
2387
- build,
2388
- error,
2389
- hasBuildEverFailed,
2390
- isPreparationComplete,
2391
- preparationStageLabel,
2392
- preparationProgress,
2393
- preparationUploadedSize
2394
- },
2395
- setState
2396
- ] = (0, import_react6.useState)(initalState);
2397
- const onError = (e) => {
2398
- const error2 = e.response ? e.response.data : e;
2399
- logForCI_default(error2);
2400
- setState((prevState) => ({
2401
- ...prevState,
2402
- error: error2
2403
- }));
2404
- };
2490
+ const [state, setState] = (0, import_react6.useState)({ state: "initializing" });
2405
2491
  (0, import_react6.useEffect)(() => {
2406
- let firebaseUnsubscribe;
2407
- runBuild_default({
2408
- onEvent: (eventName, data) => {
2409
- logger_default.debug({ eventName, data }, "Build component: runBuild.onEvent");
2410
- if (eventName === "firebase-subscribed") {
2411
- firebaseUnsubscribe = data.firebaseUnsubscribe;
2412
- } else if (eventName === "progress") {
2413
- setState((prevState) => ({ ...prevState, ...data }));
2414
- }
2415
- },
2492
+ runBuild({
2493
+ configPath,
2494
+ exitAfterUploading,
2495
+ onBuildFinishedWebhook,
2416
2496
  shouldCodeSign,
2417
- configPath
2418
- }).catch(onError);
2497
+ updateState
2498
+ }).catch((e) => {
2499
+ const error = e.response ? e.response.data : e;
2500
+ logForCI_default(error);
2501
+ updateState({ state: "error", error });
2502
+ });
2419
2503
  return () => {
2420
- if (firebaseUnsubscribe) {
2421
- firebaseUnsubscribe();
2422
- }
2504
+ var _a2;
2505
+ return (_a2 = state.onUnmount) == null ? void 0 : _a2.call(state);
2423
2506
  };
2424
- }, [commandUsed, shouldCodeSign, configPath]);
2425
- (0, import_react6.useEffect)(() => {
2426
- if (hasBuildEverFailed && shouldExitOnBuildFailure_default(build)) {
2507
+ }, []);
2508
+ function updateState(changes) {
2509
+ setState(
2510
+ (previousState) => ({ ...previousState, ...changes })
2511
+ );
2512
+ if (changes.state === "exit-after-uploading") {
2513
+ setTimeout(() => exit(), 10);
2514
+ }
2515
+ if (changes.state === "build-failed") {
2427
2516
  setTimeout(() => exit(new Error("Build has failed")), 10);
2428
2517
  }
2429
- }, [build, exit, hasBuildEverFailed]);
2430
- if (error) {
2431
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ErrorDisplay_default, { commandUsed, error });
2432
2518
  }
2433
- if (!appPkg) {
2434
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InitialLoadingState_default, {});
2435
- }
2436
- const onBuildFailure = () => {
2437
- if (hasBuildEverFailed) {
2438
- return;
2439
- }
2440
- setState((prevState) => ({
2441
- ...prevState,
2442
- hasBuildEverFailed: true
2443
- }));
2444
- };
2445
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2446
- MainLayout_default,
2447
- {
2448
- appId,
2449
- appName: build && build.appName || appPkg.name,
2450
- appVersion: build && build.appVersion || appPkg.version,
2451
- build,
2452
- commandUsed,
2453
- hasBuildEverFailed,
2454
- children: isPreparationComplete ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BuildProgress_default, { build, onBuildFailure }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2455
- Preparation_default,
2519
+ switch (state.state) {
2520
+ case "build-failed":
2521
+ case "building": {
2522
+ if (!state.build) {
2523
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorDisplay, { commandUsed, error: "No build is set" });
2524
+ }
2525
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2526
+ MainLayout_default,
2456
2527
  {
2457
- progressPercentage: preparationProgress,
2458
- stageLabel: preparationStageLabel,
2459
- uploadedSize: preparationUploadedSize
2528
+ appName: state.build.appName || ((_a = state.appPkg) == null ? void 0 : _a.name) || "",
2529
+ appVersion: state.build.appVersion || ((_b = state.appPkg) == null ? void 0 : _b.version) || "",
2530
+ build: state.build,
2531
+ commandUsed,
2532
+ hasBuildEverFailed: state.state === "build-failed",
2533
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(BuildProgress_default, { build: state.build, onBuildFailure: () => {
2534
+ } })
2460
2535
  }
2461
- )
2536
+ );
2462
2537
  }
2463
- );
2464
- };
2465
- Build.propTypes = {
2466
- commandUsed: import_prop_types9.default.string.isRequired,
2467
- shouldCodeSign: import_prop_types9.default.bool,
2468
- configPath: import_prop_types9.default.string
2469
- };
2470
- var Build_default = Build;
2538
+ case "error":
2539
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorDisplay, { commandUsed, error: state.error });
2540
+ case "exit-after-uploading":
2541
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink13.Text, { children: "App is building on ToDesktop. Exit since the --async flag is passed." });
2542
+ case "initializing":
2543
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink13.Text, { children: "Getting app info..." });
2544
+ case "preparing":
2545
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2546
+ MainLayout_default,
2547
+ {
2548
+ appName: ((_c = state.build) == null ? void 0 : _c.appName) || ((_d = state.appPkg) == null ? void 0 : _d.name) || "",
2549
+ appVersion: ((_e = state.build) == null ? void 0 : _e.appVersion) || ((_f = state.appPkg) == null ? void 0 : _f.version) || "",
2550
+ build: state.build,
2551
+ commandUsed,
2552
+ hasBuildEverFailed: false,
2553
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2554
+ Preparation_default,
2555
+ {
2556
+ progressPercentage: state.preparationProgress || 0,
2557
+ stageLabel: state.preparationStageLabel || "Preparing",
2558
+ uploadedSize: state.preparationUploadedSize
2559
+ }
2560
+ )
2561
+ }
2562
+ );
2563
+ default:
2564
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2565
+ ErrorDisplay,
2566
+ {
2567
+ commandUsed,
2568
+ error: new Error(`Unknown state ${state.state}`)
2569
+ }
2570
+ );
2571
+ }
2572
+ }
2471
2573
 
2472
2574
  // src/utilities/checkIfReactIsUsable.ts
2473
2575
  var import_react7 = require("react");
@@ -2483,30 +2585,30 @@ var checkIfReactIsUsable_default = ({ cons = console, proc = process } = {}) =>
2483
2585
  // src/components/LoginHOC.tsx
2484
2586
  var import_ink16 = require("ink");
2485
2587
  var import_react10 = require("react");
2486
- var import_prop_types11 = __toESM(require("prop-types"));
2588
+ var import_prop_types9 = __toESM(require("prop-types"));
2487
2589
  var import_is_ci4 = __toESM(require("is-ci"));
2488
2590
 
2489
2591
  // src/components/Login.tsx
2490
2592
  var import_ink14 = require("ink");
2491
- var import_prop_types10 = __toESM(require("prop-types"));
2593
+ var import_prop_types8 = __toESM(require("prop-types"));
2492
2594
  var import_react9 = require("react");
2493
2595
  var import_react_final_form = require("react-final-form");
2494
2596
 
2495
2597
  // src/components/TextInput.tsx
2496
2598
  var import_ink_text_input = __toESM(require("ink-text-input"));
2497
2599
  var import_react8 = __toESM(require("react"));
2498
- var import_jsx_runtime14 = require("react/jsx-runtime");
2600
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2499
2601
  var TextInput = ({ onBlur, onFocus, ...props }) => {
2500
2602
  import_react8.default.useEffect(() => {
2501
2603
  onFocus();
2502
2604
  return onBlur;
2503
2605
  }, [onFocus, onBlur]);
2504
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink_text_input.default, { ...props, showCursor: true });
2606
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink_text_input.default, { ...props, showCursor: true });
2505
2607
  };
2506
2608
  var TextInput_default = TextInput;
2507
2609
 
2508
2610
  // src/components/Login.tsx
2509
- var import_jsx_runtime15 = require("react/jsx-runtime");
2611
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2510
2612
  var loginFields = [
2511
2613
  {
2512
2614
  name: "email",
@@ -2564,18 +2666,18 @@ var Login = ({ setIsLoggedIn }) => {
2564
2666
  }
2565
2667
  };
2566
2668
  if (error)
2567
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ErrorDisplay_default, { error });
2568
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2569
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { children: "You are not currently logged in." }),
2570
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { children: "Log in below:" }),
2571
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { marginBottom: 1 }),
2572
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_final_form.Form, { onSubmit: onSubmitLogin, children: ({ handleSubmit, validating }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { flexDirection: "column", children: loginFields.map(
2573
- ({ name, label, placeholder, validate, Input, mask }, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_final_form.Field, { name, validate, children: ({ input, meta }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { flexDirection: "column", children: activeField >= index ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { children: [
2574
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { bold: activeField === index, children: [
2669
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ErrorDisplay, { error });
2670
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2671
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { children: "You are not currently logged in." }),
2672
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { children: "Log in below:" }),
2673
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Box, { marginBottom: 1 }),
2674
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_final_form.Form, { onSubmit: onSubmitLogin, children: ({ handleSubmit, validating }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Box, { flexDirection: "column", children: loginFields.map(
2675
+ ({ name, label, placeholder, validate, Input, mask }, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_final_form.Field, { name, validate, children: ({ input, meta }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Box, { flexDirection: "column", children: activeField >= index ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink14.Box, { children: [
2676
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink14.Text, { bold: activeField === index, children: [
2575
2677
  label,
2576
2678
  ": "
2577
2679
  ] }),
2578
- activeField === index ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2680
+ activeField === index ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2579
2681
  Input,
2580
2682
  {
2581
2683
  ...input,
@@ -2592,35 +2694,35 @@ var Login = ({ setIsLoggedIn }) => {
2592
2694
  }
2593
2695
  }
2594
2696
  }
2595
- ) : input.value && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { children: name === "accessToken" ? input.value.replace(/./gi, "*") : input.value }) || placeholder && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { color: "gray", children: placeholder }),
2596
- meta.invalid && meta.touched && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { marginLeft: 2, marginRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { color: "red", children: "\u2716" }) }),
2597
- meta.error && meta.touched && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Error2, { errorMessage: meta.error })
2697
+ ) : input.value && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { children: name === "accessToken" ? input.value.replace(/./gi, "*") : input.value }) || placeholder && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { color: "gray", children: placeholder }),
2698
+ meta.invalid && meta.touched && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Box, { marginLeft: 2, marginRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { color: "red", children: "\u2716" }) }),
2699
+ meta.error && meta.touched && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Error2, { errorMessage: meta.error })
2598
2700
  ] }) : null }) }, name)
2599
2701
  ) }) }),
2600
- isSubmittingForm && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { children: "Logging in..." }) }),
2601
- failureMessage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Error2, { errorMessage: failureMessage, marginTop: true })
2702
+ isSubmittingForm && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { children: "Logging in..." }) }),
2703
+ failureMessage && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Error2, { errorMessage: failureMessage, marginTop: true })
2602
2704
  ] });
2603
2705
  };
2604
- var Error2 = ({ errorMessage, marginTop }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { marginTop: marginTop ? 1 : 0, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { color: "red", children: errorMessage }) });
2706
+ var Error2 = ({ errorMessage, marginTop }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Box, { marginTop: marginTop ? 1 : 0, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { color: "red", children: errorMessage }) });
2605
2707
  Error2.propTypes = {
2606
- errorMessage: import_prop_types10.default.string.isRequired,
2607
- marginTop: import_prop_types10.default.bool
2708
+ errorMessage: import_prop_types8.default.string.isRequired,
2709
+ marginTop: import_prop_types8.default.bool
2608
2710
  };
2609
2711
  Login.propTypes = {
2610
- setIsLoggedIn: import_prop_types10.default.func.isRequired
2712
+ setIsLoggedIn: import_prop_types8.default.func.isRequired
2611
2713
  };
2612
2714
  var Login_default = Login;
2613
2715
 
2614
2716
  // src/components/LoadingText.tsx
2615
2717
  var import_ink15 = require("ink");
2616
- var import_jsx_runtime16 = require("react/jsx-runtime");
2718
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2617
2719
  var LoadingText = ({ text = "Loading" }) => {
2618
2720
  const { stdout } = (0, import_ink15.useStdout)();
2619
2721
  const stdOutRedirected = !stdout.isTTY;
2620
2722
  if (stdOutRedirected) {
2621
2723
  return null;
2622
2724
  }
2623
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink15.Text, { children: [
2725
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink15.Text, { children: [
2624
2726
  text,
2625
2727
  "..."
2626
2728
  ] });
@@ -2628,7 +2730,7 @@ var LoadingText = ({ text = "Loading" }) => {
2628
2730
  var LoadingText_default = LoadingText;
2629
2731
 
2630
2732
  // src/components/LoginHOC.tsx
2631
- var import_jsx_runtime17 = require("react/jsx-runtime");
2733
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2632
2734
  var LoginHOC = ({ children, isInteractive = true }) => {
2633
2735
  const [isLoggedIn, setIsLoggedIn] = (0, import_react10.useState)(false);
2634
2736
  const [isEffectDone, setEffectDone] = (0, import_react10.useState)(false);
@@ -2697,19 +2799,19 @@ var LoginHOC = ({ children, isInteractive = true }) => {
2697
2799
  isAccessTokenValid();
2698
2800
  }, []);
2699
2801
  if (error && isInteractive) {
2700
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ErrorDisplay_default, { error });
2802
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ErrorDisplay, { error });
2701
2803
  }
2702
2804
  if (!isEffectDone) {
2703
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(LoadingText_default, {});
2805
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(LoadingText_default, {});
2704
2806
  }
2705
2807
  if (!isLoggedIn && isInteractive) {
2706
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Login_default, { setIsLoggedIn });
2808
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Login_default, { setIsLoggedIn });
2707
2809
  }
2708
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, { children });
2810
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children });
2709
2811
  };
2710
2812
  LoginHOC.propTypes = {
2711
- children: import_prop_types11.default.object,
2712
- isInteractive: import_prop_types11.default.bool
2813
+ children: import_prop_types9.default.object,
2814
+ isInteractive: import_prop_types9.default.bool
2713
2815
  };
2714
2816
  var LoginHOC_default = LoginHOC;
2715
2817
 
@@ -2740,41 +2842,44 @@ var ErrorBoundary = class extends import_react11.default.Component {
2740
2842
  };
2741
2843
  var ErrorBoundary_default = ErrorBoundary;
2742
2844
 
2743
- // src/components/OngoingBuildGuard.tsx
2845
+ // src/commands/build/components/OngoingBuildGuard.tsx
2744
2846
  var import_ink20 = require("ink");
2745
2847
  var import_ink_select_input = __toESM(require("ink-select-input"));
2746
- var import_prop_types14 = __toESM(require("prop-types"));
2848
+ var import_prop_types11 = __toESM(require("prop-types"));
2747
2849
  var import_react13 = require("react");
2748
2850
 
2749
2851
  // src/components/CustomSelectInputIndicator.tsx
2750
2852
  var import_ink17 = require("ink");
2751
- var import_jsx_runtime18 = require("react/jsx-runtime");
2853
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2752
2854
  function CustomSelectInputIndicator({
2753
2855
  isSelected,
2754
2856
  ...props
2755
2857
  }) {
2756
2858
  const selectIndicator = process.env.NODE_ENV === "test" || process.platform === "win32" ? ">" : "\u276F";
2757
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Box, { marginRight: 1, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Text, { children: isSelected ? selectIndicator : " " }) });
2859
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Box, { marginRight: 1, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { children: isSelected ? selectIndicator : " " }) });
2758
2860
  }
2759
2861
 
2760
2862
  // src/components/CustomSelectInputItem.tsx
2761
2863
  var import_ink18 = require("ink");
2762
- var import_prop_types12 = __toESM(require("prop-types"));
2763
- var import_jsx_runtime19 = require("react/jsx-runtime");
2764
- var CustomSelectInputItem = ({ isSelected, label, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { bold: isSelected, color: isSelected ? void 0 : "gray", ...props, children: label });
2765
- CustomSelectInputItem.propTypes = {
2766
- isSelected: import_prop_types12.default.bool.isRequired,
2767
- label: import_prop_types12.default.string.isRequired
2768
- };
2769
- var CustomSelectInputItem_default = CustomSelectInputItem;
2864
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2865
+ function CustomSelectInputItem({
2866
+ isSelected,
2867
+ label,
2868
+ ...props
2869
+ }) {
2870
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink18.Text, { bold: isSelected, color: isSelected ? void 0 : "gray", ...props, children: label });
2871
+ }
2770
2872
 
2771
2873
  // src/components/ViewBuild.tsx
2772
2874
  var import_ink19 = require("ink");
2773
- var import_prop_types13 = __toESM(require("prop-types"));
2875
+ var import_prop_types10 = __toESM(require("prop-types"));
2774
2876
  var import_react12 = require("react");
2775
2877
 
2776
2878
  // src/utilities/getLatestBuildId.ts
2777
- var getLatestBuildId_default = async ({ appId, userId }) => {
2879
+ async function getLatestBuildId({
2880
+ appId,
2881
+ userId
2882
+ }) {
2778
2883
  logger_default.debug({ appId }, "getLatestBuildId");
2779
2884
  const appRef = firestore_default.doc(`users/${userId}/applications/${appId}`);
2780
2885
  const appSnapshot = await appRef.get();
@@ -2782,10 +2887,40 @@ var getLatestBuildId_default = async ({ appId, userId }) => {
2782
2887
  throw new Error(`Application with ID of ${appId} doesn't exist.`);
2783
2888
  }
2784
2889
  const buildsResult = await appRef.collection("builds").orderBy("createdAt", "desc").limit(1).get();
2785
- if (buildsResult.empty) {
2786
- return null;
2787
- } else {
2788
- return buildsResult.docs[0].id;
2890
+ return buildsResult.empty ? void 0 : buildsResult.docs[0].id;
2891
+ }
2892
+
2893
+ // src/utilities/subscribeToFirebaseDoc.ts
2894
+ async function subscribeToFirebaseDoc(key, receiver) {
2895
+ return new Promise((resolve4, reject) => {
2896
+ logger_default.debug({ key }, "subscribeToFirebaseDoc");
2897
+ const unsubscribe = firestore_default.doc(key).onSnapshot(
2898
+ (snapshot) => {
2899
+ receiver({
2900
+ snapshot,
2901
+ data: snapshot.exists ? snapshot.data() : void 0
2902
+ });
2903
+ resolve4(unsubscribe);
2904
+ },
2905
+ (err) => {
2906
+ reject(err);
2907
+ }
2908
+ );
2909
+ });
2910
+ }
2911
+
2912
+ // src/utilities/subscribeToBuild.ts
2913
+ var subscribeToBuild_default = async ({ appId, buildId, onDataReceived, userId }) => {
2914
+ try {
2915
+ return await subscribeToFirebaseDoc(
2916
+ `users/${userId}/applications/${appId}/builds/${buildId}`,
2917
+ async ({ data }) => {
2918
+ onDataReceived(data);
2919
+ }
2920
+ );
2921
+ } catch (e) {
2922
+ e.message = `Failed while subscribing to build; ${e.message}`;
2923
+ throw e;
2789
2924
  }
2790
2925
  };
2791
2926
 
@@ -2820,7 +2955,7 @@ var findAppUserId = async (appId) => {
2820
2955
  var findAppUserId_default = findAppUserId;
2821
2956
 
2822
2957
  // src/components/ViewBuild.tsx
2823
- var import_jsx_runtime20 = require("react/jsx-runtime");
2958
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2824
2959
  var ViewBuild = ({ commandUsed, id, configPath }) => {
2825
2960
  const exit = useExit_default();
2826
2961
  const [
@@ -2887,11 +3022,11 @@ var ViewBuild = ({ commandUsed, id, configPath }) => {
2887
3022
  if (id) {
2888
3023
  subscribe(id);
2889
3024
  } else {
2890
- getLatestBuildId_default({ appId: config2.id, userId }).catch(onError).then((latestBuildId) => {
3025
+ getLatestBuildId({ appId: config2.id, userId }).catch(onError).then((latestBuildId) => {
2891
3026
  if (!latestBuildId) {
2892
3027
  setState((previousState) => ({
2893
3028
  ...previousState,
2894
- arbitraryMessageComponent: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { children: "There are no builds yet" })
3029
+ arbitraryMessageComponent: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink19.Text, { children: "There are no builds yet" })
2895
3030
  }));
2896
3031
  return;
2897
3032
  }
@@ -2917,13 +3052,13 @@ var ViewBuild = ({ commandUsed, id, configPath }) => {
2917
3052
  }
2918
3053
  }, [arbitraryMessageComponent, exit]);
2919
3054
  if (error) {
2920
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ErrorDisplay_default, { commandUsed, error });
3055
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorDisplay, { commandUsed, error });
2921
3056
  }
2922
3057
  if (arbitraryMessageComponent) {
2923
3058
  return arbitraryMessageComponent;
2924
3059
  }
2925
3060
  if (isLoading) {
2926
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LoadingText_default, {});
3061
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LoadingText_default, {});
2927
3062
  }
2928
3063
  const onBuildFailure = () => {
2929
3064
  if (hasBuildEverFailed) {
@@ -2934,7 +3069,7 @@ var ViewBuild = ({ commandUsed, id, configPath }) => {
2934
3069
  hasBuildEverFailed: true
2935
3070
  }));
2936
3071
  };
2937
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3072
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2938
3073
  MainLayout_default,
2939
3074
  {
2940
3075
  appId,
@@ -2943,7 +3078,7 @@ var ViewBuild = ({ commandUsed, id, configPath }) => {
2943
3078
  build,
2944
3079
  commandUsed,
2945
3080
  hasBuildEverFailed,
2946
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BuildProgress_default, { build, onBuildFailure })
3081
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(BuildProgress_default, { build, onBuildFailure })
2947
3082
  }
2948
3083
  );
2949
3084
  };
@@ -2961,7 +3096,7 @@ ViewBuild.propTypes = {
2961
3096
  );
2962
3097
  }
2963
3098
  },
2964
- commandUsed: import_prop_types13.default.string.isRequired,
3099
+ commandUsed: import_prop_types10.default.string.isRequired,
2965
3100
  shouldViewLatest: (props, propName, componentName) => {
2966
3101
  if ([props.id, props.shouldViewLatest].filter(Boolean).length !== 1) {
2967
3102
  return new Error(
@@ -2975,13 +3110,13 @@ ViewBuild.propTypes = {
2975
3110
  );
2976
3111
  }
2977
3112
  },
2978
- configPath: import_prop_types13.default.string
3113
+ configPath: import_prop_types10.default.string
2979
3114
  };
2980
3115
  var ViewBuild_default = ViewBuild;
2981
3116
 
2982
- // src/components/OngoingBuildGuard.tsx
3117
+ // src/commands/build/components/OngoingBuildGuard.tsx
2983
3118
  var import_is_ci5 = __toESM(require("is-ci"));
2984
- var import_jsx_runtime21 = require("react/jsx-runtime");
3119
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2985
3120
  var OngoingBuildGuard = ({ children, commandUsed, configPath }) => {
2986
3121
  const { isRawModeSupported } = (0, import_ink20.useStdin)();
2987
3122
  const onInput = useInput_default();
@@ -3038,20 +3173,20 @@ var OngoingBuildGuard = ({ children, commandUsed, configPath }) => {
3038
3173
  });
3039
3174
  }
3040
3175
  if (error) {
3041
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ErrorDisplay_default, { commandUsed, error });
3176
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ErrorDisplay, { commandUsed, error });
3042
3177
  }
3043
3178
  if (isLoading) {
3044
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { children: "..." });
3179
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink20.Text, { children: "..." });
3045
3180
  }
3046
3181
  if (itemChosen) {
3047
3182
  logger_default.debug({ itemChosen }, "OngoingBuildGuard component: item chosen");
3048
3183
  const build = builds.find(Boolean);
3049
3184
  if (itemChosen.value === "view") {
3050
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ViewBuild_default, { commandUsed, id: build.id });
3185
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ViewBuild_default, { commandUsed, id: build.id });
3051
3186
  } else if (itemChosen.value === "cancel") {
3052
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CancelBuild_default, { appId, commandUsed, id: build.id, children });
3187
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CancelBuild_default, { appId, commandUsed, id: build.id, children });
3053
3188
  } else if (itemChosen.value === "concurrent") {
3054
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
3189
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_jsx_runtime20.Fragment, { children });
3055
3190
  } else {
3056
3191
  setTimeout(exit, 10);
3057
3192
  }
@@ -3080,34 +3215,34 @@ var OngoingBuildGuard = ({ children, commandUsed, configPath }) => {
3080
3215
  value: "exit"
3081
3216
  });
3082
3217
  const handleSelect = (itemChosen2) => setState((previousState) => ({ ...previousState, itemChosen: itemChosen2 }));
3083
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
3084
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_ink20.Box, { marginBottom: 1, children: [
3085
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { bold: true, children: multiple ? "There are ongoing builds " : "There is an ongoing build " }),
3086
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_ink20.Text, { children: [
3218
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
3219
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink20.Box, { marginBottom: 1, children: [
3220
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink20.Text, { bold: true, children: multiple ? "There are ongoing builds " : "There is an ongoing build " }),
3221
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink20.Text, { children: [
3087
3222
  "(",
3088
3223
  latestBuild.appName,
3089
3224
  latestBuild.appVersion ? ` v${latestBuild.appVersion}` : "",
3090
3225
  ")"
3091
3226
  ] })
3092
3227
  ] }),
3093
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3228
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3094
3229
  import_ink_select_input.default,
3095
3230
  {
3096
3231
  indicatorComponent: CustomSelectInputIndicator,
3097
- itemComponent: CustomSelectInputItem_default,
3232
+ itemComponent: CustomSelectInputItem,
3098
3233
  items,
3099
3234
  onSelect: handleSelect
3100
3235
  }
3101
3236
  )
3102
3237
  ] });
3103
3238
  } else {
3104
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
3239
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_jsx_runtime20.Fragment, { children });
3105
3240
  }
3106
3241
  };
3107
3242
  OngoingBuildGuard.propTypes = {
3108
- commandUsed: import_prop_types14.default.string.isRequired,
3109
- children: import_prop_types14.default.oneOfType([import_prop_types14.default.array, import_prop_types14.default.object]),
3110
- configPath: import_prop_types14.default.string
3243
+ commandUsed: import_prop_types11.default.string.isRequired,
3244
+ children: import_prop_types11.default.oneOfType([import_prop_types11.default.array, import_prop_types11.default.object]),
3245
+ configPath: import_prop_types11.default.string
3111
3246
  };
3112
3247
  var OngoingBuildGuard_default = OngoingBuildGuard;
3113
3248
 
@@ -3154,92 +3289,97 @@ var useAnalyticsCommand = (command, flags = {}, properties = {}) => {
3154
3289
  return { hasAttemptedTracking };
3155
3290
  };
3156
3291
 
3157
- // src/commands/BuildCommand.tsx
3158
- var import_jsx_runtime22 = require("react/jsx-runtime");
3159
- var BuildCommand = ({
3160
- shouldCodeSign = true,
3161
- configPath = null
3162
- }) => {
3292
+ // src/commands/build/BuildCommand.tsx
3293
+ var import_jsx_runtime21 = require("react/jsx-runtime");
3294
+ function BuildCommand({
3295
+ configPath,
3296
+ exitAfterUploading,
3297
+ onBuildFinishedWebhook,
3298
+ shouldCodeSign
3299
+ }) {
3163
3300
  checkIfReactIsUsable_default();
3164
3301
  useAnalyticsCommand("build", {
3302
+ async: exitAfterUploading,
3165
3303
  codeSign: shouldCodeSign,
3166
- config: configPath
3304
+ config: configPath,
3305
+ webhook: onBuildFinishedWebhook
3167
3306
  });
3168
3307
  const commandUsed = "todesktop build";
3169
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(LoginHOC_default, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(OngoingBuildGuard_default, { configPath, commandUsed, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3170
- Build_default,
3308
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LoginHOC_default, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OngoingBuildGuard_default, { configPath, commandUsed, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3309
+ Build,
3171
3310
  {
3172
3311
  commandUsed,
3173
- shouldCodeSign,
3174
- configPath
3312
+ configPath,
3313
+ exitAfterUploading,
3314
+ onBuildFinishedWebhook,
3315
+ shouldCodeSign
3175
3316
  }
3176
3317
  ) }) }) });
3177
- };
3178
- var BuildCommand_default = BuildCommand;
3318
+ }
3179
3319
 
3180
3320
  // src/components/ViewBuilds.tsx
3181
3321
  var import_ink26 = require("ink");
3182
- var import_prop_types20 = __toESM(require("prop-types"));
3322
+ var import_prop_types17 = __toESM(require("prop-types"));
3183
3323
  var import_react16 = require("react");
3184
3324
 
3185
3325
  // src/components/Table.tsx
3186
3326
  var import_ink24 = require("ink");
3187
- var import_prop_types19 = __toESM(require("prop-types"));
3327
+ var import_prop_types16 = __toESM(require("prop-types"));
3188
3328
 
3189
3329
  // src/components/TableEnd.tsx
3190
3330
  var import_ink21 = require("ink");
3191
- var import_prop_types15 = __toESM(require("prop-types"));
3192
- var import_jsx_runtime23 = require("react/jsx-runtime");
3331
+ var import_prop_types12 = __toESM(require("prop-types"));
3332
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3193
3333
  var TableEnd = ({ keyDetails, ...props }) => {
3194
3334
  let content = "\u2514";
3195
3335
  content += Object.values(keyDetails).map(({ width }) => "\u2500".repeat(width + 2)).join("\u2534");
3196
3336
  content += "\u2518";
3197
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink21.Box, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink21.Text, { children: content }) });
3337
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Box, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { children: content }) });
3198
3338
  };
3199
3339
  TableEnd.propTypes = {
3200
- keyDetails: import_prop_types15.default.object.isRequired
3340
+ keyDetails: import_prop_types12.default.object.isRequired
3201
3341
  };
3202
3342
  var TableEnd_default = TableEnd;
3203
3343
 
3204
3344
  // src/components/TableHead.tsx
3205
3345
  var import_ink22 = require("ink");
3206
- var import_prop_types16 = __toESM(require("prop-types"));
3346
+ var import_prop_types13 = __toESM(require("prop-types"));
3207
3347
  var import_react15 = require("react");
3208
- var import_jsx_runtime24 = require("react/jsx-runtime");
3348
+ var import_jsx_runtime23 = require("react/jsx-runtime");
3209
3349
  var TableHead = ({ keyDetails, ...props }) => {
3210
3350
  let topLine = "\u250C";
3211
3351
  topLine += Object.values(keyDetails).map(({ width }) => "\u2500".repeat(width + 2)).join("\u252C");
3212
3352
  topLine += "\u2510";
3213
3353
  const contentLineElements = Object.values(keyDetails).map(
3214
3354
  ({ key, width }, index) => {
3215
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react15.Fragment, { children: [
3216
- index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { children: " \u2502 " }) : null,
3217
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Box, { width, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { bold: true, children: key }) })
3355
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react15.Fragment, { children: [
3356
+ index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { children: " \u2502 " }) : null,
3357
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Box, { width, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { bold: true, children: key }) })
3218
3358
  ] }, key);
3219
3359
  }
3220
3360
  );
3221
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink22.Box, { flexDirection: "column", ...props, children: [
3222
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { children: topLine }) }),
3223
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink22.Box, { children: [
3224
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { children: "\u2502 " }),
3361
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_ink22.Box, { flexDirection: "column", ...props, children: [
3362
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { children: topLine }) }),
3363
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_ink22.Box, { children: [
3364
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { children: "\u2502 " }),
3225
3365
  contentLineElements,
3226
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { children: " \u2502" })
3366
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { children: " \u2502" })
3227
3367
  ] }) })
3228
3368
  ] });
3229
3369
  };
3230
3370
  TableHead.propTypes = {
3231
- bottomLinePrefix: import_prop_types16.default.string,
3232
- keyDetails: import_prop_types16.default.object.isRequired
3371
+ bottomLinePrefix: import_prop_types13.default.string,
3372
+ keyDetails: import_prop_types13.default.object.isRequired
3233
3373
  };
3234
3374
  var TableHead_default = TableHead;
3235
3375
 
3236
3376
  // src/components/TableBody.tsx
3237
- var import_prop_types18 = __toESM(require("prop-types"));
3377
+ var import_prop_types15 = __toESM(require("prop-types"));
3238
3378
 
3239
3379
  // src/components/TableRow.tsx
3240
3380
  var import_ink23 = require("ink");
3241
- var import_prop_types17 = __toESM(require("prop-types"));
3242
- var import_jsx_runtime25 = require("react/jsx-runtime");
3381
+ var import_prop_types14 = __toESM(require("prop-types"));
3382
+ var import_jsx_runtime24 = require("react/jsx-runtime");
3243
3383
  var TableRow = ({
3244
3384
  data,
3245
3385
  getCellTextProps = ({ props }) => props,
@@ -3249,12 +3389,12 @@ var TableRow = ({
3249
3389
  let topLine = "\u251C";
3250
3390
  topLine += Object.values(keyDetails).map(({ width }) => "\u2500".repeat(width + 2)).join("\u253C");
3251
3391
  topLine += "\u2524";
3252
- const content = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
3253
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Text, { children: "\u2502 " }),
3392
+ const content = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
3393
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { children: "\u2502 " }),
3254
3394
  Object.entries(data).map(([key, value], index) => {
3255
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_ink23.Box, { children: [
3256
- index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Text, { children: " \u2502 " }) : null,
3257
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Box, { width: keyDetails[key].width, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3395
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink23.Box, { children: [
3396
+ index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { children: " \u2502 " }) : null,
3397
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Box, { width: keyDetails[key].width, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3258
3398
  import_ink23.Text,
3259
3399
  {
3260
3400
  ...getCellTextProps({
@@ -3268,25 +3408,25 @@ var TableRow = ({
3268
3408
  ) })
3269
3409
  ] }, key);
3270
3410
  }),
3271
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Text, { children: " \u2502" })
3411
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { children: " \u2502" })
3272
3412
  ] });
3273
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_ink23.Box, { flexDirection: "column", children: [
3274
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Text, { children: topLine }),
3275
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Box, { children: content })
3413
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_jsx_runtime24.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink23.Box, { flexDirection: "column", children: [
3414
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { children: topLine }),
3415
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Box, { children: content })
3276
3416
  ] }) });
3277
3417
  };
3278
3418
  TableRow.propTypes = {
3279
- data: import_prop_types17.default.object.isRequired,
3280
- getCellTextProps: import_prop_types17.default.func,
3281
- keyDetails: import_prop_types17.default.object.isRequired,
3282
- textProps: import_prop_types17.default.object
3419
+ data: import_prop_types14.default.object.isRequired,
3420
+ getCellTextProps: import_prop_types14.default.func,
3421
+ keyDetails: import_prop_types14.default.object.isRequired,
3422
+ textProps: import_prop_types14.default.object
3283
3423
  };
3284
3424
  var TableRow_default = TableRow;
3285
3425
 
3286
3426
  // src/components/TableBody.tsx
3287
- var import_jsx_runtime26 = require("react/jsx-runtime");
3427
+ var import_jsx_runtime25 = require("react/jsx-runtime");
3288
3428
  var TableBody = ({ data, getCellTextProps, keyDetails }) => {
3289
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_jsx_runtime26.Fragment, { children: data.map((rowData, index) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3429
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: data.map((rowData, index) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3290
3430
  TableRow_default,
3291
3431
  {
3292
3432
  data: rowData,
@@ -3310,8 +3450,8 @@ TableBody.propTypes = {
3310
3450
  );
3311
3451
  }
3312
3452
  },
3313
- getCellTextProps: import_prop_types18.default.func,
3314
- keyDetails: import_prop_types18.default.object.isRequired
3453
+ getCellTextProps: import_prop_types15.default.func,
3454
+ keyDetails: import_prop_types15.default.object.isRequired
3315
3455
  };
3316
3456
  var TableBody_default = TableBody;
3317
3457
 
@@ -3331,12 +3471,12 @@ var getKeyDetails_default = (data) => {
3331
3471
  };
3332
3472
 
3333
3473
  // src/components/Table.tsx
3334
- var import_jsx_runtime27 = require("react/jsx-runtime");
3474
+ var import_jsx_runtime26 = require("react/jsx-runtime");
3335
3475
  var Table = ({ data, getCellTextProps }) => {
3336
3476
  const keyDetails = getKeyDetails_default(data);
3337
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_ink24.Box, { flexDirection: "column", children: [
3338
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(TableHead_default, { keyDetails }),
3339
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3477
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_ink24.Box, { flexDirection: "column", children: [
3478
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TableHead_default, { keyDetails }),
3479
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3340
3480
  TableBody_default,
3341
3481
  {
3342
3482
  data,
@@ -3344,7 +3484,7 @@ var Table = ({ data, getCellTextProps }) => {
3344
3484
  keyDetails
3345
3485
  }
3346
3486
  ),
3347
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(TableEnd_default, { keyDetails })
3487
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TableEnd_default, { keyDetails })
3348
3488
  ] });
3349
3489
  };
3350
3490
  Table.propTypes = {
@@ -3361,7 +3501,7 @@ Table.propTypes = {
3361
3501
  );
3362
3502
  }
3363
3503
  },
3364
- getCellTextProps: import_prop_types19.default.func
3504
+ getCellTextProps: import_prop_types16.default.func
3365
3505
  };
3366
3506
  var Table_default = Table;
3367
3507
 
@@ -3410,7 +3550,7 @@ var import_chalk2 = __toESM(require("chalk"));
3410
3550
  var React3 = __toESM(require("react"));
3411
3551
  var import_ink25 = require("ink");
3412
3552
  var import_util = __toESM(require("util"));
3413
- var import_jsx_runtime28 = require("react/jsx-runtime");
3553
+ var import_jsx_runtime27 = require("react/jsx-runtime");
3414
3554
  var supportsColor = import_chalk2.default.stderr.supportsColor;
3415
3555
  var SyntaxHighlight = ({
3416
3556
  object,
@@ -3420,12 +3560,12 @@ var SyntaxHighlight = ({
3420
3560
  const highlightedCode = React3.useMemo(() => {
3421
3561
  return stdout.isTTY ? import_util.default.inspect(object, { colors, depth: 6 }) : JSON.stringify(object, null, 2);
3422
3562
  }, [colors, object, stdout.isTTY]);
3423
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ink25.Text, { wrap: "end", children: highlightedCode });
3563
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ink25.Text, { wrap: "end", children: highlightedCode });
3424
3564
  };
3425
3565
  var SyntaxHighlight_default = SyntaxHighlight;
3426
3566
 
3427
3567
  // src/components/ViewBuilds.tsx
3428
- var import_jsx_runtime29 = require("react/jsx-runtime");
3568
+ var import_jsx_runtime28 = require("react/jsx-runtime");
3429
3569
  var ViewBuilds = ({
3430
3570
  commandUsed,
3431
3571
  configPath,
@@ -3554,10 +3694,10 @@ var ViewBuilds = ({
3554
3694
  }
3555
3695
  }, [exit, isLoading, shouldExitAfterViewingBuilds, isInitialLoadComplete]);
3556
3696
  if (error) {
3557
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ErrorDisplay_default, { commandUsed, error });
3697
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ErrorDisplay, { commandUsed, error });
3558
3698
  }
3559
3699
  if (!isInitialLoadComplete) {
3560
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(LoadingText_default, {});
3700
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(LoadingText_default, {});
3561
3701
  }
3562
3702
  if (builds.length) {
3563
3703
  const formatData = (builds2) => builds2.map((build) => ({
@@ -3586,19 +3726,19 @@ var ViewBuilds = ({
3586
3726
  }
3587
3727
  return result;
3588
3728
  };
3589
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
3590
- format === "table" ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3729
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
3730
+ format === "table" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3591
3731
  Table_default,
3592
3732
  {
3593
3733
  getCellTextProps,
3594
3734
  data: formatData(builds)
3595
3735
  }
3596
3736
  ) : null,
3597
- format === "json" ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(SyntaxHighlight_default, { object: removeAppBuilderLibConfig(builds) }) : null,
3598
- !shouldExitAfterViewingBuilds && builds.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ink26.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ink26.Text, { color: "gray", dimColor: true, children: isLoading ? "Loading more..." : hasMoreToLoad ? `Showing the latest ${builds.length} builds. Press space/down to load more.` : `Showing all (${builds.length}) builds` }) }) : null
3737
+ format === "json" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SyntaxHighlight_default, { object: removeAppBuilderLibConfig(builds) }) : null,
3738
+ !shouldExitAfterViewingBuilds && builds.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ink26.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ink26.Text, { color: "gray", dimColor: true, children: isLoading ? "Loading more..." : hasMoreToLoad ? `Showing the latest ${builds.length} builds. Press space/down to load more.` : `Showing all (${builds.length}) builds` }) }) : null
3599
3739
  ] });
3600
3740
  } else {
3601
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ink26.Text, { children: "There are no builds yet" });
3741
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ink26.Text, { children: "There are no builds yet" });
3602
3742
  }
3603
3743
  };
3604
3744
  var removeAppBuilderLibConfig = (builds) => {
@@ -3616,16 +3756,16 @@ var removeAppBuilderLibConfig = (builds) => {
3616
3756
  });
3617
3757
  };
3618
3758
  ViewBuilds.propTypes = {
3619
- commandUsed: import_prop_types20.default.string.isRequired,
3620
- configPath: import_prop_types20.default.string,
3621
- count: import_prop_types20.default.number,
3622
- format: import_prop_types20.default.string,
3623
- exit: import_prop_types20.default.bool
3759
+ commandUsed: import_prop_types17.default.string.isRequired,
3760
+ configPath: import_prop_types17.default.string,
3761
+ count: import_prop_types17.default.number,
3762
+ format: import_prop_types17.default.string,
3763
+ exit: import_prop_types17.default.bool
3624
3764
  };
3625
3765
  var ViewBuilds_default = ViewBuilds;
3626
3766
 
3627
3767
  // src/commands/BuildsCommand.tsx
3628
- var import_jsx_runtime30 = require("react/jsx-runtime");
3768
+ var import_jsx_runtime29 = require("react/jsx-runtime");
3629
3769
  var BuildsCommand = ({
3630
3770
  id,
3631
3771
  shouldViewLatest,
@@ -3647,10 +3787,10 @@ var BuildsCommand = ({
3647
3787
  let commandUsed = "todesktop builds";
3648
3788
  if (id) {
3649
3789
  commandUsed += " <id>";
3650
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ViewBuild_default, { commandUsed, id, configPath });
3790
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ViewBuild_default, { commandUsed, id, configPath });
3651
3791
  } else if (shouldViewLatest) {
3652
3792
  commandUsed += " --latest";
3653
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3793
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3654
3794
  ViewBuild_default,
3655
3795
  {
3656
3796
  commandUsed,
@@ -3659,7 +3799,7 @@ var BuildsCommand = ({
3659
3799
  }
3660
3800
  );
3661
3801
  } else {
3662
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3802
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3663
3803
  ViewBuilds_default,
3664
3804
  {
3665
3805
  commandUsed,
@@ -3671,14 +3811,14 @@ var BuildsCommand = ({
3671
3811
  );
3672
3812
  }
3673
3813
  };
3674
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(LoginHOC_default, { children: getContents() }) });
3814
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(LoginHOC_default, { children: getContents() }) });
3675
3815
  };
3676
3816
  var BuildsCommand_default = BuildsCommand;
3677
3817
 
3678
3818
  // src/commands/LogoutCommand.tsx
3679
3819
  var import_react17 = require("react");
3680
3820
  var import_ink27 = require("ink");
3681
- var import_jsx_runtime31 = require("react/jsx-runtime");
3821
+ var import_jsx_runtime30 = require("react/jsx-runtime");
3682
3822
  var Logout = () => {
3683
3823
  const exit = useExit_default();
3684
3824
  const { hasAttemptedTracking } = useAnalyticsCommand("logout");
@@ -3689,10 +3829,10 @@ var Logout = () => {
3689
3829
  exit();
3690
3830
  }
3691
3831
  }, [exit, hasAttemptedTracking]);
3692
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ink27.Text, { children: "Log out successful" });
3832
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ink27.Text, { children: "Log out successful" });
3693
3833
  };
3694
3834
  var LogoutWrapper = () => {
3695
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(LoginHOC_default, { isInteractive: false, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Logout, {}) }) });
3835
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(LoginHOC_default, { isInteractive: false, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Logout, {}) }) });
3696
3836
  };
3697
3837
  var LogoutCommand_default = LogoutWrapper;
3698
3838
 
@@ -3703,9 +3843,9 @@ var import_react18 = require("react");
3703
3843
  // src/components/SelectTable.tsx
3704
3844
  var import_ink28 = require("ink");
3705
3845
  var import_ink_select_input2 = __toESM(require("ink-select-input"));
3706
- var import_prop_types21 = __toESM(require("prop-types"));
3707
- var import_jsx_runtime32 = require("react/jsx-runtime");
3708
- var CustomIndicator = (props) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(CustomSelectInputIndicator, { marginTop: 1, ...props });
3846
+ var import_prop_types18 = __toESM(require("prop-types"));
3847
+ var import_jsx_runtime31 = require("react/jsx-runtime");
3848
+ var CustomIndicator = (props) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(CustomSelectInputIndicator, { marginTop: 1, ...props });
3709
3849
  var SelectTable = ({ data, onSelect }) => {
3710
3850
  const keyDetails = getKeyDetails_default(data);
3711
3851
  const getSelectItems = () => {
@@ -3722,7 +3862,7 @@ var SelectTable = ({ data, onSelect }) => {
3722
3862
  index,
3723
3863
  isSelected
3724
3864
  }) => {
3725
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3865
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3726
3866
  TableRow_default,
3727
3867
  {
3728
3868
  data: data[index],
@@ -3732,9 +3872,9 @@ var SelectTable = ({ data, onSelect }) => {
3732
3872
  index
3733
3873
  );
3734
3874
  };
3735
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink28.Box, { flexDirection: "column", children: [
3736
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TableHead_default, { keyDetails, marginLeft: 2 }),
3737
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3875
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_ink28.Box, { flexDirection: "column", children: [
3876
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(TableHead_default, { keyDetails, marginLeft: 2 }),
3877
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3738
3878
  import_ink_select_input2.default,
3739
3879
  {
3740
3880
  indicatorComponent: CustomIndicator,
@@ -3743,7 +3883,7 @@ var SelectTable = ({ data, onSelect }) => {
3743
3883
  onSelect
3744
3884
  }
3745
3885
  ),
3746
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TableEnd_default, { keyDetails, marginLeft: 2 })
3886
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(TableEnd_default, { keyDetails, marginLeft: 2 })
3747
3887
  ] });
3748
3888
  };
3749
3889
  SelectTable.propTypes = {
@@ -3760,12 +3900,49 @@ SelectTable.propTypes = {
3760
3900
  );
3761
3901
  }
3762
3902
  },
3763
- onSelect: import_prop_types21.default.func
3903
+ onSelect: import_prop_types18.default.func
3764
3904
  };
3765
3905
  var SelectTable_default = SelectTable;
3766
3906
 
3907
+ // src/utilities/getBuildById.ts
3908
+ async function getBuildById({
3909
+ appId,
3910
+ buildId,
3911
+ userId
3912
+ }) {
3913
+ logger_default.debug({ appId, buildId }, "getBuildById");
3914
+ const snapshot = await firestore_default.doc(`users/${userId}/applications/${appId}/builds/${buildId}`).get();
3915
+ if (!snapshot.exists) {
3916
+ return null;
3917
+ }
3918
+ return snapshot.data();
3919
+ }
3920
+
3921
+ // src/utilities/getLatestReleasedBuild.ts
3922
+ async function getLatestReleasedBuild({
3923
+ appId,
3924
+ userId
3925
+ }) {
3926
+ var _a, _b;
3927
+ const appRef = firestore_default.doc(`users/${userId}/applications/${appId}`);
3928
+ const appSnapshot = await appRef.get();
3929
+ const app = appSnapshot.exists ? appSnapshot.data() : void 0;
3930
+ if (!app) {
3931
+ throw new Error(`Application with ID of ${appId} doesn't exist.`);
3932
+ }
3933
+ if ((_a = app.meta) == null ? void 0 : _a.latestReleaseBuildId) {
3934
+ return await getBuildById({
3935
+ buildId: (_b = app.meta) == null ? void 0 : _b.latestReleaseBuildId,
3936
+ appId,
3937
+ userId
3938
+ }) || void 0;
3939
+ }
3940
+ const buildsResult = await appRef.collection("builds").orderBy("releasedAt", "desc").limit(1).get();
3941
+ return buildsResult.empty ? void 0 : buildsResult.docs[0];
3942
+ }
3943
+
3767
3944
  // src/components/BuildPicker.tsx
3768
- var import_jsx_runtime33 = require("react/jsx-runtime");
3945
+ var import_jsx_runtime32 = require("react/jsx-runtime");
3769
3946
  function BuildPicker({
3770
3947
  buildFilter,
3771
3948
  buildId,
@@ -3773,13 +3950,14 @@ function BuildPicker({
3773
3950
  commandUsed,
3774
3951
  configPath,
3775
3952
  loadData = loadPickerData,
3953
+ noBuildsDescription = null,
3776
3954
  question = "Which build would you like to test?"
3777
3955
  }) {
3778
3956
  const exit = useExit_default();
3779
3957
  const onInput = useInput_default();
3780
3958
  const isRealIdPassed = buildId && buildId !== "latest";
3781
3959
  const [state, setState] = (0, import_react18.useState)({
3782
- selectedBuildId: isRealIdPassed ? buildId : null,
3960
+ selectedBuildId: isRealIdPassed ? buildId : void 0,
3783
3961
  state: isRealIdPassed ? "selected" : "loading"
3784
3962
  });
3785
3963
  (0, import_react18.useEffect)(() => {
@@ -3789,44 +3967,49 @@ function BuildPicker({
3789
3967
  configPath,
3790
3968
  state: state.state,
3791
3969
  updateState
3792
- });
3970
+ }).catch(logger_default.error);
3793
3971
  }, []);
3794
- onInput((input, key) => {
3972
+ onInput((_, key) => {
3795
3973
  if (key.escape && ["show-builds", "loading"].includes(state.state)) {
3796
3974
  exit();
3797
3975
  }
3798
3976
  });
3799
3977
  function updateState(changes) {
3800
- setState((previousState) => ({ ...previousState, ...changes }));
3978
+ setState(
3979
+ (previousState) => ({ ...previousState, ...changes })
3980
+ );
3981
+ if (changes.state && ["error", "no-builds"].includes(changes.state)) {
3982
+ setTimeout(() => exit(), 10);
3983
+ }
3801
3984
  }
3802
3985
  function onSelect(item) {
3803
3986
  updateState({ selectedBuildId: item.value.ID, state: "selected" });
3804
3987
  }
3805
3988
  switch (state.state) {
3806
3989
  case "error":
3807
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ErrorDisplay_default, { commandUsed, error: state.error });
3990
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ErrorDisplay, { commandUsed, error: state.error });
3808
3991
  case "loading":
3809
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(LoadingText_default, {});
3992
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(LoadingText_default, {});
3810
3993
  case "no-builds":
3811
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ink29.Box, { children: [
3812
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink29.Text, { children: "No eligible builds found " }),
3813
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink29.Text, { dimColor: true, children: "(candidates for smoke-test must be built with @todesktop/runtime@1.2.1 or later)" })
3994
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink29.Box, { children: [
3995
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Text, { children: "No eligible builds found " }),
3996
+ noBuildsDescription && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Text, { dimColor: true, children: noBuildsDescription })
3814
3997
  ] });
3815
3998
  case "selected":
3816
3999
  return children(state.selectedBuildId);
3817
4000
  case "show-builds":
3818
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
3819
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink29.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink29.Text, { children: question }) }),
3820
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(SelectTable_default, { data: state.builds, onSelect }),
3821
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink29.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ink29.Text, { dimColor: true, children: [
4001
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
4002
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Text, { children: question }) }),
4003
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(SelectTable_default, { data: state.builds, onSelect }),
4004
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink29.Text, { dimColor: true, children: [
3822
4005
  "Showing the latest ",
3823
4006
  state.builds.length,
3824
4007
  " unreleased successful builds"
3825
4008
  ] }) })
3826
4009
  ] });
3827
4010
  default:
3828
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3829
- ErrorDisplay_default,
4011
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4012
+ ErrorDisplay,
3830
4013
  {
3831
4014
  commandUsed,
3832
4015
  error: new Error(`Unknown state ${state.state}`)
@@ -3859,8 +4042,8 @@ async function loadPickerData({
3859
4042
  state: builds.length > 0 ? "show-builds" : "no-builds"
3860
4043
  });
3861
4044
  }
3862
- } catch (error) {
3863
- updateState({ error, state: "error" });
4045
+ } catch (e) {
4046
+ updateState({ error: CliError.from(e), state: "error" });
3864
4047
  }
3865
4048
  }
3866
4049
  async function getLatestBuild({
@@ -3868,7 +4051,7 @@ async function getLatestBuild({
3868
4051
  }) {
3869
4052
  const { id: appId } = getProjectConfig(configPath).config;
3870
4053
  const { id: userId } = await findAppUserId_default(appId);
3871
- return getLatestBuildId_default({ appId, userId });
4054
+ return getLatestBuildId({ appId, userId });
3872
4055
  }
3873
4056
  async function getBuildItems({
3874
4057
  buildFilter = () => true,
@@ -3876,13 +4059,14 @@ async function getBuildItems({
3876
4059
  }) {
3877
4060
  const { id: appId } = getProjectConfig(configPath).config;
3878
4061
  const { id: userId, label: userName } = await findAppUserId_default(appId);
4062
+ const latestReleasedBuild = await getLatestReleasedBuild({ appId, userId });
3879
4063
  const rawBuilds = await getBuilds({
3880
4064
  addWhereClauses: (query) => query.where("status", "==", "succeeded"),
3881
4065
  appId,
3882
4066
  limit: 50,
3883
4067
  userId
3884
4068
  });
3885
- return rawBuilds.filter(buildFilter).slice(0, 5).map((build) => ({
4069
+ return rawBuilds.filter((build) => buildFilter({ build, latestReleasedBuild })).slice(0, 5).map((build) => ({
3886
4070
  ID: build.id,
3887
4071
  Version: build.appVersion || "unknown",
3888
4072
  "Creation date": getRelativeDateFromDateString_default(build.createdAt),
@@ -3890,47 +4074,9 @@ async function getBuildItems({
3890
4074
  }));
3891
4075
  }
3892
4076
 
3893
- // src/commands/release/components/ReleaseBuild.tsx
3894
- var import_ink31 = require("ink");
4077
+ // src/commands/release/components/ReleaseBuildLogic.tsx
3895
4078
  var import_react19 = require("react");
3896
4079
 
3897
- // src/components/BuildError.tsx
3898
- var import_ink30 = require("ink");
3899
- var import_ink_link4 = __toESM(require("ink-link"));
3900
- var import_jsx_runtime34 = require("react/jsx-runtime");
3901
- function BuildError({
3902
- build,
3903
- description = "Can't release",
3904
- message: message2
3905
- }) {
3906
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_ink30.Box, { flexDirection: "column", children: [
3907
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_ink30.Text, { bold: true, color: "red", children: [
3908
- description,
3909
- " ",
3910
- build.appName,
3911
- " v",
3912
- build.appVersion
3913
- ] }),
3914
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink30.Box, { marginBottom: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink30.Text, { children: message2 }) }),
3915
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink30.Text, { bold: true, children: "See web UI for more information: " }),
3916
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink30.Text, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink_link4.default, { fallback: false, url: build.url, children: build.url }) })
3917
- ] });
3918
- }
3919
-
3920
- // src/utilities/getBuildById.ts
3921
- async function getBuildById({
3922
- appId,
3923
- buildId,
3924
- userId
3925
- }) {
3926
- logger_default.debug({ appId, buildId }, "getBuildById");
3927
- const snapshot = await firestore_default.doc(`users/${userId}/applications/${appId}/builds/${buildId}`).get();
3928
- if (!snapshot.exists) {
3929
- return null;
3930
- }
3931
- return snapshot.data();
3932
- }
3933
-
3934
4080
  // src/utilities/getBuildAttributes.ts
3935
4081
  async function getBuildAttributes({
3936
4082
  buildId,
@@ -3939,7 +4085,9 @@ async function getBuildAttributes({
3939
4085
  const {
3940
4086
  id: appId,
3941
4087
  nodeVersion,
3942
- npmVersion
4088
+ npmVersion,
4089
+ pnpmVersion,
4090
+ yarnVersion
3943
4091
  } = getProjectConfig(configPath).config;
3944
4092
  const { id: userId } = await findAppUserId_default(appId);
3945
4093
  const { uid: contextUserId } = currentUser();
@@ -3951,6 +4099,8 @@ async function getBuildAttributes({
3951
4099
  contextUserId,
3952
4100
  nodeVersion,
3953
4101
  npmVersion,
4102
+ pnpmVersion,
4103
+ yarnVersion,
3954
4104
  userId
3955
4105
  };
3956
4106
  }
@@ -3963,151 +4113,150 @@ async function fetchBuild({
3963
4113
  if (!build) {
3964
4114
  throw new Error(`No such build ${buildId} for application ${appId}`);
3965
4115
  }
3966
- return build;
4116
+ return build;
4117
+ }
4118
+
4119
+ // src/commands/release/utilities/releaseBuild.ts
4120
+ async function releaseBuild({
4121
+ appId,
4122
+ buildId
4123
+ }) {
4124
+ try {
4125
+ await getCallableFirebaseFunction_default("releaseBuild")({ appId, buildId });
4126
+ } catch (e) {
4127
+ if (isFirebaseFunctionError(e) && (e.code === "failed-precondition" || e.code === "not-found")) {
4128
+ throw CliError.from(e, "build-error");
4129
+ } else {
4130
+ throw new CliError("Unexpected internal error while releasing build");
4131
+ }
4132
+ }
4133
+ }
4134
+ function isFirebaseFunctionError(error) {
4135
+ return Boolean(
4136
+ typeof error === "object" && error && "code" in error && error.code === "string"
4137
+ );
4138
+ }
4139
+
4140
+ // src/commands/release/components/ReleaseBuildView.tsx
4141
+ var import_ink31 = require("ink");
4142
+
4143
+ // src/components/BuildError.tsx
4144
+ var import_ink30 = require("ink");
4145
+ var import_ink_link4 = __toESM(require("ink-link"));
4146
+ var import_jsx_runtime33 = require("react/jsx-runtime");
4147
+ function BuildError({
4148
+ build,
4149
+ description = "Can't release",
4150
+ message: message2
4151
+ }) {
4152
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ink30.Box, { flexDirection: "column", children: [
4153
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ink30.Text, { bold: true, color: "red", children: [
4154
+ description,
4155
+ " ",
4156
+ build.appName,
4157
+ " v",
4158
+ build.appVersion
4159
+ ] }),
4160
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink30.Box, { marginBottom: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink30.Text, { children: message2 }) }),
4161
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink30.Text, { bold: true, children: "See web UI for more information: " }),
4162
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink30.Text, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink_link4.default, { fallback: false, url: build.url, children: build.url }) })
4163
+ ] });
4164
+ }
4165
+
4166
+ // src/commands/release/components/ReleaseBuildView.tsx
4167
+ var import_jsx_runtime34 = require("react/jsx-runtime");
4168
+ function ReleaseBuildView({
4169
+ commandUsed,
4170
+ state
4171
+ }) {
4172
+ var _a, _b;
4173
+ switch (state.state) {
4174
+ case "build-error": {
4175
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4176
+ BuildError,
4177
+ {
4178
+ build: state.build,
4179
+ description: "Can't release build",
4180
+ message: ((_a = state.error) == null ? void 0 : _a.message) || "Internal build error"
4181
+ }
4182
+ );
4183
+ }
4184
+ case "complete":
4185
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink31.Text, { bold: true, color: "greenBright", children: "Released!" });
4186
+ case "error":
4187
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4188
+ ErrorDisplay,
4189
+ {
4190
+ commandUsed,
4191
+ error: { stack: (_b = state.error) == null ? void 0 : _b.stack }
4192
+ }
4193
+ );
4194
+ case "loading":
4195
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LoadingText_default, {});
4196
+ case "releasing":
4197
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LoadingText_default, { text: "Releasing" });
4198
+ default:
4199
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4200
+ ErrorDisplay,
4201
+ {
4202
+ commandUsed,
4203
+ error: new Error(`Unknown state ${state}`)
4204
+ }
4205
+ );
4206
+ }
3967
4207
  }
3968
4208
 
3969
- // src/commands/release/components/ReleaseBuild.tsx
4209
+ // src/commands/release/components/ReleaseBuildLogic.tsx
3970
4210
  var import_jsx_runtime35 = require("react/jsx-runtime");
3971
- function ReleaseBuild({
4211
+ function ReleaseBuildLogic({
4212
+ buildId,
3972
4213
  commandUsed,
3973
- id,
3974
4214
  configPath
3975
4215
  }) {
3976
4216
  const exit = useExit_default();
3977
- const [
3978
- {
3979
- appId,
3980
- arbitraryMessageComponent,
3981
- build,
3982
- error,
3983
- hasBeenValidatedSuccessfully,
3984
- isComplete,
3985
- isReleasing
3986
- },
3987
- setState
3988
- ] = (0, import_react19.useState)({
3989
- appId: null,
3990
- build: null,
3991
- error: null,
3992
- hasBeenValidatedSuccessfully: false,
3993
- isComplete: false,
3994
- isReleasing: false,
3995
- arbitraryMessageComponent: null
3996
- });
3997
- const onError = (e) => {
3998
- const error2 = e.response ? e.response.data : e;
3999
- logForCI_default(error2);
4000
- setState((prevState) => ({
4001
- ...prevState,
4002
- error: error2
4003
- }));
4004
- };
4005
- (0, import_react19.useEffect)(() => {
4006
- if (!build) {
4007
- getBuildAttributes({ buildId: id, configPath }).then(({ appId: appId2, build: build2 }) => {
4008
- setState((previousState) => ({ ...previousState, appId: appId2, build: build2 }));
4009
- }).catch(onError);
4010
- }
4011
- }, [build, configPath, id]);
4012
- (0, import_react19.useEffect)(() => {
4013
- if (!build || hasBeenValidatedSuccessfully || arbitraryMessageComponent) {
4014
- return;
4015
- }
4016
- let validationMessage;
4017
- if (build.releasedAt) {
4018
- validationMessage = `It has already been released.`;
4019
- } else if (build.status !== "succeeded") {
4020
- validationMessage = `The build must have completed successfully. Actual build status: ${build.status}`;
4021
- }
4022
- if (validationMessage) {
4023
- setState((previousState) => ({
4024
- ...previousState,
4025
- arbitraryMessageComponent: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(BuildError, { build, message: validationMessage }),
4026
- hasBeenValidatedSuccessfully: false
4027
- }));
4028
- } else {
4029
- setState((previousState) => ({
4030
- ...previousState,
4031
- hasBeenValidatedSuccessfully: true
4032
- }));
4033
- }
4034
- }, [arbitraryMessageComponent, build, hasBeenValidatedSuccessfully]);
4035
- (0, import_react19.useEffect)(() => {
4036
- if (!hasBeenValidatedSuccessfully || isReleasing || isComplete) {
4037
- return;
4038
- }
4039
- setState((previousState) => ({
4040
- ...previousState,
4041
- isReleasing: true
4042
- }));
4043
- getCallableFirebaseFunction_default("releaseBuild")({
4044
- appId,
4045
- buildId: build.id
4046
- }).then(() => {
4047
- logForCI_default("Released!");
4048
- setState((previousState) => ({
4049
- ...previousState,
4050
- isReleasing: false,
4051
- isComplete: true
4052
- }));
4053
- }).catch((e) => {
4054
- if (["failed-precondition", "not-found"].includes(e.code)) {
4055
- setState((previousState) => ({
4056
- ...previousState,
4057
- arbitraryMessageComponent: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4058
- BuildError,
4059
- {
4060
- build,
4061
- message: e instanceof Error ? e.message : JSON.stringify(e)
4062
- }
4063
- ),
4064
- isReleasing: false
4065
- }));
4066
- } else {
4067
- onError(new Error("Unexpected internal error while releasing build"));
4068
- }
4069
- });
4070
- }, [appId, build, hasBeenValidatedSuccessfully, id, isComplete, isReleasing]);
4217
+ const [state, setState] = (0, import_react19.useState)({ state: "loading" });
4071
4218
  (0, import_react19.useEffect)(() => {
4072
- if (isComplete) {
4073
- setTimeout(exit, 10);
4074
- return;
4075
- }
4076
- if (arbitraryMessageComponent) {
4077
- setTimeout(() => exit(new Error("Validation failed")), 10);
4219
+ releaseBuildWorkflow({ configPath, buildId, updateState }).catch(
4220
+ logger_default.error
4221
+ );
4222
+ }, []);
4223
+ function updateState(changes) {
4224
+ setState(
4225
+ (previousState) => ({ ...previousState, ...changes })
4226
+ );
4227
+ if (changes.state && ["build-error", "complete"].includes(changes.state)) {
4228
+ setTimeout(() => exit(), 10);
4078
4229
  }
4079
- }, [arbitraryMessageComponent, exit, isComplete]);
4080
- if (error) {
4081
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ErrorDisplay_default, { commandUsed, error });
4082
- }
4083
- if (arbitraryMessageComponent) {
4084
- return arbitraryMessageComponent;
4085
- }
4086
- if (isReleasing) {
4087
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ink31.Text, { children: "Releasing..." });
4088
4230
  }
4089
- if (isComplete) {
4090
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ink31.Text, { bold: true, color: "greenBright", children: "Released!" });
4231
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReleaseBuildView, { commandUsed, state });
4232
+ }
4233
+ async function releaseBuildWorkflow({
4234
+ buildId,
4235
+ configPath,
4236
+ updateState
4237
+ }) {
4238
+ try {
4239
+ const { appId, build } = await getBuildAttributes({
4240
+ buildId,
4241
+ configPath
4242
+ });
4243
+ updateState({ build });
4244
+ updateState({ state: "releasing" });
4245
+ await releaseBuild({ appId, buildId });
4246
+ logForCI_default("Released!");
4247
+ updateState({ state: "complete" });
4248
+ } catch (e) {
4249
+ const error = CliError.from(e);
4250
+ updateState({ error, state: error.type });
4091
4251
  }
4092
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ink31.Text, { children: "..." });
4093
4252
  }
4094
4253
 
4095
4254
  // src/commands/release/components/ReleaseConfirmation.tsx
4096
- var import_ink33 = require("ink");
4097
- var import_react20 = require("react");
4255
+ var import_ink34 = require("ink");
4256
+ var import_react21 = require("react");
4098
4257
 
4099
4258
  // src/commands/smoke-test/utilities/build.ts
4100
4259
  var import_semver = __toESM(require("semver"));
4101
-
4102
- // src/commands/smoke-test/utilities/SmokeError.ts
4103
- var SmokeError = class extends Error {
4104
- constructor(message2, { cause, type = "error" } = {}) {
4105
- super(message2, { cause });
4106
- this.type = type;
4107
- }
4108
- };
4109
-
4110
- // src/commands/smoke-test/utilities/build.ts
4111
4260
  var MIN_RUNTIME_VERSION = "1.2.1-1";
4112
4261
  function isBuildTestable(build) {
4113
4262
  try {
@@ -4173,17 +4322,17 @@ function makeProgress(build) {
4173
4322
  }
4174
4323
  function validateBuild(build) {
4175
4324
  if (build.status !== "succeeded") {
4176
- throw new SmokeError(
4325
+ throw new CliError(
4177
4326
  `The build must be completed successfully. Actual build status: ${build.status}`
4178
4327
  );
4179
4328
  }
4180
4329
  if (!build.todesktopRuntimeVersionSpecified) {
4181
- throw new SmokeError(
4330
+ throw new CliError(
4182
4331
  "The build has no todesktopRuntimeVersionSpecified set. Please make sure @todesktop/runtime is installed as a dependency and make a new build"
4183
4332
  );
4184
4333
  }
4185
4334
  if (!isRuntimeVerRangeAllowed(build.todesktopRuntimeVersionSpecified)) {
4186
- throw new SmokeError(
4335
+ throw new CliError(
4187
4336
  `This build should have @todesktop/runtime version ${MIN_RUNTIME_VERSION} or later to run smoke tests.`
4188
4337
  );
4189
4338
  }
@@ -4197,10 +4346,115 @@ function isRuntimeVerRangeAllowed(runtimeVerRange) {
4197
4346
  });
4198
4347
  }
4199
4348
 
4200
- // src/commands/release/components/YesNoConfirmation.tsx
4349
+ // src/commands/release/utilities/analyzeRelease.ts
4350
+ var import_semver2 = __toESM(require("semver"));
4351
+ var MIN_RUNTIME_VERSION_FOR_DOWNGRADE = "1.4.1-1";
4352
+ function analyzeRelease({
4353
+ build,
4354
+ latestReleasedBuild
4355
+ }) {
4356
+ const result = {
4357
+ downgradeAllowed: Boolean(
4358
+ (latestReleasedBuild == null ? void 0 : latestReleasedBuild.todesktopRuntimeVersionUsed) && import_semver2.default.gte(
4359
+ latestReleasedBuild.todesktopRuntimeVersionUsed,
4360
+ MIN_RUNTIME_VERSION_FOR_DOWNGRADE
4361
+ )
4362
+ ),
4363
+ error: "",
4364
+ isAlreadyReleased: Boolean(build.releasedAt),
4365
+ isAlreadyTheLatest: build.id === (latestReleasedBuild == null ? void 0 : latestReleasedBuild.id),
4366
+ isOlderThanLatest: Boolean(
4367
+ latestReleasedBuild && latestReleasedBuild.appVersion && build.appVersion && import_semver2.default.gt(latestReleasedBuild.appVersion, build.appVersion)
4368
+ ),
4369
+ isReleasable: false,
4370
+ isSucceeded: build.status === "succeeded",
4371
+ isTheSameVersionAsLatest: Boolean(
4372
+ build.appVersion && build.appVersion === (latestReleasedBuild == null ? void 0 : latestReleasedBuild.appVersion)
4373
+ )
4374
+ };
4375
+ if (!result.isSucceeded) {
4376
+ return {
4377
+ ...result,
4378
+ error: `The build must have completed successfully. Actual build status: ${build.status}.`
4379
+ };
4380
+ }
4381
+ if (result.isAlreadyTheLatest) {
4382
+ return {
4383
+ ...result,
4384
+ error: "This build is already the latest released build."
4385
+ };
4386
+ }
4387
+ if (result.isTheSameVersionAsLatest) {
4388
+ return {
4389
+ ...result,
4390
+ error: `This build has the same version as the latest released build (build ID: ${latestReleasedBuild == null ? void 0 : latestReleasedBuild.id}, version: ${build.appVersion}).`
4391
+ };
4392
+ }
4393
+ if (!result.downgradeAllowed && result.isAlreadyReleased) {
4394
+ return { ...result, error: "This build has already been released." };
4395
+ }
4396
+ if (!result.downgradeAllowed && result.isOlderThanLatest && latestReleasedBuild) {
4397
+ return {
4398
+ ...result,
4399
+ error: `App version (${build.appVersion}) is not newer than latest released version (${latestReleasedBuild.appVersion}) (build ID: ${latestReleasedBuild.id}).`
4400
+ };
4401
+ }
4402
+ return { ...result, isReleasable: true };
4403
+ }
4404
+
4405
+ // src/commands/release/components/DowngradeConfirmation.tsx
4201
4406
  var import_ink32 = require("ink");
4202
- var import_ink_select_input3 = __toESM(require("ink-select-input"));
4407
+ var import_ink_text_input2 = __toESM(require("ink-text-input"));
4408
+ var import_react20 = require("react");
4203
4409
  var import_jsx_runtime36 = require("react/jsx-runtime");
4410
+ function DowngradeConfirmation({
4411
+ build,
4412
+ latestReleasedBuild,
4413
+ confirmDowngrade: confirmDowngrade2
4414
+ }) {
4415
+ const [confirmText, setConfirmText] = (0, import_react20.useState)("");
4416
+ const [showError, setShowError] = (0, import_react20.useState)(false);
4417
+ const buildVersion = build.appVersion;
4418
+ const latestVersion2 = latestReleasedBuild.appVersion;
4419
+ function onType(text) {
4420
+ setShowError(false);
4421
+ setConfirmText(text);
4422
+ }
4423
+ function onSubmit(text) {
4424
+ if (text.trim() === buildVersion) {
4425
+ confirmDowngrade2();
4426
+ } else {
4427
+ setShowError(true);
4428
+ }
4429
+ }
4430
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ink32.Box, { flexDirection: "column", children: [
4431
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Box, { borderColor: "red", borderStyle: "double", paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ink32.Text, { children: [
4432
+ "You're trying to release the build ",
4433
+ buildVersion,
4434
+ ", which is older than the latest published version ",
4435
+ latestVersion2,
4436
+ ". This may cause issues if your customers are using a version of your desktop app that contains a version of @todesktop/runtime that is earlier than v",
4437
+ MIN_RUNTIME_VERSION_FOR_DOWNGRADE,
4438
+ "."
4439
+ ] }) }),
4440
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { children: "Are you sure that you wish to release this build?" }) }),
4441
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ink32.Text, { children: [
4442
+ "To continue, type the text ",
4443
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { bold: true, children: buildVersion }),
4444
+ " below to confirm:"
4445
+ ] }) }),
4446
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ink32.Box, { children: [
4447
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Box, { marginRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { children: "Confirm new version:" }) }),
4448
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink_text_input2.default, { value: confirmText, onChange: onType, onSubmit })
4449
+ ] }),
4450
+ showError && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { color: "red", children: "You've entered the wrong version" }) })
4451
+ ] });
4452
+ }
4453
+
4454
+ // src/commands/release/components/YesNoConfirmation.tsx
4455
+ var import_ink33 = require("ink");
4456
+ var import_ink_select_input3 = __toESM(require("ink-select-input"));
4457
+ var import_jsx_runtime37 = require("react/jsx-runtime");
4204
4458
  function YesNoConfirmation({
4205
4459
  onYes,
4206
4460
  title,
@@ -4218,107 +4472,175 @@ function YesNoConfirmation({
4218
4472
  setTimeout(() => exit(), 10);
4219
4473
  }
4220
4474
  }
4221
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ink32.Box, { flexDirection: "column", children: [
4222
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Box, { children: typeof title === "string" ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { children: title }) : title }),
4223
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Box, { marginBottom: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4475
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ink33.Box, { flexDirection: "column", children: [
4476
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ink33.Box, { children: typeof title === "string" ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ink33.Text, { children: title }) : title }),
4477
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ink33.Box, { marginBottom: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4224
4478
  import_ink_select_input3.default,
4225
4479
  {
4226
4480
  indicatorComponent: CustomSelectInputIndicator,
4227
4481
  initialIndex: 1,
4228
- itemComponent: CustomSelectInputItem_default,
4482
+ itemComponent: CustomSelectInputItem,
4229
4483
  items,
4230
4484
  onSelect
4231
4485
  }
4232
4486
  ) }),
4233
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { dimColor: true, children: footer })
4487
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ink33.Text, { dimColor: true, children: footer })
4234
4488
  ] });
4235
4489
  }
4236
4490
 
4237
4491
  // src/commands/release/components/ReleaseConfirmation.tsx
4238
- var import_jsx_runtime37 = require("react/jsx-runtime");
4492
+ var import_jsx_runtime38 = require("react/jsx-runtime");
4239
4493
  function ReleaseConfirmation({
4240
4494
  buildId,
4241
4495
  children,
4242
4496
  configPath,
4243
4497
  disabled = false,
4244
- loadBuild = getBuildAttributes
4498
+ loadData = loadBuildsData
4245
4499
  }) {
4246
- const [state, setState] = (0, import_react20.useState)({
4500
+ var _a;
4501
+ const [state, setState] = (0, import_react21.useState)({
4247
4502
  state: disabled ? "bypass" : "loading"
4248
4503
  });
4249
- (0, import_react20.useEffect)(() => {
4504
+ (0, import_react21.useEffect)(() => {
4250
4505
  confirmationWorkflow({
4251
4506
  buildId,
4252
4507
  configPath,
4253
- loadBuild,
4254
- setState,
4255
- state
4256
- }).catch((error) => {
4257
- setState({ state: "error", error });
4258
- });
4508
+ loadData,
4509
+ state,
4510
+ updateState
4511
+ }).catch(logger_default.error);
4259
4512
  }, []);
4260
4513
  function updateState(changes) {
4261
- setState((previousState) => ({ ...previousState, ...changes }));
4514
+ setState(
4515
+ (previousState) => ({ ...previousState, ...changes })
4516
+ );
4262
4517
  }
4263
4518
  switch (state.state) {
4519
+ case "build-error": {
4520
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4521
+ BuildError,
4522
+ {
4523
+ build: state.build,
4524
+ description: "Can't release build",
4525
+ message: ((_a = state.error) == null ? void 0 : _a.message) || "Internal build error"
4526
+ }
4527
+ );
4528
+ }
4264
4529
  case "bypass":
4265
4530
  return children;
4531
+ case "confirm-downgrade":
4532
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4533
+ DowngradeConfirmation,
4534
+ {
4535
+ build: state.build,
4536
+ latestReleasedBuild: state.latestReleasedBuild,
4537
+ confirmDowngrade: state.onConfirm
4538
+ }
4539
+ );
4266
4540
  case "confirm-release":
4267
4541
  if (!state.build) {
4268
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ErrorDisplay_default, { error: new Error("state.build is empty") });
4542
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ErrorDisplay, { error: new Error("state.build is empty") });
4269
4543
  }
4270
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4544
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4271
4545
  YesNoConfirmation,
4272
4546
  {
4273
- title: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
4274
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ink33.Text, { children: [
4547
+ title: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
4548
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_ink34.Text, { children: [
4275
4549
  "This will release build ",
4276
4550
  state.build.id,
4277
4551
  " as "
4278
4552
  ] }),
4279
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ink33.Text, { bold: true, children: [
4553
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_ink34.Text, { bold: true, children: [
4280
4554
  state.build.appName,
4281
4555
  " v",
4282
4556
  state.build.appVersion
4283
4557
  ] }),
4284
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ink33.Text, { children: ", are you sure?" })
4558
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ink34.Text, { children: ", are you sure?" })
4285
4559
  ] }),
4286
- onYes: () => updateState({ state: "bypass" })
4560
+ onYes: state.onConfirm
4287
4561
  }
4288
4562
  );
4289
4563
  case "confirm-test":
4290
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4564
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4291
4565
  YesNoConfirmation,
4292
4566
  {
4293
- title: state.testConfirmationText,
4294
- onYes: () => updateState({ state: "confirm-release" })
4567
+ title: state.confirmationText,
4568
+ onYes: state.onConfirm
4295
4569
  }
4296
4570
  );
4297
4571
  case "error":
4298
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ErrorDisplay_default, { error: state.error });
4572
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ErrorDisplay, { error: state.error });
4299
4573
  case "loading":
4300
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LoadingText_default, {});
4574
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LoadingText_default, {});
4301
4575
  default:
4302
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ErrorDisplay_default, { error: new Error(`Unknown state ${state.state}`) });
4576
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4577
+ ErrorDisplay,
4578
+ {
4579
+ error: new Error(`Unknown state ${state.state}`)
4580
+ }
4581
+ );
4303
4582
  }
4304
4583
  }
4305
4584
  async function confirmationWorkflow({
4306
4585
  buildId,
4307
4586
  configPath,
4308
- loadBuild,
4309
- setState,
4587
+ loadData,
4588
+ updateState,
4310
4589
  state
4311
4590
  }) {
4312
- if (state.state === "bypass") {
4591
+ try {
4592
+ const { build, latestReleasedBuild } = await loadData({
4593
+ buildId,
4594
+ configPath
4595
+ });
4596
+ updateState({ build, latestReleasedBuild });
4597
+ const report = analyzeRelease({ build, latestReleasedBuild });
4598
+ if (!report.isReleasable) {
4599
+ updateState({ error: new Error(report.error), state: "build-error" });
4600
+ return;
4601
+ }
4602
+ if (state.state !== "bypass") {
4603
+ await confirmDowngrade({ report, updateState });
4604
+ await confirmSmokeTest({ build, updateState });
4605
+ await confirmRelease({ updateState });
4606
+ }
4607
+ updateState({ state: "bypass" });
4608
+ } catch (e) {
4609
+ updateState({ state: "error", error: CliError.from(e) });
4610
+ }
4611
+ }
4612
+ async function confirmDowngrade({
4613
+ report,
4614
+ updateState
4615
+ }) {
4616
+ if (!report.isOlderThanLatest) {
4313
4617
  return;
4314
4618
  }
4315
- const { build } = await loadBuild({ buildId, configPath });
4316
- const testConfirmationText = getTestConfirmation(build);
4317
- setState({
4318
- build,
4319
- testConfirmationText,
4320
- state: testConfirmationText ? "confirm-test" : "confirm-release"
4321
- });
4619
+ let onConfirm;
4620
+ const downgradePromise = new Promise((r) => onConfirm = r);
4621
+ updateState({ onConfirm, state: "confirm-downgrade" });
4622
+ await downgradePromise;
4623
+ }
4624
+ async function confirmSmokeTest({
4625
+ build,
4626
+ updateState
4627
+ }) {
4628
+ const confirmationText = getTestConfirmation(build);
4629
+ if (confirmationText === "") {
4630
+ return;
4631
+ }
4632
+ let onConfirm;
4633
+ const downgradePromise = new Promise((r) => onConfirm = r);
4634
+ updateState({ onConfirm, state: "confirm-test", confirmationText });
4635
+ await downgradePromise;
4636
+ }
4637
+ async function confirmRelease({
4638
+ updateState
4639
+ }) {
4640
+ let onConfirm;
4641
+ const downgradePromise = new Promise((r) => onConfirm = r);
4642
+ updateState({ onConfirm, state: "confirm-release" });
4643
+ await downgradePromise;
4322
4644
  }
4323
4645
  function getTestConfirmation(build) {
4324
4646
  const continueText = "Are you sure you want to release?";
@@ -4340,9 +4662,20 @@ function getTestConfirmation(build) {
4340
4662
  return "";
4341
4663
  }
4342
4664
  }
4665
+ async function loadBuildsData({
4666
+ buildId,
4667
+ configPath
4668
+ }) {
4669
+ const { appId, build, userId } = await getBuildAttributes({
4670
+ buildId,
4671
+ configPath
4672
+ });
4673
+ const latestReleasedBuild = await getLatestReleasedBuild({ appId, userId });
4674
+ return { build, latestReleasedBuild };
4675
+ }
4343
4676
 
4344
4677
  // src/commands/release/ReleaseCommand.tsx
4345
- var import_jsx_runtime38 = require("react/jsx-runtime");
4678
+ var import_jsx_runtime39 = require("react/jsx-runtime");
4346
4679
  function ReleaseCommand({
4347
4680
  buildId,
4348
4681
  configPath,
@@ -4358,26 +4691,27 @@ function ReleaseCommand({
4358
4691
  if (buildId) {
4359
4692
  command += " " + (buildId === "latest" ? "--latest" : "<id>");
4360
4693
  }
4361
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LoginHOC_default, { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4694
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(LoginHOC_default, { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4362
4695
  BuildPicker,
4363
4696
  {
4364
4697
  buildFilter: isBuildReleasable,
4365
4698
  buildId,
4366
4699
  commandUsed: command,
4367
4700
  configPath,
4701
+ noBuildsDescription: "(i.e. unreleased and successful)",
4368
4702
  question: "Which build would you like to release?",
4369
- children: (id) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4703
+ children: (id) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4370
4704
  ReleaseConfirmation,
4371
4705
  {
4372
4706
  buildId: id,
4373
4707
  configPath,
4374
4708
  disabled: !shouldConfirm,
4375
- children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4376
- ReleaseBuild,
4709
+ children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4710
+ ReleaseBuildLogic,
4377
4711
  {
4712
+ buildId: id,
4378
4713
  commandUsed: command,
4379
- configPath,
4380
- id
4714
+ configPath
4381
4715
  }
4382
4716
  )
4383
4717
  }
@@ -4385,37 +4719,37 @@ function ReleaseCommand({
4385
4719
  }
4386
4720
  ) }) });
4387
4721
  }
4388
- function isBuildReleasable(build) {
4389
- return !build.releasedAt;
4722
+ function isBuildReleasable(opts) {
4723
+ return analyzeRelease(opts).isReleasable;
4390
4724
  }
4391
4725
 
4392
4726
  // src/commands/smoke-test/SmokeTestCommand.tsx
4393
- var import_react22 = require("react");
4727
+ var import_react23 = require("react");
4394
4728
 
4395
4729
  // src/commands/smoke-test/components/Cancellation.tsx
4396
- var import_ink34 = require("ink");
4397
- var import_react21 = require("react");
4398
- var import_jsx_runtime39 = require("react/jsx-runtime");
4730
+ var import_ink35 = require("ink");
4731
+ var import_react22 = require("react");
4732
+ var import_jsx_runtime40 = require("react/jsx-runtime");
4399
4733
  function Cancellation({
4400
4734
  children,
4401
4735
  disabled = false,
4402
4736
  exitWhenCanceled = true,
4403
4737
  onCancel,
4404
- renderCanceling = () => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(LoadingText_default, { text: `Canceling ${subject}` }),
4405
- renderCanceled = () => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_ink34.Text, { children: [
4738
+ renderCanceling = () => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LoadingText_default, { text: `Canceling ${subject}` }),
4739
+ renderCanceled = () => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_ink35.Text, { children: [
4406
4740
  upperCaseFirstChar(subject),
4407
4741
  " canceled."
4408
4742
  ] }),
4409
- renderCancelText = () => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_ink34.Text, { color: "gray", children: [
4410
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ink34.Text, { bold: true, children: "[esc]:" }),
4743
+ renderCancelText = () => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_ink35.Text, { color: "gray", children: [
4744
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ink35.Text, { bold: true, children: "[esc]:" }),
4411
4745
  " cancel ",
4412
4746
  subject
4413
4747
  ] }),
4414
- renderError = (e) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ErrorDisplay_default, { error: e }),
4748
+ renderError = (e) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ErrorDisplay, { error: e }),
4415
4749
  subject
4416
4750
  }) {
4417
- const [state, setState] = (0, import_react21.useState)("normal");
4418
- const [error, setError] = (0, import_react21.useState)();
4751
+ const [state, setState] = (0, import_react22.useState)("normal");
4752
+ const [error, setError] = (0, import_react22.useState)();
4419
4753
  const onInput = useInput_default();
4420
4754
  const exit = useExit_default();
4421
4755
  onInput(async (input, key) => {
@@ -4447,9 +4781,9 @@ function Cancellation({
4447
4781
  case "error":
4448
4782
  return renderError(error);
4449
4783
  default:
4450
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
4784
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
4451
4785
  children,
4452
- !disabled && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ink34.Box, { children: renderCancelText() })
4786
+ !disabled && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ink35.Box, { children: renderCancelText() })
4453
4787
  ] });
4454
4788
  }
4455
4789
  }
@@ -4458,11 +4792,11 @@ function upperCaseFirstChar(str) {
4458
4792
  }
4459
4793
 
4460
4794
  // src/commands/smoke-test/components/SmokeTestView.tsx
4461
- var import_ink36 = require("ink");
4795
+ var import_ink37 = require("ink");
4462
4796
 
4463
4797
  // src/commands/smoke-test/components/ProgressBar.tsx
4464
- var import_ink35 = require("ink");
4465
- var import_jsx_runtime40 = require("react/jsx-runtime");
4798
+ var import_ink36 = require("ink");
4799
+ var import_jsx_runtime41 = require("react/jsx-runtime");
4466
4800
  function ProgressBar2({
4467
4801
  color = "white",
4468
4802
  label,
@@ -4473,28 +4807,28 @@ function ProgressBar2({
4473
4807
  }) {
4474
4808
  const percentage = progress > 0 ? Math.round(progress).toString().padStart(2, "0") + "%" : "";
4475
4809
  const displayedText = (text || "").replace(/(?:\r\n|\r|\n)\s*/g, "\u21B5 ").replace(/(.{63}).+/, "$1\u2026");
4476
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_ink35.Box, { marginBottom, children: [
4477
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ink35.Box, { width: labelWidth, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_ink35.Text, { children: [
4810
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_ink36.Box, { marginBottom, children: [
4811
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ink36.Box, { width: labelWidth, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_ink36.Text, { children: [
4478
4812
  label,
4479
4813
  ":"
4480
4814
  ] }) }),
4481
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ink35.Box, { width: 6, justifyContent: "flex-end", marginRight: 1, children: Boolean(percentage) && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ink35.Text, { backgroundColor: color, color: "black", children: ` ${percentage} ` }) }),
4482
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ink35.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ink35.Text, { children: displayedText }) })
4815
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ink36.Box, { width: 6, justifyContent: "flex-end", marginRight: 1, children: Boolean(percentage) && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ink36.Text, { backgroundColor: color, color: "black", children: ` ${percentage} ` }) }),
4816
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ink36.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ink36.Text, { children: displayedText }) })
4483
4817
  ] });
4484
4818
  }
4485
4819
 
4486
4820
  // src/commands/smoke-test/components/TestProgress.tsx
4487
- var import_jsx_runtime41 = require("react/jsx-runtime");
4821
+ var import_jsx_runtime42 = require("react/jsx-runtime");
4488
4822
  function TestProgress({
4489
4823
  progress
4490
4824
  }) {
4491
4825
  if (!progress) {
4492
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(LoadingText_default, { text: "Loading test stats" });
4826
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(LoadingText_default, { text: "Loading test stats" });
4493
4827
  }
4494
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
4495
- !progress.windows.shouldSkip && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(OsProgress, { os: "Windows", progress: progress.windows }),
4496
- !progress.mac.shouldSkip && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(OsProgress, { os: "macOS", progress: progress.mac }),
4497
- !progress.linux.shouldSkip && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(OsProgress, { os: "Linux", progress: progress.linux })
4828
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
4829
+ !progress.windows.shouldSkip && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(OsProgress, { os: "Windows", progress: progress.windows }),
4830
+ !progress.mac.shouldSkip && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(OsProgress, { os: "macOS", progress: progress.mac }),
4831
+ !progress.linux.shouldSkip && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(OsProgress, { os: "Linux", progress: progress.linux })
4498
4832
  ] });
4499
4833
  }
4500
4834
  function OsProgress({
@@ -4508,7 +4842,7 @@ function OsProgress({
4508
4842
  skipped: "yellow"
4509
4843
  };
4510
4844
  const text = progress.message + (progress.state === "progress" ? "..." : "");
4511
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4845
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4512
4846
  ProgressBar2,
4513
4847
  {
4514
4848
  color: colors[progress.state],
@@ -4520,7 +4854,7 @@ function OsProgress({
4520
4854
  }
4521
4855
 
4522
4856
  // src/commands/smoke-test/components/SmokeTestView.tsx
4523
- var import_jsx_runtime42 = require("react/jsx-runtime");
4857
+ var import_jsx_runtime43 = require("react/jsx-runtime");
4524
4858
  function SmokeTestView({
4525
4859
  commandUsed = "",
4526
4860
  state
@@ -4528,7 +4862,7 @@ function SmokeTestView({
4528
4862
  var _a, _b, _c, _d;
4529
4863
  switch (state.state) {
4530
4864
  case "build-error": {
4531
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4865
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4532
4866
  BuildError,
4533
4867
  {
4534
4868
  build: state.build,
@@ -4538,25 +4872,25 @@ function SmokeTestView({
4538
4872
  );
4539
4873
  }
4540
4874
  case "canceled":
4541
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ink36.Text, { children: "Smoke test canceled." });
4875
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ink37.Text, { children: "Smoke test canceled." });
4542
4876
  case "complete":
4543
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ink36.Text, { bold: true, color: "greenBright", children: "Smoke test passed." });
4877
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ink37.Text, { bold: true, color: "greenBright", children: "Smoke test passed." });
4544
4878
  case "error":
4545
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4546
- ErrorDisplay_default,
4879
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4880
+ ErrorDisplay,
4547
4881
  {
4548
4882
  commandUsed,
4549
4883
  error: { stack: (_b = state.error) == null ? void 0 : _b.stack }
4550
4884
  }
4551
4885
  );
4552
4886
  case "loading":
4553
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(LoadingText_default, { text: "Preparing smoke test" });
4887
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(LoadingText_default, { text: "Preparing smoke test" });
4554
4888
  case "progress":
4555
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(TestProgress, { progress: state.progress });
4889
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(TestProgress, { progress: state.progress });
4556
4890
  case "progress-error":
4557
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
4558
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(TestProgress, { progress: state.progress }),
4559
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4891
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
4892
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(TestProgress, { progress: state.progress }),
4893
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4560
4894
  BuildError,
4561
4895
  {
4562
4896
  build: state.build,
@@ -4566,8 +4900,8 @@ function SmokeTestView({
4566
4900
  )
4567
4901
  ] });
4568
4902
  default:
4569
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4570
- ErrorDisplay_default,
4903
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4904
+ ErrorDisplay,
4571
4905
  {
4572
4906
  commandUsed,
4573
4907
  error: new Error(`Unknown state ${state}`)
@@ -4596,6 +4930,8 @@ async function queueSmokeTest({
4596
4930
  contextUserId,
4597
4931
  nodeVersion,
4598
4932
  npmVersion,
4933
+ pnpmVersion,
4934
+ yarnVersion,
4599
4935
  userId
4600
4936
  }) {
4601
4937
  try {
@@ -4604,15 +4940,14 @@ async function queueSmokeTest({
4604
4940
  buildId,
4605
4941
  nodeVersion,
4606
4942
  npmVersion,
4943
+ pnpmVersion,
4944
+ yarnVersion,
4607
4945
  userId,
4608
4946
  contextUserId
4609
4947
  });
4610
4948
  } catch (e) {
4611
- if (["failed-precondition", "not-found"].includes(e.code)) {
4612
- throw new SmokeError(e.mesage, { type: "build-error" });
4613
- } else {
4614
- throw new SmokeError("Unexpected internal error while testing build");
4615
- }
4949
+ logger_default.error({ error: e });
4950
+ throw new CliError(`Unexpected internal error while testing build`);
4616
4951
  }
4617
4952
  }
4618
4953
 
@@ -4644,7 +4979,7 @@ async function waitUntilFinished({
4644
4979
  }
4645
4980
 
4646
4981
  // src/commands/smoke-test/SmokeTestCommand.tsx
4647
- var import_jsx_runtime43 = require("react/jsx-runtime");
4982
+ var import_jsx_runtime44 = require("react/jsx-runtime");
4648
4983
  function SmokeTestCommand({
4649
4984
  buildId,
4650
4985
  configPath
@@ -4652,14 +4987,15 @@ function SmokeTestCommand({
4652
4987
  checkIfReactIsUsable_default();
4653
4988
  useAnalyticsCommand("smoke-test", { buildId, configPath });
4654
4989
  const command = `todesktop release ${buildId === "latest" ? "--latest" : "<id>"}`;
4655
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(LoginHOC_default, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4990
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(LoginHOC_default, { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
4656
4991
  BuildPicker,
4657
4992
  {
4658
- buildFilter: isBuildTestable,
4993
+ buildFilter: ({ build }) => isBuildTestable(build),
4659
4994
  buildId,
4660
4995
  commandUsed: command,
4661
4996
  configPath,
4662
- children: (id) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4997
+ noBuildsDescription: "(candidates for smoke-test must be built with @todesktop/runtime@1.2.1 or later)",
4998
+ children: (id) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
4663
4999
  SmokeTestContainer,
4664
5000
  {
4665
5001
  commandUsed: command,
@@ -4676,18 +5012,20 @@ function SmokeTestContainer({
4676
5012
  configPath
4677
5013
  }) {
4678
5014
  const exit = useExit_default();
4679
- const [state, setState] = (0, import_react22.useState)({
5015
+ const [state, setState] = (0, import_react23.useState)({
4680
5016
  buildId,
4681
5017
  state: "loading"
4682
5018
  });
4683
- const [abortController] = (0, import_react22.useState)(new AbortController());
5019
+ const [abortController] = (0, import_react23.useState)(new AbortController());
4684
5020
  const abortSignal = abortController.signal;
4685
- (0, import_react22.useEffect)(() => {
4686
- smokeTestWorkflow({ abortSignal, buildId, configPath, updateState });
5021
+ (0, import_react23.useEffect)(() => {
5022
+ smokeTestWorkflow({ abortSignal, buildId, configPath, updateState }).catch(
5023
+ logger_default.error
5024
+ );
4687
5025
  }, []);
4688
5026
  function updateState(changes) {
4689
5027
  setState((previousState) => ({ ...previousState, ...changes }));
4690
- if (["build-error", "complete", "progress-error"].includes(changes.state)) {
5028
+ if (changes.state && ["build-error", "complete", "progress-error"].includes(changes.state)) {
4691
5029
  setTimeout(() => exit(), 10);
4692
5030
  }
4693
5031
  }
@@ -4697,13 +5035,13 @@ function SmokeTestContainer({
4697
5035
  await cancelSmokeTest(state);
4698
5036
  }
4699
5037
  }
4700
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5038
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
4701
5039
  Cancellation,
4702
5040
  {
4703
5041
  disabled: !isCancelable(state),
4704
5042
  onCancel,
4705
5043
  subject: "smoke test",
4706
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SmokeTestView, { commandUsed, state })
5044
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SmokeTestView, { commandUsed, state })
4707
5045
  }
4708
5046
  );
4709
5047
  }
@@ -4714,7 +5052,16 @@ async function smokeTestWorkflow({
4714
5052
  updateState
4715
5053
  }) {
4716
5054
  try {
4717
- const { appId, build, contextUserId, nodeVersion, npmVersion, userId } = await getBuildAttributes({
5055
+ const {
5056
+ appId,
5057
+ build,
5058
+ contextUserId,
5059
+ nodeVersion,
5060
+ npmVersion,
5061
+ pnpmVersion,
5062
+ yarnVersion,
5063
+ userId
5064
+ } = await getBuildAttributes({
4718
5065
  buildId,
4719
5066
  configPath
4720
5067
  });
@@ -4726,6 +5073,8 @@ async function smokeTestWorkflow({
4726
5073
  buildId,
4727
5074
  nodeVersion,
4728
5075
  npmVersion,
5076
+ pnpmVersion,
5077
+ yarnVersion,
4729
5078
  userId,
4730
5079
  contextUserId
4731
5080
  });
@@ -4744,8 +5093,9 @@ async function smokeTestWorkflow({
4744
5093
  canceled: "canceled"
4745
5094
  };
4746
5095
  updateState({ state: stateMap[total.state] });
4747
- } catch (error) {
4748
- updateState({ error, state: error.type || "error" });
5096
+ } catch (e) {
5097
+ const error = CliError.from(e);
5098
+ updateState({ error, state: error.type });
4749
5099
  }
4750
5100
  }
4751
5101
  function buildCanBeCanceled(build) {
@@ -4761,39 +5111,39 @@ function isCancelable(state) {
4761
5111
  }
4762
5112
 
4763
5113
  // src/commands/WhoamiCommand.tsx
4764
- var import_react23 = require("react");
4765
- var import_ink37 = require("ink");
4766
- var import_jsx_runtime44 = require("react/jsx-runtime");
5114
+ var import_react24 = require("react");
5115
+ var import_ink38 = require("ink");
5116
+ var import_jsx_runtime45 = require("react/jsx-runtime");
4767
5117
  var WhoAmI = () => {
4768
5118
  const exit = useExit_default();
4769
5119
  checkIfReactIsUsable_default();
4770
5120
  const auth = getAuthConfig();
4771
5121
  const { hasAttemptedTracking } = useAnalyticsCommand("whoami", {}, {});
4772
- (0, import_react23.useEffect)(() => {
5122
+ (0, import_react24.useEffect)(() => {
4773
5123
  if (hasAttemptedTracking) {
4774
5124
  exit();
4775
5125
  }
4776
5126
  }, [exit, hasAttemptedTracking]);
4777
5127
  if (!auth || !auth.email) {
4778
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ink37.Text, { children: "You're not signed in" });
5128
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ink38.Text, { children: "You're not signed in" });
4779
5129
  }
4780
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ink37.Text, { children: auth.email });
5130
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ink38.Text, { children: auth.email });
4781
5131
  };
4782
- var WhoAmIWrapper = () => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(LoginHOC_default, { isInteractive: false, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(WhoAmI, {}) }) });
5132
+ var WhoAmIWrapper = () => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(LoginHOC_default, { isInteractive: false, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(WhoAmI, {}) }) });
4783
5133
  var WhoamiCommand_default = WhoAmIWrapper;
4784
5134
 
4785
5135
  // src/utilities/exitIfCLIOutOfDate.ts
4786
5136
  var import_chalk3 = __toESM(require("chalk"));
4787
5137
  var import_is_installed_globally = __toESM(require("is-installed-globally"));
4788
5138
  var import_latest_version = __toESM(require("latest-version"));
4789
- var import_semver2 = __toESM(require("semver"));
5139
+ var import_semver3 = __toESM(require("semver"));
4790
5140
  var exitIfCLIOutOfDate_default = () => {
4791
5141
  if (process.env.AVA_PATH) {
4792
5142
  return;
4793
5143
  }
4794
5144
  const pkg = getToDesktopPackageJson();
4795
5145
  (0, import_latest_version.default)(pkg.name).then((latest) => {
4796
- if (import_semver2.default.gt(latest, pkg.version)) {
5146
+ if (import_semver3.default.gt(latest, pkg.version)) {
4797
5147
  const commandToUpdate = import_chalk3.default.greenBright(
4798
5148
  `npm install ${import_is_installed_globally.default ? "--location=global" : "--save-dev"} @todesktop/cli`
4799
5149
  );
@@ -4801,7 +5151,7 @@ var exitIfCLIOutOfDate_default = () => {
4801
5151
  `Your version of @todesktop/cli is out of date.
4802
5152
  Run ${commandToUpdate} to update to v${latest}.`
4803
5153
  );
4804
- if (!import_semver2.default.satisfies(latest, `^${pkg.version}`)) {
5154
+ if (!import_semver3.default.satisfies(latest, `^${pkg.version}`)) {
4805
5155
  console.log(`CLI is exiting because it is out out of date.`);
4806
5156
  process.exit(1);
4807
5157
  }
@@ -4820,7 +5170,7 @@ var package_default = {
4820
5170
  access: "public"
4821
5171
  },
4822
5172
  name: "@todesktop/cli",
4823
- version: "1.7.6",
5173
+ version: "1.7.7",
4824
5174
  license: "MIT",
4825
5175
  author: "Dave Jeffery <dave@todesktop.com> (http://www.todesktop.com/)",
4826
5176
  homepage: "https://todesktop.com/cli",
@@ -4900,7 +5250,7 @@ var package_default = {
4900
5250
  "xdg-basedir": "^4.0.0"
4901
5251
  },
4902
5252
  devDependencies: {
4903
- "@todesktop/shared": "^7.186.6",
5253
+ "@todesktop/shared": "^7.186.18",
4904
5254
  "@types/bunyan": "^1.8.6",
4905
5255
  "@types/node": "^20.8.4",
4906
5256
  "@types/react": "^18.0.26",
@@ -4925,11 +5275,12 @@ var package_default = {
4925
5275
  husky: "^4.3.0",
4926
5276
  "ink-testing-library": "^2.1.0",
4927
5277
  "lint-staged": "^10.2.11",
5278
+ "package-json-type": "^1.0.3",
4928
5279
  prettier: "^2.8.1",
4929
5280
  proxyquire: "^2.1.3",
4930
5281
  sinon: "^9.0.3",
4931
5282
  typescript: "^4.9.4",
4932
- "typescript-strict-plugin": "^2.1.0"
5283
+ "typescript-strict-plugin": "^2.2.1"
4933
5284
  },
4934
5285
  ava: {
4935
5286
  extensions: [
@@ -4940,6 +5291,7 @@ var package_default = {
4940
5291
  "test/**/*.ts",
4941
5292
  "**/*.test.ts",
4942
5293
  "**/*.test.tsx",
5294
+ "!build/**/*",
4943
5295
  "!test/fixtures/**/*",
4944
5296
  "!test/utilities/**/*"
4945
5297
  ],
@@ -5046,17 +5398,30 @@ function parseCount(value) {
5046
5398
  var configOption = new import_commander.Option(
5047
5399
  "--config [string]",
5048
5400
  "Path to a different configuration file. If not specified, `todesktop.json` in the project root will be used"
5049
- );
5401
+ ).argParser((value) => {
5402
+ if (typeof value === "string" && value === "") {
5403
+ throw new import_commander.InvalidArgumentError("Provided config path is empty");
5404
+ }
5405
+ return value;
5406
+ });
5050
5407
  import_commander.program.name("todekstop").version(getCliVersion_default());
5051
5408
  import_commander.program.command("build").description(
5052
- "Build an Electron app with native installers, code signing baked-in, etc. but without releasing it (existing users won't get an auto-update). For quicker builds, you can append `--code-sign=false` to disablecode-signing and notarization."
5409
+ "Build an Electron app with native installers, code signing baked-in, etc. but without releasing it (existing users won't get an auto-update). For quicker builds, you can append `--code-sign=false` to disable code-signing and notarization."
5053
5410
  ).option(
5054
5411
  "--code-sign [bool]",
5055
5412
  "Whether or not code-signing and notarization should be performed. Disable this for quicker builds"
5056
- ).addOption(configOption).action(({ codeSign, config: config2 }) => {
5057
- runCommand(BuildCommand_default, {
5058
- shouldCodeSign: codeSign !== "false",
5059
- configPath: config2
5413
+ ).option(
5414
+ "--async",
5415
+ "Upload your app for building on our servers but exit after the upload is complete. Do not wait until the app is actually built"
5416
+ ).option(
5417
+ "--webhook [string]",
5418
+ "Send POST request to the webhook URL after building is finished"
5419
+ ).addOption(configOption).action(({ async, codeSign, config: config2, webhook }) => {
5420
+ runCommand(BuildCommand, {
5421
+ configPath: config2,
5422
+ exitAfterUploading: async === true,
5423
+ onBuildFinishedWebhook: webhook,
5424
+ shouldCodeSign: codeSign !== "false"
5060
5425
  });
5061
5426
  });
5062
5427
  import_commander.program.command("builds").description("View your builds").argument("[id]", "View a specific build by ID").option("--latest", "View the latest build").addOption(configOption).option("--count [number]", "Number of builds to show per page", parseCount).addOption(
@@ -5095,7 +5460,7 @@ import_commander.program.command("whoami").description("Prints the email of the
5095
5460
  });
5096
5461
  var runCommand = (component, props = null, { exitIfOutOfDate = true } = {}) => {
5097
5462
  onCommand_default({ exitIfOutOfDate });
5098
- const { waitUntilExit } = (0, import_ink38.render)((0, import_react24.createElement)(component, props));
5463
+ const { waitUntilExit } = (0, import_ink39.render)((0, import_react25.createElement)(component, props));
5099
5464
  waitUntilExit().catch((error) => {
5100
5465
  console.error(error.stack);
5101
5466
  });