@todesktop/cli 1.7.7 → 1.8.1

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,11 @@ var full_default = (context) => {
1663
1674
  validSemver: {},
1664
1675
  minLength: 1
1665
1676
  },
1677
+ pnpmVersion: {
1678
+ type: "string",
1679
+ validSemver: {},
1680
+ minLength: 1
1681
+ },
1666
1682
  appBuilderLibVersion: {
1667
1683
  type: "string",
1668
1684
  validSemver: {},
@@ -1834,8 +1850,8 @@ function computeFullProjectConfig(partialConfig, projectRoot) {
1834
1850
  }
1835
1851
 
1836
1852
  // src/utilities/projectConfig/getProjectConfig.ts
1837
- function getProjectConfig(configPath = null) {
1838
- if (configPath === null) {
1853
+ function getProjectConfig(configPath) {
1854
+ if (!configPath) {
1839
1855
  logger_default.debug("No config path provided, searching for one");
1840
1856
  configPath = import_find_up.default.sync("todesktop.json");
1841
1857
  if (!configPath) {
@@ -1844,10 +1860,6 @@ function getProjectConfig(configPath = null) {
1844
1860
  );
1845
1861
  }
1846
1862
  } 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
1863
  configPath = (0, import_path5.resolve)(process.cwd(), configPath);
1852
1864
  if (!(0, import_fs.existsSync)(configPath)) {
1853
1865
  logger_default.error("Provided config path does not exist");
@@ -1862,74 +1874,145 @@ function getProjectConfig(configPath = null) {
1862
1874
  return { config: result, unprocessedConfig: config2, projectRoot };
1863
1875
  }
1864
1876
 
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;
1884
- }
1885
- }
1886
-
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
- });
1877
+ // src/utilities/shouldExitOnBuildFailure.ts
1878
+ var shouldExitOnBuildFailure_default = (build) => {
1879
+ const hasBuildSettled = ["linux", "mac", "windows"].every(
1880
+ (platform) => build[platform].shouldSkip || "succeeded" === build[platform].status || "failed" === build[platform].status && build[platform].numberOfAttemptedBuilds === 2
1881
+ );
1882
+ const hasSettledWithError = ["linux", "mac", "windows"].some(
1883
+ (platform) => "failed" === build[platform].status
1884
+ );
1885
+ return hasBuildSettled && hasSettledWithError;
1907
1886
  };
1908
1887
 
1909
- // src/utilities/subscribeToBuild.ts
1910
- var subscribeToBuild_default = async ({ appId, buildId, onDataReceived, userId }) => {
1911
- try {
1912
- return await subscribeToFirebaseDoc_default(
1913
- `users/${userId}/applications/${appId}/builds/${buildId}`,
1914
- async ({ data }) => {
1915
- onDataReceived(data);
1916
- }
1917
- );
1918
- } catch (e) {
1919
- e.message = `Failed while subscribing to build; ${e.message}`;
1920
- throw e;
1888
+ // src/commands/build/utilities/getPackageJson.ts
1889
+ var import_lodash3 = __toESM(require("lodash.merge"));
1890
+ var import_path6 = __toESM(require("path"));
1891
+ function deleteNullDeps(dep) {
1892
+ Object.keys(dep).forEach((key) => {
1893
+ if (dep[key] === null) {
1894
+ delete dep[key];
1895
+ }
1896
+ });
1897
+ return dep;
1898
+ }
1899
+ function removeNullDependencies(pkgJson) {
1900
+ const { dependencies, devDependencies } = pkgJson;
1901
+ if (dependencies) {
1902
+ pkgJson.dependencies = deleteNullDeps(dependencies);
1921
1903
  }
1922
- };
1923
-
1924
- // src/utilities/uploadApplicationSource.ts
1904
+ if (devDependencies) {
1905
+ pkgJson.devDependencies = deleteNullDeps(devDependencies);
1906
+ }
1907
+ return pkgJson;
1908
+ }
1909
+ function getAppPkgJson({ config: config2 }) {
1910
+ const packageJsonFromConfig = config2.packageJson || {};
1911
+ const extendsFrom = packageJsonFromConfig.extends || "package.json";
1912
+ const packageJsonFromFile = readJson(import_path6.default.join(config2.appPath, extendsFrom));
1913
+ return removeNullDependencies(
1914
+ (0, import_lodash3.default)({}, packageJsonFromFile, packageJsonFromConfig)
1915
+ );
1916
+ }
1917
+ var getPackageJson_default = getAppPkgJson;
1918
+
1919
+ // src/commands/build/utilities/getVersionControlInfo.ts
1920
+ var gitRevSync = __toESM(require("git-rev-sync"));
1921
+ var getVersionControlInfo_default = async (directory) => {
1922
+ let result = {};
1923
+ try {
1924
+ const gitCommitHash = gitRevSync.long(directory);
1925
+ if (gitCommitHash) {
1926
+ result = {
1927
+ branchName: gitRevSync.branch(directory),
1928
+ commitDate: gitRevSync.date().toISOString(),
1929
+ commitId: gitCommitHash,
1930
+ commitMessage: gitRevSync.message(),
1931
+ hasUncommittedChanges: gitRevSync.isDirty(),
1932
+ repositoryRemoteUrl: gitRevSync.remoteUrl(),
1933
+ versionControlSystemName: "git"
1934
+ };
1935
+ }
1936
+ } catch (e) {
1937
+ }
1938
+ return result;
1939
+ };
1940
+
1941
+ // src/commands/build/utilities/spyBuild.ts
1942
+ var import_events = __toESM(require("events"));
1943
+ function spyBuild() {
1944
+ const emitter = new import_events.default();
1945
+ let unsubscribeSnapshot;
1946
+ let latestBuildDoc;
1947
+ function when(predicate) {
1948
+ if (latestBuildDoc && predicate(latestBuildDoc)) {
1949
+ return Promise.resolve(latestBuildDoc);
1950
+ }
1951
+ return new Promise((resolve4, reject) => {
1952
+ emitter.on("update", ({ build }) => {
1953
+ if (predicate(build)) {
1954
+ resolve4(build);
1955
+ }
1956
+ }).once("error", (e) => reject(e));
1957
+ });
1958
+ }
1959
+ return {
1960
+ onUpdate(callback) {
1961
+ emitter.on("update", callback);
1962
+ },
1963
+ subscribe({
1964
+ appId,
1965
+ buildId,
1966
+ userId
1967
+ }) {
1968
+ const buildPath = `users/${userId}/applications/${appId}/builds/${buildId}`;
1969
+ unsubscribeSnapshot = firestore_default.doc(buildPath).onSnapshot(
1970
+ (snapshot) => {
1971
+ latestBuildDoc = snapshot.exists ? snapshot.data() : void 0;
1972
+ emitter.emit("update", {
1973
+ build: latestBuildDoc,
1974
+ snapshot
1975
+ });
1976
+ },
1977
+ (err) => {
1978
+ emitter.emit("error", err);
1979
+ }
1980
+ );
1981
+ },
1982
+ unsubscribe() {
1983
+ unsubscribeSnapshot == null ? void 0 : unsubscribeSnapshot();
1984
+ },
1985
+ async whenFirstUpdate() {
1986
+ if (latestBuildDoc) {
1987
+ return Promise.resolve(latestBuildDoc);
1988
+ }
1989
+ return new Promise((resolve4, reject) => {
1990
+ emitter.once("update", (data) => resolve4(data)).once("error", (e) => reject(e));
1991
+ });
1992
+ },
1993
+ async whenSettled() {
1994
+ return when((build) => buildHasSettled(build));
1995
+ }
1996
+ };
1997
+ }
1998
+ function buildHasSettled(build) {
1999
+ return ["linux", "mac", "windows"].every(
2000
+ (platform) => {
2001
+ var _a, _b, _c, _d, _e;
2002
+ 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;
2003
+ }
2004
+ );
2005
+ }
2006
+
2007
+ // src/commands/build/utilities/uploadApplicationSource.ts
1925
2008
  var import_fast_glob = __toESM(require("fast-glob"));
1926
2009
  var fs5 = __toESM(require("fs"));
1927
- var path8 = __toESM(require("path"));
2010
+ var path9 = __toESM(require("path"));
1928
2011
 
1929
- // src/utilities/generateS3Key.ts
2012
+ // src/commands/build/utilities/generateS3Key.ts
1930
2013
  var generateS3Key_default = ({ appId, buildId, filenameSuffix }) => `${appId}/sourceArchives/${buildId}--${filenameSuffix}`;
1931
2014
 
1932
- // src/utilities/uploadToS3.ts
2015
+ // src/commands/build/utilities/uploadToS3.ts
1933
2016
  var import_superagent = __toESM(require("superagent"));
1934
2017
  var import_stream_to_array = __toESM(require("stream-to-array"));
1935
2018
  var { TODESKTOP_CLI_S3_BUCKET } = getEnvironmentVariables_default();
@@ -1981,12 +2064,12 @@ var uploadToS3 = async ({
1981
2064
  };
1982
2065
  var uploadToS3_default = uploadToS3WithRetry;
1983
2066
 
1984
- // src/utilities/zip.ts
2067
+ // src/commands/build/utilities/zip.ts
1985
2068
  var import_archiver = __toESM(require("archiver"));
1986
2069
  var import_du = __toESM(require("du"));
1987
2070
  var import_fs2 = __toESM(require("fs"));
1988
2071
  var import_chalk = __toESM(require("chalk"));
1989
- var import_path6 = __toESM(require("path"));
2072
+ var import_path7 = __toESM(require("path"));
1990
2073
  async function zip_default({
1991
2074
  files,
1992
2075
  fileSizeLimit = 20,
@@ -2041,7 +2124,7 @@ Your app is larger than ${fileSizeLimit}MB. Your app is ${import_chalk.default.b
2041
2124
  processedFiles.forEach(({ from, isDirectory, stats, to }) => {
2042
2125
  if (isDirectory) {
2043
2126
  stream.directory(from, to);
2044
- } else if (appPkgJson && to === import_path6.default.join("app", "package.json")) {
2127
+ } else if (appPkgJson && to === import_path7.default.join("app", "package.json")) {
2045
2128
  stream.append(JSON.stringify(appPkgJson), {
2046
2129
  name: to
2047
2130
  });
@@ -2060,28 +2143,28 @@ Your app is larger than ${fileSizeLimit}MB. Your app is ${import_chalk.default.b
2060
2143
  return stream;
2061
2144
  }
2062
2145
 
2063
- // src/utilities/uploadApplicationSource.ts
2146
+ // src/commands/build/utilities/uploadApplicationSource.ts
2064
2147
  var getAppFiles = async (globsInput, appPath, appPkgJson) => {
2065
2148
  const globs = ["!node_modules", "!**/node_modules", "!.git", "!**/.git"];
2066
2149
  if (globsInput && globsInput.length) {
2067
2150
  globs.push(
2068
2151
  ...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")
2152
+ path9.join(appPath, "package.json"),
2153
+ path9.join(appPath, "package-lock.json"),
2154
+ path9.join(appPath, "yarn.lock"),
2155
+ path9.join(appPath, "pnpm-lock.yaml"),
2156
+ path9.join(appPath, "shrinkwrap.yaml")
2074
2157
  );
2075
2158
  } else {
2076
2159
  globs.push("**");
2077
2160
  }
2078
2161
  for (const hookName of ["todesktop:beforeInstall", "todesktop:afterPack"]) {
2079
2162
  if (appPkgJson.scripts && appPkgJson.scripts[hookName]) {
2080
- globs.push(path8.join(appPath, appPkgJson.scripts[hookName]));
2163
+ globs.push(path9.join(appPath, appPkgJson.scripts[hookName]));
2081
2164
  }
2082
2165
  }
2083
2166
  const normalizedGlobs = globs.map((glob) => {
2084
- const globToUse = path8.isAbsolute(glob) ? path8.relative(appPath, glob) : glob;
2167
+ const globToUse = path9.isAbsolute(glob) ? path9.relative(appPath, glob) : glob;
2085
2168
  return globToUse.replace(/\\/g, "/").replace(/\/+$/, "");
2086
2169
  }).filter((glob) => !glob.startsWith("..") && !glob.startsWith("!.."));
2087
2170
  let absolutePaths = await (0, import_fast_glob.default)(normalizedGlobs, {
@@ -2096,7 +2179,7 @@ var getAppFiles = async (globsInput, appPath, appPkgJson) => {
2096
2179
  });
2097
2180
  if (process.platform === "win32") {
2098
2181
  absolutePaths = absolutePaths.map(
2099
- (absolutePath) => absolutePath.replace(/\//g, path8.sep)
2182
+ (absolutePath) => absolutePath.replace(/\//g, path9.sep)
2100
2183
  );
2101
2184
  }
2102
2185
  if (!absolutePaths || !absolutePaths.length) {
@@ -2108,25 +2191,31 @@ var getAppFiles = async (globsInput, appPath, appPkgJson) => {
2108
2191
  } else {
2109
2192
  let mainFilePath = appPath;
2110
2193
  if (appPkgJson.main) {
2111
- mainFilePath = path8.join(mainFilePath, appPkgJson.main);
2194
+ mainFilePath = path9.join(mainFilePath, appPkgJson.main);
2112
2195
  }
2113
2196
  if (fs5.statSync(mainFilePath).isDirectory()) {
2114
- mainFilePath = path8.join(mainFilePath, "index.js");
2197
+ mainFilePath = path9.join(mainFilePath, "index.js");
2115
2198
  }
2116
2199
  if (!absolutePaths.includes(mainFilePath)) {
2117
2200
  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.`
2201
+ `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
2202
  );
2120
2203
  }
2121
2204
  }
2122
2205
  return absolutePaths.map((absolutePath) => {
2123
2206
  return {
2124
2207
  from: absolutePath,
2125
- to: path8.join("app", path8.relative(appPath, absolutePath))
2208
+ to: path9.join("app", path9.relative(appPath, absolutePath))
2126
2209
  };
2127
2210
  });
2128
2211
  };
2129
- var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, config: config2, onProgress }) => {
2212
+ async function uploadApplicationSource({
2213
+ appId,
2214
+ appPkgJson,
2215
+ buildId,
2216
+ config: config2,
2217
+ onProgress
2218
+ }) {
2130
2219
  logger_default.debug(
2131
2220
  {
2132
2221
  appId,
@@ -2143,48 +2232,70 @@ var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, confi
2143
2232
  ...(config2.extraContentFiles || []).map(({ from, to = "." }) => {
2144
2233
  return {
2145
2234
  from,
2146
- to: path8.join("extraContentFiles", to, path8.basename(from))
2235
+ to: path9.join("extraContentFiles", to, path9.basename(from))
2147
2236
  };
2148
2237
  }),
2149
2238
  ...(config2.extraResources || []).map(({ from, to = "." }) => {
2150
2239
  return {
2151
2240
  from,
2152
- to: path8.join("extraResources", to, path8.basename(from))
2241
+ to: path9.join("extraResources", to, path9.basename(from))
2153
2242
  };
2154
2243
  }),
2155
2244
  {
2156
2245
  from: config2.icon,
2157
- to: path8.join("icons", "appIcon" + path8.extname(config2.icon))
2246
+ to: path9.join("icons", "appIcon" + path9.extname(config2.icon))
2158
2247
  }
2159
2248
  ];
2160
2249
  if (config2.linux) {
2161
2250
  if (config2.linux.icon) {
2162
2251
  files.push({
2163
2252
  from: config2.linux.icon,
2164
- to: path8.join(
2253
+ to: path9.join(
2165
2254
  "icons",
2166
- "appIcon-linux" + path8.extname(config2.linux.icon)
2255
+ "appIcon-linux" + path9.extname(config2.linux.icon)
2167
2256
  )
2168
2257
  });
2169
2258
  }
2170
2259
  }
2260
+ if (Array.isArray(config2.fileAssociations)) {
2261
+ const possibleIcons = [];
2262
+ for (const fa of config2.fileAssociations) {
2263
+ if (fa.icon) {
2264
+ const icon = path9.parse(fa.icon);
2265
+ possibleIcons.push(
2266
+ { ext: fa.ext, icon: path9.join(icon.dir, icon.name) + ".ico" },
2267
+ { ext: fa.ext, icon: path9.join(icon.dir, icon.name) + ".icns" }
2268
+ );
2269
+ }
2270
+ }
2271
+ await Promise.all(
2272
+ possibleIcons.map(async ({ ext, icon }) => {
2273
+ if (await exists(icon)) {
2274
+ files.push({
2275
+ from: icon,
2276
+ to: path9.join("icons", "association-" + ext + path9.extname(icon))
2277
+ });
2278
+ }
2279
+ })
2280
+ );
2281
+ }
2171
2282
  if (config2.mac) {
2172
2283
  if (config2.mac.entitlements) {
2173
2284
  files.push({
2174
2285
  from: config2.mac.entitlements,
2175
- to: path8.join("other", "mac", "entitlements.mac.plist")
2286
+ to: path9.join("other", "mac", "entitlements.mac.plist")
2176
2287
  });
2177
2288
  }
2178
2289
  if (config2.mac.icon) {
2179
2290
  files.push({
2180
2291
  from: config2.mac.icon,
2181
- to: path8.join("icons", "appIcon-mac" + path8.extname(config2.mac.icon))
2292
+ to: path9.join("icons", "appIcon-mac" + path9.extname(config2.mac.icon))
2182
2293
  });
2183
2294
  }
2184
2295
  if (config2.mac.requirements) {
2185
2296
  files.push({
2186
2297
  from: config2.mac.requirements,
2187
- to: path8.join("other", "mac", "requirements.txt")
2298
+ to: path9.join("other", "mac", "requirements.txt")
2188
2299
  });
2189
2300
  }
2190
2301
  }
@@ -2192,7 +2303,7 @@ var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, confi
2192
2303
  if (config2.dmg.background) {
2193
2304
  files.push({
2194
2305
  from: config2.dmg.background,
2195
- to: path8.join("other", "mac", "dmg-background.tiff")
2306
+ to: path9.join("other", "mac", "dmg-background.tiff")
2196
2307
  });
2197
2308
  }
2198
2309
  }
@@ -2200,16 +2311,16 @@ var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, confi
2200
2311
  if (config2.windows.icon) {
2201
2312
  files.push({
2202
2313
  from: config2.windows.icon,
2203
- to: path8.join(
2314
+ to: path9.join(
2204
2315
  "icons",
2205
- "appIcon-windows" + path8.extname(config2.windows.icon)
2316
+ "appIcon-windows" + path9.extname(config2.windows.icon)
2206
2317
  )
2207
2318
  });
2208
2319
  }
2209
2320
  if (config2.windows.nsisInclude) {
2210
2321
  files.push({
2211
2322
  from: config2.windows.nsisInclude,
2212
- to: path8.join("other", "installer.nsh")
2323
+ to: path9.join("other", "installer.nsh")
2213
2324
  });
2214
2325
  }
2215
2326
  }
@@ -2235,50 +2346,39 @@ var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, confi
2235
2346
  onProgress(loaded / total * 100, loaded);
2236
2347
  }
2237
2348
  });
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
2349
  }
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);
2350
+ async function exists(filePath) {
2351
+ try {
2352
+ await fs5.promises.access(filePath);
2353
+ return true;
2354
+ } catch (e) {
2355
+ return false;
2258
2356
  }
2259
- return pkgJson;
2260
2357
  }
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
2358
 
2271
- // src/utilities/runBuild.ts
2272
- var runBuild_default = async ({ onEvent, shouldCodeSign = true, configPath }) => {
2359
+ // src/commands/build/utilities/runBuild.ts
2360
+ async function runBuild({
2361
+ configPath,
2362
+ exitAfterUploading,
2363
+ shouldCodeSign = true,
2364
+ onBuildFinishedWebhook,
2365
+ updateState
2366
+ }) {
2367
+ var _a;
2273
2368
  logForCI_default("Getting application information...");
2274
- const primaryUserId = currentUser().uid;
2369
+ const primaryUserId = (_a = currentUser()) == null ? void 0 : _a.uid;
2275
2370
  const { config: config2, unprocessedConfig } = getProjectConfig(configPath);
2276
2371
  const appId = config2.id;
2277
2372
  const appPkgJson = getPackageJson_default({ config: config2 });
2278
- onEvent("progress", {
2373
+ const buildObserver = spyBuild();
2374
+ buildObserver.onUpdate(({ build: build2 }) => updateState({ build: build2 }));
2375
+ updateState({
2279
2376
  appId,
2280
2377
  appPkg: appPkgJson,
2281
- preparationProgress: 0.02
2378
+ onUnmount: () => buildObserver.unsubscribe(),
2379
+ preparationStageLabel: "Preparing",
2380
+ preparationProgress: 0.02,
2381
+ state: "preparing"
2282
2382
  });
2283
2383
  logForCI_default("Preparing...");
2284
2384
  let buildId;
@@ -2288,6 +2388,7 @@ var runBuild_default = async ({ onEvent, shouldCodeSign = true, configPath }) =>
2288
2388
  appPkgProductName: appPkgJson.productName,
2289
2389
  appVersion: appPkgJson.version,
2290
2390
  id: config2.id,
2391
+ onBuildFinishedWebhook,
2291
2392
  projectConfig: unprocessedConfig,
2292
2393
  shouldCodeSign: shouldCodeSign !== false,
2293
2394
  shouldRelease: false,
@@ -2295,46 +2396,38 @@ var runBuild_default = async ({ onEvent, shouldCodeSign = true, configPath }) =>
2295
2396
  versionControlInfo: await getVersionControlInfo_default(config2.appPath)
2296
2397
  });
2297
2398
  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 });
2399
+ buildObserver.subscribe({ appId, buildId, userId: prepareResult.userId });
2400
+ await buildObserver.whenFirstUpdate();
2305
2401
  } catch (e) {
2306
- e.message = `Failed while preparing new build; ${e.message}`;
2307
- throw e;
2402
+ throw addErrorMessage(e, "Failed while preparing new build");
2308
2403
  }
2309
- onEvent("progress", {
2404
+ updateState({
2310
2405
  preparationProgress: 0.05,
2311
2406
  preparationStageLabel: "Uploading"
2312
2407
  });
2313
2408
  logForCI_default("Uploading...");
2314
2409
  let sourceArchiveDetails;
2315
2410
  try {
2316
- const uploadAppSourceResult = await uploadApplicationSource_default({
2411
+ sourceArchiveDetails = await uploadApplicationSource({
2317
2412
  appId,
2318
2413
  appPkgJson,
2319
2414
  buildId,
2320
2415
  config: config2,
2321
2416
  onProgress(progress, uploadedSize) {
2322
- const dataToEmit = {
2417
+ const preparationState = {
2323
2418
  preparationProgress: 0.05 + progress / 100 * 0.95,
2324
2419
  preparationUploadedSize: ""
2325
2420
  };
2326
2421
  if (uploadedSize) {
2327
- dataToEmit.preparationUploadedSize = (0, import_pretty_bytes.default)(uploadedSize);
2422
+ preparationState.preparationUploadedSize = (0, import_pretty_bytes.default)(uploadedSize);
2328
2423
  }
2329
- onEvent("progress", dataToEmit);
2424
+ updateState(preparationState);
2330
2425
  }
2331
2426
  });
2332
- sourceArchiveDetails = uploadAppSourceResult;
2333
2427
  } catch (e) {
2334
- e.message = `Failed while uploading application source; ${e.message}`;
2335
- throw e;
2428
+ throw addErrorMessage(e, "Failed while uploading application source");
2336
2429
  }
2337
- onEvent("progress", { isPreparationComplete: true });
2430
+ updateState({ state: "building" });
2338
2431
  logForCI_default("Kicking off build...");
2339
2432
  try {
2340
2433
  await postToFirebaseFunction_default("kickOffBuild", {
@@ -2346,128 +2439,131 @@ var runBuild_default = async ({ onEvent, shouldCodeSign = true, configPath }) =>
2346
2439
  sourceArchiveDetails,
2347
2440
  nodeVersion: config2.nodeVersion,
2348
2441
  npmVersion: config2.npmVersion,
2442
+ pnpmVersion: config2.pnpmVersion,
2349
2443
  appBuilderLibVersion: config2.appBuilderLibVersion
2350
2444
  });
2351
2445
  } catch (e) {
2352
- e.message = `Failed while kicking off build; ${e.message}`;
2353
- throw e;
2446
+ throw addErrorMessage(e, "Failed while kicking off build");
2354
2447
  }
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
- };
2448
+ if (exitAfterUploading) {
2449
+ updateState({ state: "exit-after-uploading" });
2450
+ return;
2451
+ }
2452
+ const build = await buildObserver.whenSettled();
2453
+ if ((build == null ? void 0 : build.status) === "failed" && shouldExitOnBuildFailure_default(build)) {
2454
+ updateState({ state: "build-failed" });
2455
+ }
2456
+ }
2457
+ function addErrorMessage(e, message2) {
2458
+ let originalMessage = "";
2459
+ if (e instanceof Error) {
2460
+ originalMessage = e.message;
2461
+ } else if (e && typeof e === "object") {
2462
+ originalMessage = JSON.stringify(e);
2463
+ } else {
2464
+ originalMessage = String(e);
2465
+ }
2466
+ const newError = new Error(`${message2}; ${originalMessage}`);
2467
+ if (e instanceof Error) {
2468
+ newError.stack = e.stack;
2469
+ }
2470
+ return newError;
2471
+ }
2367
2472
 
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 }) => {
2473
+ // src/commands/build/components/Build.tsx
2474
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2475
+ function Build({
2476
+ commandUsed,
2477
+ configPath,
2478
+ exitAfterUploading,
2479
+ onBuildFinishedWebhook,
2480
+ shouldCodeSign
2481
+ }) {
2482
+ var _a, _b, _c, _d, _e, _f;
2382
2483
  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
- };
2484
+ const [state, setState] = (0, import_react6.useState)({ state: "initializing" });
2405
2485
  (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
- },
2486
+ runBuild({
2487
+ configPath,
2488
+ exitAfterUploading,
2489
+ onBuildFinishedWebhook,
2416
2490
  shouldCodeSign,
2417
- configPath
2418
- }).catch(onError);
2491
+ updateState
2492
+ }).catch((e) => {
2493
+ const error = e.response ? e.response.data : e;
2494
+ logForCI_default(error);
2495
+ updateState({ state: "error", error });
2496
+ });
2419
2497
  return () => {
2420
- if (firebaseUnsubscribe) {
2421
- firebaseUnsubscribe();
2422
- }
2498
+ var _a2;
2499
+ return (_a2 = state.onUnmount) == null ? void 0 : _a2.call(state);
2423
2500
  };
2424
- }, [commandUsed, shouldCodeSign, configPath]);
2425
- (0, import_react6.useEffect)(() => {
2426
- if (hasBuildEverFailed && shouldExitOnBuildFailure_default(build)) {
2501
+ }, []);
2502
+ function updateState(changes) {
2503
+ setState(
2504
+ (previousState) => ({ ...previousState, ...changes })
2505
+ );
2506
+ if (changes.state === "exit-after-uploading") {
2507
+ setTimeout(() => exit(), 10);
2508
+ }
2509
+ if (changes.state === "build-failed") {
2427
2510
  setTimeout(() => exit(new Error("Build has failed")), 10);
2428
2511
  }
2429
- }, [build, exit, hasBuildEverFailed]);
2430
- if (error) {
2431
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ErrorDisplay_default, { commandUsed, error });
2432
2512
  }
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,
2513
+ switch (state.state) {
2514
+ case "build-failed":
2515
+ case "building": {
2516
+ if (!state.build) {
2517
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorDisplay, { commandUsed, error: "No build is set" });
2518
+ }
2519
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2520
+ MainLayout_default,
2456
2521
  {
2457
- progressPercentage: preparationProgress,
2458
- stageLabel: preparationStageLabel,
2459
- uploadedSize: preparationUploadedSize
2522
+ appName: state.build.appName || ((_a = state.appPkg) == null ? void 0 : _a.name) || "",
2523
+ appVersion: state.build.appVersion || ((_b = state.appPkg) == null ? void 0 : _b.version) || "",
2524
+ build: state.build,
2525
+ commandUsed,
2526
+ hasBuildEverFailed: state.state === "build-failed",
2527
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(BuildProgress_default, { build: state.build, onBuildFailure: () => {
2528
+ } })
2460
2529
  }
2461
- )
2530
+ );
2462
2531
  }
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;
2532
+ case "error":
2533
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorDisplay, { commandUsed, error: state.error });
2534
+ case "exit-after-uploading":
2535
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink13.Text, { children: "App is building on ToDesktop. Exit since the --async flag is passed." });
2536
+ case "initializing":
2537
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink13.Text, { children: "Getting app info..." });
2538
+ case "preparing":
2539
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2540
+ MainLayout_default,
2541
+ {
2542
+ appName: ((_c = state.build) == null ? void 0 : _c.appName) || ((_d = state.appPkg) == null ? void 0 : _d.name) || "",
2543
+ appVersion: ((_e = state.build) == null ? void 0 : _e.appVersion) || ((_f = state.appPkg) == null ? void 0 : _f.version) || "",
2544
+ build: state.build,
2545
+ commandUsed,
2546
+ hasBuildEverFailed: false,
2547
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2548
+ Preparation_default,
2549
+ {
2550
+ progressPercentage: state.preparationProgress || 0,
2551
+ stageLabel: state.preparationStageLabel || "Preparing",
2552
+ uploadedSize: state.preparationUploadedSize
2553
+ }
2554
+ )
2555
+ }
2556
+ );
2557
+ default:
2558
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2559
+ ErrorDisplay,
2560
+ {
2561
+ commandUsed,
2562
+ error: new Error(`Unknown state ${state.state}`)
2563
+ }
2564
+ );
2565
+ }
2566
+ }
2471
2567
 
2472
2568
  // src/utilities/checkIfReactIsUsable.ts
2473
2569
  var import_react7 = require("react");
@@ -2483,30 +2579,30 @@ var checkIfReactIsUsable_default = ({ cons = console, proc = process } = {}) =>
2483
2579
  // src/components/LoginHOC.tsx
2484
2580
  var import_ink16 = require("ink");
2485
2581
  var import_react10 = require("react");
2486
- var import_prop_types11 = __toESM(require("prop-types"));
2582
+ var import_prop_types9 = __toESM(require("prop-types"));
2487
2583
  var import_is_ci4 = __toESM(require("is-ci"));
2488
2584
 
2489
2585
  // src/components/Login.tsx
2490
2586
  var import_ink14 = require("ink");
2491
- var import_prop_types10 = __toESM(require("prop-types"));
2587
+ var import_prop_types8 = __toESM(require("prop-types"));
2492
2588
  var import_react9 = require("react");
2493
2589
  var import_react_final_form = require("react-final-form");
2494
2590
 
2495
2591
  // src/components/TextInput.tsx
2496
2592
  var import_ink_text_input = __toESM(require("ink-text-input"));
2497
2593
  var import_react8 = __toESM(require("react"));
2498
- var import_jsx_runtime14 = require("react/jsx-runtime");
2594
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2499
2595
  var TextInput = ({ onBlur, onFocus, ...props }) => {
2500
2596
  import_react8.default.useEffect(() => {
2501
2597
  onFocus();
2502
2598
  return onBlur;
2503
2599
  }, [onFocus, onBlur]);
2504
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink_text_input.default, { ...props, showCursor: true });
2600
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink_text_input.default, { ...props, showCursor: true });
2505
2601
  };
2506
2602
  var TextInput_default = TextInput;
2507
2603
 
2508
2604
  // src/components/Login.tsx
2509
- var import_jsx_runtime15 = require("react/jsx-runtime");
2605
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2510
2606
  var loginFields = [
2511
2607
  {
2512
2608
  name: "email",
@@ -2564,18 +2660,18 @@ var Login = ({ setIsLoggedIn }) => {
2564
2660
  }
2565
2661
  };
2566
2662
  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: [
2663
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ErrorDisplay, { error });
2664
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2665
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { children: "You are not currently logged in." }),
2666
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { children: "Log in below:" }),
2667
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Box, { marginBottom: 1 }),
2668
+ /* @__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(
2669
+ ({ 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: [
2670
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink14.Text, { bold: activeField === index, children: [
2575
2671
  label,
2576
2672
  ": "
2577
2673
  ] }),
2578
- activeField === index ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2674
+ activeField === index ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2579
2675
  Input,
2580
2676
  {
2581
2677
  ...input,
@@ -2592,35 +2688,35 @@ var Login = ({ setIsLoggedIn }) => {
2592
2688
  }
2593
2689
  }
2594
2690
  }
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 })
2691
+ ) : 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 }),
2692
+ 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" }) }),
2693
+ meta.error && meta.touched && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Error2, { errorMessage: meta.error })
2598
2694
  ] }) : null }) }, name)
2599
2695
  ) }) }),
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 })
2696
+ 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..." }) }),
2697
+ failureMessage && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Error2, { errorMessage: failureMessage, marginTop: true })
2602
2698
  ] });
2603
2699
  };
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 }) });
2700
+ 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
2701
  Error2.propTypes = {
2606
- errorMessage: import_prop_types10.default.string.isRequired,
2607
- marginTop: import_prop_types10.default.bool
2702
+ errorMessage: import_prop_types8.default.string.isRequired,
2703
+ marginTop: import_prop_types8.default.bool
2608
2704
  };
2609
2705
  Login.propTypes = {
2610
- setIsLoggedIn: import_prop_types10.default.func.isRequired
2706
+ setIsLoggedIn: import_prop_types8.default.func.isRequired
2611
2707
  };
2612
2708
  var Login_default = Login;
2613
2709
 
2614
2710
  // src/components/LoadingText.tsx
2615
2711
  var import_ink15 = require("ink");
2616
- var import_jsx_runtime16 = require("react/jsx-runtime");
2712
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2617
2713
  var LoadingText = ({ text = "Loading" }) => {
2618
2714
  const { stdout } = (0, import_ink15.useStdout)();
2619
2715
  const stdOutRedirected = !stdout.isTTY;
2620
2716
  if (stdOutRedirected) {
2621
2717
  return null;
2622
2718
  }
2623
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink15.Text, { children: [
2719
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink15.Text, { children: [
2624
2720
  text,
2625
2721
  "..."
2626
2722
  ] });
@@ -2628,7 +2724,7 @@ var LoadingText = ({ text = "Loading" }) => {
2628
2724
  var LoadingText_default = LoadingText;
2629
2725
 
2630
2726
  // src/components/LoginHOC.tsx
2631
- var import_jsx_runtime17 = require("react/jsx-runtime");
2727
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2632
2728
  var LoginHOC = ({ children, isInteractive = true }) => {
2633
2729
  const [isLoggedIn, setIsLoggedIn] = (0, import_react10.useState)(false);
2634
2730
  const [isEffectDone, setEffectDone] = (0, import_react10.useState)(false);
@@ -2697,19 +2793,19 @@ var LoginHOC = ({ children, isInteractive = true }) => {
2697
2793
  isAccessTokenValid();
2698
2794
  }, []);
2699
2795
  if (error && isInteractive) {
2700
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ErrorDisplay_default, { error });
2796
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ErrorDisplay, { error });
2701
2797
  }
2702
2798
  if (!isEffectDone) {
2703
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(LoadingText_default, {});
2799
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(LoadingText_default, {});
2704
2800
  }
2705
2801
  if (!isLoggedIn && isInteractive) {
2706
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Login_default, { setIsLoggedIn });
2802
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Login_default, { setIsLoggedIn });
2707
2803
  }
2708
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, { children });
2804
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children });
2709
2805
  };
2710
2806
  LoginHOC.propTypes = {
2711
- children: import_prop_types11.default.object,
2712
- isInteractive: import_prop_types11.default.bool
2807
+ children: import_prop_types9.default.object,
2808
+ isInteractive: import_prop_types9.default.bool
2713
2809
  };
2714
2810
  var LoginHOC_default = LoginHOC;
2715
2811
 
@@ -2740,41 +2836,44 @@ var ErrorBoundary = class extends import_react11.default.Component {
2740
2836
  };
2741
2837
  var ErrorBoundary_default = ErrorBoundary;
2742
2838
 
2743
- // src/components/OngoingBuildGuard.tsx
2839
+ // src/commands/build/components/OngoingBuildGuard.tsx
2744
2840
  var import_ink20 = require("ink");
2745
2841
  var import_ink_select_input = __toESM(require("ink-select-input"));
2746
- var import_prop_types14 = __toESM(require("prop-types"));
2842
+ var import_prop_types11 = __toESM(require("prop-types"));
2747
2843
  var import_react13 = require("react");
2748
2844
 
2749
2845
  // src/components/CustomSelectInputIndicator.tsx
2750
2846
  var import_ink17 = require("ink");
2751
- var import_jsx_runtime18 = require("react/jsx-runtime");
2847
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2752
2848
  function CustomSelectInputIndicator({
2753
2849
  isSelected,
2754
2850
  ...props
2755
2851
  }) {
2756
2852
  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 : " " }) });
2853
+ 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
2854
  }
2759
2855
 
2760
2856
  // src/components/CustomSelectInputItem.tsx
2761
2857
  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;
2858
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2859
+ function CustomSelectInputItem({
2860
+ isSelected,
2861
+ label,
2862
+ ...props
2863
+ }) {
2864
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink18.Text, { bold: isSelected, color: isSelected ? void 0 : "gray", ...props, children: label });
2865
+ }
2770
2866
 
2771
2867
  // src/components/ViewBuild.tsx
2772
2868
  var import_ink19 = require("ink");
2773
- var import_prop_types13 = __toESM(require("prop-types"));
2869
+ var import_prop_types10 = __toESM(require("prop-types"));
2774
2870
  var import_react12 = require("react");
2775
2871
 
2776
2872
  // src/utilities/getLatestBuildId.ts
2777
- var getLatestBuildId_default = async ({ appId, userId }) => {
2873
+ async function getLatestBuildId({
2874
+ appId,
2875
+ userId
2876
+ }) {
2778
2877
  logger_default.debug({ appId }, "getLatestBuildId");
2779
2878
  const appRef = firestore_default.doc(`users/${userId}/applications/${appId}`);
2780
2879
  const appSnapshot = await appRef.get();
@@ -2782,10 +2881,40 @@ var getLatestBuildId_default = async ({ appId, userId }) => {
2782
2881
  throw new Error(`Application with ID of ${appId} doesn't exist.`);
2783
2882
  }
2784
2883
  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;
2884
+ return buildsResult.empty ? void 0 : buildsResult.docs[0].id;
2885
+ }
2886
+
2887
+ // src/utilities/subscribeToFirebaseDoc.ts
2888
+ async function subscribeToFirebaseDoc(key, receiver) {
2889
+ return new Promise((resolve4, reject) => {
2890
+ logger_default.debug({ key }, "subscribeToFirebaseDoc");
2891
+ const unsubscribe = firestore_default.doc(key).onSnapshot(
2892
+ (snapshot) => {
2893
+ receiver({
2894
+ snapshot,
2895
+ data: snapshot.exists ? snapshot.data() : void 0
2896
+ });
2897
+ resolve4(unsubscribe);
2898
+ },
2899
+ (err) => {
2900
+ reject(err);
2901
+ }
2902
+ );
2903
+ });
2904
+ }
2905
+
2906
+ // src/utilities/subscribeToBuild.ts
2907
+ var subscribeToBuild_default = async ({ appId, buildId, onDataReceived, userId }) => {
2908
+ try {
2909
+ return await subscribeToFirebaseDoc(
2910
+ `users/${userId}/applications/${appId}/builds/${buildId}`,
2911
+ async ({ data }) => {
2912
+ onDataReceived(data);
2913
+ }
2914
+ );
2915
+ } catch (e) {
2916
+ e.message = `Failed while subscribing to build; ${e.message}`;
2917
+ throw e;
2789
2918
  }
2790
2919
  };
2791
2920
 
@@ -2820,7 +2949,7 @@ var findAppUserId = async (appId) => {
2820
2949
  var findAppUserId_default = findAppUserId;
2821
2950
 
2822
2951
  // src/components/ViewBuild.tsx
2823
- var import_jsx_runtime20 = require("react/jsx-runtime");
2952
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2824
2953
  var ViewBuild = ({ commandUsed, id, configPath }) => {
2825
2954
  const exit = useExit_default();
2826
2955
  const [
@@ -2887,11 +3016,11 @@ var ViewBuild = ({ commandUsed, id, configPath }) => {
2887
3016
  if (id) {
2888
3017
  subscribe(id);
2889
3018
  } else {
2890
- getLatestBuildId_default({ appId: config2.id, userId }).catch(onError).then((latestBuildId) => {
3019
+ getLatestBuildId({ appId: config2.id, userId }).catch(onError).then((latestBuildId) => {
2891
3020
  if (!latestBuildId) {
2892
3021
  setState((previousState) => ({
2893
3022
  ...previousState,
2894
- arbitraryMessageComponent: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { children: "There are no builds yet" })
3023
+ arbitraryMessageComponent: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink19.Text, { children: "There are no builds yet" })
2895
3024
  }));
2896
3025
  return;
2897
3026
  }
@@ -2917,13 +3046,13 @@ var ViewBuild = ({ commandUsed, id, configPath }) => {
2917
3046
  }
2918
3047
  }, [arbitraryMessageComponent, exit]);
2919
3048
  if (error) {
2920
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ErrorDisplay_default, { commandUsed, error });
3049
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorDisplay, { commandUsed, error });
2921
3050
  }
2922
3051
  if (arbitraryMessageComponent) {
2923
3052
  return arbitraryMessageComponent;
2924
3053
  }
2925
3054
  if (isLoading) {
2926
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LoadingText_default, {});
3055
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LoadingText_default, {});
2927
3056
  }
2928
3057
  const onBuildFailure = () => {
2929
3058
  if (hasBuildEverFailed) {
@@ -2934,7 +3063,7 @@ var ViewBuild = ({ commandUsed, id, configPath }) => {
2934
3063
  hasBuildEverFailed: true
2935
3064
  }));
2936
3065
  };
2937
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3066
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2938
3067
  MainLayout_default,
2939
3068
  {
2940
3069
  appId,
@@ -2943,7 +3072,7 @@ var ViewBuild = ({ commandUsed, id, configPath }) => {
2943
3072
  build,
2944
3073
  commandUsed,
2945
3074
  hasBuildEverFailed,
2946
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BuildProgress_default, { build, onBuildFailure })
3075
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(BuildProgress_default, { build, onBuildFailure })
2947
3076
  }
2948
3077
  );
2949
3078
  };
@@ -2961,7 +3090,7 @@ ViewBuild.propTypes = {
2961
3090
  );
2962
3091
  }
2963
3092
  },
2964
- commandUsed: import_prop_types13.default.string.isRequired,
3093
+ commandUsed: import_prop_types10.default.string.isRequired,
2965
3094
  shouldViewLatest: (props, propName, componentName) => {
2966
3095
  if ([props.id, props.shouldViewLatest].filter(Boolean).length !== 1) {
2967
3096
  return new Error(
@@ -2975,13 +3104,13 @@ ViewBuild.propTypes = {
2975
3104
  );
2976
3105
  }
2977
3106
  },
2978
- configPath: import_prop_types13.default.string
3107
+ configPath: import_prop_types10.default.string
2979
3108
  };
2980
3109
  var ViewBuild_default = ViewBuild;
2981
3110
 
2982
- // src/components/OngoingBuildGuard.tsx
3111
+ // src/commands/build/components/OngoingBuildGuard.tsx
2983
3112
  var import_is_ci5 = __toESM(require("is-ci"));
2984
- var import_jsx_runtime21 = require("react/jsx-runtime");
3113
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2985
3114
  var OngoingBuildGuard = ({ children, commandUsed, configPath }) => {
2986
3115
  const { isRawModeSupported } = (0, import_ink20.useStdin)();
2987
3116
  const onInput = useInput_default();
@@ -3038,20 +3167,20 @@ var OngoingBuildGuard = ({ children, commandUsed, configPath }) => {
3038
3167
  });
3039
3168
  }
3040
3169
  if (error) {
3041
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ErrorDisplay_default, { commandUsed, error });
3170
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ErrorDisplay, { commandUsed, error });
3042
3171
  }
3043
3172
  if (isLoading) {
3044
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { children: "..." });
3173
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink20.Text, { children: "..." });
3045
3174
  }
3046
3175
  if (itemChosen) {
3047
3176
  logger_default.debug({ itemChosen }, "OngoingBuildGuard component: item chosen");
3048
3177
  const build = builds.find(Boolean);
3049
3178
  if (itemChosen.value === "view") {
3050
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ViewBuild_default, { commandUsed, id: build.id });
3179
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ViewBuild_default, { commandUsed, id: build.id });
3051
3180
  } else if (itemChosen.value === "cancel") {
3052
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CancelBuild_default, { appId, commandUsed, id: build.id, children });
3181
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CancelBuild_default, { appId, commandUsed, id: build.id, children });
3053
3182
  } else if (itemChosen.value === "concurrent") {
3054
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
3183
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_jsx_runtime20.Fragment, { children });
3055
3184
  } else {
3056
3185
  setTimeout(exit, 10);
3057
3186
  }
@@ -3080,34 +3209,34 @@ var OngoingBuildGuard = ({ children, commandUsed, configPath }) => {
3080
3209
  value: "exit"
3081
3210
  });
3082
3211
  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: [
3212
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
3213
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink20.Box, { marginBottom: 1, children: [
3214
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink20.Text, { bold: true, children: multiple ? "There are ongoing builds " : "There is an ongoing build " }),
3215
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink20.Text, { children: [
3087
3216
  "(",
3088
3217
  latestBuild.appName,
3089
3218
  latestBuild.appVersion ? ` v${latestBuild.appVersion}` : "",
3090
3219
  ")"
3091
3220
  ] })
3092
3221
  ] }),
3093
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3222
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3094
3223
  import_ink_select_input.default,
3095
3224
  {
3096
3225
  indicatorComponent: CustomSelectInputIndicator,
3097
- itemComponent: CustomSelectInputItem_default,
3226
+ itemComponent: CustomSelectInputItem,
3098
3227
  items,
3099
3228
  onSelect: handleSelect
3100
3229
  }
3101
3230
  )
3102
3231
  ] });
3103
3232
  } else {
3104
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
3233
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_jsx_runtime20.Fragment, { children });
3105
3234
  }
3106
3235
  };
3107
3236
  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
3237
+ commandUsed: import_prop_types11.default.string.isRequired,
3238
+ children: import_prop_types11.default.oneOfType([import_prop_types11.default.array, import_prop_types11.default.object]),
3239
+ configPath: import_prop_types11.default.string
3111
3240
  };
3112
3241
  var OngoingBuildGuard_default = OngoingBuildGuard;
3113
3242
 
@@ -3154,92 +3283,97 @@ var useAnalyticsCommand = (command, flags = {}, properties = {}) => {
3154
3283
  return { hasAttemptedTracking };
3155
3284
  };
3156
3285
 
3157
- // src/commands/BuildCommand.tsx
3158
- var import_jsx_runtime22 = require("react/jsx-runtime");
3159
- var BuildCommand = ({
3160
- shouldCodeSign = true,
3161
- configPath = null
3162
- }) => {
3286
+ // src/commands/build/BuildCommand.tsx
3287
+ var import_jsx_runtime21 = require("react/jsx-runtime");
3288
+ function BuildCommand({
3289
+ configPath,
3290
+ exitAfterUploading,
3291
+ onBuildFinishedWebhook,
3292
+ shouldCodeSign
3293
+ }) {
3163
3294
  checkIfReactIsUsable_default();
3164
3295
  useAnalyticsCommand("build", {
3296
+ async: exitAfterUploading,
3165
3297
  codeSign: shouldCodeSign,
3166
- config: configPath
3298
+ config: configPath,
3299
+ webhook: onBuildFinishedWebhook
3167
3300
  });
3168
3301
  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,
3302
+ 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)(
3303
+ Build,
3171
3304
  {
3172
3305
  commandUsed,
3173
- shouldCodeSign,
3174
- configPath
3306
+ configPath,
3307
+ exitAfterUploading,
3308
+ onBuildFinishedWebhook,
3309
+ shouldCodeSign
3175
3310
  }
3176
3311
  ) }) }) });
3177
- };
3178
- var BuildCommand_default = BuildCommand;
3312
+ }
3179
3313
 
3180
3314
  // src/components/ViewBuilds.tsx
3181
3315
  var import_ink26 = require("ink");
3182
- var import_prop_types20 = __toESM(require("prop-types"));
3316
+ var import_prop_types17 = __toESM(require("prop-types"));
3183
3317
  var import_react16 = require("react");
3184
3318
 
3185
3319
  // src/components/Table.tsx
3186
3320
  var import_ink24 = require("ink");
3187
- var import_prop_types19 = __toESM(require("prop-types"));
3321
+ var import_prop_types16 = __toESM(require("prop-types"));
3188
3322
 
3189
3323
  // src/components/TableEnd.tsx
3190
3324
  var import_ink21 = require("ink");
3191
- var import_prop_types15 = __toESM(require("prop-types"));
3192
- var import_jsx_runtime23 = require("react/jsx-runtime");
3325
+ var import_prop_types12 = __toESM(require("prop-types"));
3326
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3193
3327
  var TableEnd = ({ keyDetails, ...props }) => {
3194
3328
  let content = "\u2514";
3195
3329
  content += Object.values(keyDetails).map(({ width }) => "\u2500".repeat(width + 2)).join("\u2534");
3196
3330
  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 }) });
3331
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Box, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { children: content }) });
3198
3332
  };
3199
3333
  TableEnd.propTypes = {
3200
- keyDetails: import_prop_types15.default.object.isRequired
3334
+ keyDetails: import_prop_types12.default.object.isRequired
3201
3335
  };
3202
3336
  var TableEnd_default = TableEnd;
3203
3337
 
3204
3338
  // src/components/TableHead.tsx
3205
3339
  var import_ink22 = require("ink");
3206
- var import_prop_types16 = __toESM(require("prop-types"));
3340
+ var import_prop_types13 = __toESM(require("prop-types"));
3207
3341
  var import_react15 = require("react");
3208
- var import_jsx_runtime24 = require("react/jsx-runtime");
3342
+ var import_jsx_runtime23 = require("react/jsx-runtime");
3209
3343
  var TableHead = ({ keyDetails, ...props }) => {
3210
3344
  let topLine = "\u250C";
3211
3345
  topLine += Object.values(keyDetails).map(({ width }) => "\u2500".repeat(width + 2)).join("\u252C");
3212
3346
  topLine += "\u2510";
3213
3347
  const contentLineElements = Object.values(keyDetails).map(
3214
3348
  ({ 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 }) })
3349
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react15.Fragment, { children: [
3350
+ index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { children: " \u2502 " }) : null,
3351
+ /* @__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
3352
  ] }, key);
3219
3353
  }
3220
3354
  );
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 " }),
3355
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_ink22.Box, { flexDirection: "column", ...props, children: [
3356
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { children: topLine }) }),
3357
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_ink22.Box, { children: [
3358
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { children: "\u2502 " }),
3225
3359
  contentLineElements,
3226
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { children: " \u2502" })
3360
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { children: " \u2502" })
3227
3361
  ] }) })
3228
3362
  ] });
3229
3363
  };
3230
3364
  TableHead.propTypes = {
3231
- bottomLinePrefix: import_prop_types16.default.string,
3232
- keyDetails: import_prop_types16.default.object.isRequired
3365
+ bottomLinePrefix: import_prop_types13.default.string,
3366
+ keyDetails: import_prop_types13.default.object.isRequired
3233
3367
  };
3234
3368
  var TableHead_default = TableHead;
3235
3369
 
3236
3370
  // src/components/TableBody.tsx
3237
- var import_prop_types18 = __toESM(require("prop-types"));
3371
+ var import_prop_types15 = __toESM(require("prop-types"));
3238
3372
 
3239
3373
  // src/components/TableRow.tsx
3240
3374
  var import_ink23 = require("ink");
3241
- var import_prop_types17 = __toESM(require("prop-types"));
3242
- var import_jsx_runtime25 = require("react/jsx-runtime");
3375
+ var import_prop_types14 = __toESM(require("prop-types"));
3376
+ var import_jsx_runtime24 = require("react/jsx-runtime");
3243
3377
  var TableRow = ({
3244
3378
  data,
3245
3379
  getCellTextProps = ({ props }) => props,
@@ -3249,12 +3383,12 @@ var TableRow = ({
3249
3383
  let topLine = "\u251C";
3250
3384
  topLine += Object.values(keyDetails).map(({ width }) => "\u2500".repeat(width + 2)).join("\u253C");
3251
3385
  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 " }),
3386
+ const content = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
3387
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { children: "\u2502 " }),
3254
3388
  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)(
3389
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink23.Box, { children: [
3390
+ index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { children: " \u2502 " }) : null,
3391
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Box, { width: keyDetails[key].width, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3258
3392
  import_ink23.Text,
3259
3393
  {
3260
3394
  ...getCellTextProps({
@@ -3268,25 +3402,25 @@ var TableRow = ({
3268
3402
  ) })
3269
3403
  ] }, key);
3270
3404
  }),
3271
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Text, { children: " \u2502" })
3405
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { children: " \u2502" })
3272
3406
  ] });
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 })
3407
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_jsx_runtime24.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink23.Box, { flexDirection: "column", children: [
3408
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { children: topLine }),
3409
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Box, { children: content })
3276
3410
  ] }) });
3277
3411
  };
3278
3412
  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
3413
+ data: import_prop_types14.default.object.isRequired,
3414
+ getCellTextProps: import_prop_types14.default.func,
3415
+ keyDetails: import_prop_types14.default.object.isRequired,
3416
+ textProps: import_prop_types14.default.object
3283
3417
  };
3284
3418
  var TableRow_default = TableRow;
3285
3419
 
3286
3420
  // src/components/TableBody.tsx
3287
- var import_jsx_runtime26 = require("react/jsx-runtime");
3421
+ var import_jsx_runtime25 = require("react/jsx-runtime");
3288
3422
  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)(
3423
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: data.map((rowData, index) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3290
3424
  TableRow_default,
3291
3425
  {
3292
3426
  data: rowData,
@@ -3310,8 +3444,8 @@ TableBody.propTypes = {
3310
3444
  );
3311
3445
  }
3312
3446
  },
3313
- getCellTextProps: import_prop_types18.default.func,
3314
- keyDetails: import_prop_types18.default.object.isRequired
3447
+ getCellTextProps: import_prop_types15.default.func,
3448
+ keyDetails: import_prop_types15.default.object.isRequired
3315
3449
  };
3316
3450
  var TableBody_default = TableBody;
3317
3451
 
@@ -3331,12 +3465,12 @@ var getKeyDetails_default = (data) => {
3331
3465
  };
3332
3466
 
3333
3467
  // src/components/Table.tsx
3334
- var import_jsx_runtime27 = require("react/jsx-runtime");
3468
+ var import_jsx_runtime26 = require("react/jsx-runtime");
3335
3469
  var Table = ({ data, getCellTextProps }) => {
3336
3470
  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)(
3471
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_ink24.Box, { flexDirection: "column", children: [
3472
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TableHead_default, { keyDetails }),
3473
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3340
3474
  TableBody_default,
3341
3475
  {
3342
3476
  data,
@@ -3344,7 +3478,7 @@ var Table = ({ data, getCellTextProps }) => {
3344
3478
  keyDetails
3345
3479
  }
3346
3480
  ),
3347
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(TableEnd_default, { keyDetails })
3481
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TableEnd_default, { keyDetails })
3348
3482
  ] });
3349
3483
  };
3350
3484
  Table.propTypes = {
@@ -3361,7 +3495,7 @@ Table.propTypes = {
3361
3495
  );
3362
3496
  }
3363
3497
  },
3364
- getCellTextProps: import_prop_types19.default.func
3498
+ getCellTextProps: import_prop_types16.default.func
3365
3499
  };
3366
3500
  var Table_default = Table;
3367
3501
 
@@ -3410,7 +3544,7 @@ var import_chalk2 = __toESM(require("chalk"));
3410
3544
  var React3 = __toESM(require("react"));
3411
3545
  var import_ink25 = require("ink");
3412
3546
  var import_util = __toESM(require("util"));
3413
- var import_jsx_runtime28 = require("react/jsx-runtime");
3547
+ var import_jsx_runtime27 = require("react/jsx-runtime");
3414
3548
  var supportsColor = import_chalk2.default.stderr.supportsColor;
3415
3549
  var SyntaxHighlight = ({
3416
3550
  object,
@@ -3420,12 +3554,12 @@ var SyntaxHighlight = ({
3420
3554
  const highlightedCode = React3.useMemo(() => {
3421
3555
  return stdout.isTTY ? import_util.default.inspect(object, { colors, depth: 6 }) : JSON.stringify(object, null, 2);
3422
3556
  }, [colors, object, stdout.isTTY]);
3423
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ink25.Text, { wrap: "end", children: highlightedCode });
3557
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ink25.Text, { wrap: "end", children: highlightedCode });
3424
3558
  };
3425
3559
  var SyntaxHighlight_default = SyntaxHighlight;
3426
3560
 
3427
3561
  // src/components/ViewBuilds.tsx
3428
- var import_jsx_runtime29 = require("react/jsx-runtime");
3562
+ var import_jsx_runtime28 = require("react/jsx-runtime");
3429
3563
  var ViewBuilds = ({
3430
3564
  commandUsed,
3431
3565
  configPath,
@@ -3554,10 +3688,10 @@ var ViewBuilds = ({
3554
3688
  }
3555
3689
  }, [exit, isLoading, shouldExitAfterViewingBuilds, isInitialLoadComplete]);
3556
3690
  if (error) {
3557
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ErrorDisplay_default, { commandUsed, error });
3691
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ErrorDisplay, { commandUsed, error });
3558
3692
  }
3559
3693
  if (!isInitialLoadComplete) {
3560
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(LoadingText_default, {});
3694
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(LoadingText_default, {});
3561
3695
  }
3562
3696
  if (builds.length) {
3563
3697
  const formatData = (builds2) => builds2.map((build) => ({
@@ -3586,19 +3720,19 @@ var ViewBuilds = ({
3586
3720
  }
3587
3721
  return result;
3588
3722
  };
3589
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
3590
- format === "table" ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3723
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
3724
+ format === "table" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3591
3725
  Table_default,
3592
3726
  {
3593
3727
  getCellTextProps,
3594
3728
  data: formatData(builds)
3595
3729
  }
3596
3730
  ) : 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
3731
+ format === "json" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SyntaxHighlight_default, { object: removeAppBuilderLibConfig(builds) }) : null,
3732
+ !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
3733
  ] });
3600
3734
  } else {
3601
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ink26.Text, { children: "There are no builds yet" });
3735
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ink26.Text, { children: "There are no builds yet" });
3602
3736
  }
3603
3737
  };
3604
3738
  var removeAppBuilderLibConfig = (builds) => {
@@ -3616,16 +3750,16 @@ var removeAppBuilderLibConfig = (builds) => {
3616
3750
  });
3617
3751
  };
3618
3752
  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
3753
+ commandUsed: import_prop_types17.default.string.isRequired,
3754
+ configPath: import_prop_types17.default.string,
3755
+ count: import_prop_types17.default.number,
3756
+ format: import_prop_types17.default.string,
3757
+ exit: import_prop_types17.default.bool
3624
3758
  };
3625
3759
  var ViewBuilds_default = ViewBuilds;
3626
3760
 
3627
3761
  // src/commands/BuildsCommand.tsx
3628
- var import_jsx_runtime30 = require("react/jsx-runtime");
3762
+ var import_jsx_runtime29 = require("react/jsx-runtime");
3629
3763
  var BuildsCommand = ({
3630
3764
  id,
3631
3765
  shouldViewLatest,
@@ -3647,10 +3781,10 @@ var BuildsCommand = ({
3647
3781
  let commandUsed = "todesktop builds";
3648
3782
  if (id) {
3649
3783
  commandUsed += " <id>";
3650
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ViewBuild_default, { commandUsed, id, configPath });
3784
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ViewBuild_default, { commandUsed, id, configPath });
3651
3785
  } else if (shouldViewLatest) {
3652
3786
  commandUsed += " --latest";
3653
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3787
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3654
3788
  ViewBuild_default,
3655
3789
  {
3656
3790
  commandUsed,
@@ -3659,7 +3793,7 @@ var BuildsCommand = ({
3659
3793
  }
3660
3794
  );
3661
3795
  } else {
3662
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3796
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3663
3797
  ViewBuilds_default,
3664
3798
  {
3665
3799
  commandUsed,
@@ -3671,14 +3805,14 @@ var BuildsCommand = ({
3671
3805
  );
3672
3806
  }
3673
3807
  };
3674
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(LoginHOC_default, { children: getContents() }) });
3808
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(LoginHOC_default, { children: getContents() }) });
3675
3809
  };
3676
3810
  var BuildsCommand_default = BuildsCommand;
3677
3811
 
3678
3812
  // src/commands/LogoutCommand.tsx
3679
3813
  var import_react17 = require("react");
3680
3814
  var import_ink27 = require("ink");
3681
- var import_jsx_runtime31 = require("react/jsx-runtime");
3815
+ var import_jsx_runtime30 = require("react/jsx-runtime");
3682
3816
  var Logout = () => {
3683
3817
  const exit = useExit_default();
3684
3818
  const { hasAttemptedTracking } = useAnalyticsCommand("logout");
@@ -3689,10 +3823,10 @@ var Logout = () => {
3689
3823
  exit();
3690
3824
  }
3691
3825
  }, [exit, hasAttemptedTracking]);
3692
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ink27.Text, { children: "Log out successful" });
3826
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ink27.Text, { children: "Log out successful" });
3693
3827
  };
3694
3828
  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, {}) }) });
3829
+ 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
3830
  };
3697
3831
  var LogoutCommand_default = LogoutWrapper;
3698
3832
 
@@ -3703,9 +3837,9 @@ var import_react18 = require("react");
3703
3837
  // src/components/SelectTable.tsx
3704
3838
  var import_ink28 = require("ink");
3705
3839
  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 });
3840
+ var import_prop_types18 = __toESM(require("prop-types"));
3841
+ var import_jsx_runtime31 = require("react/jsx-runtime");
3842
+ var CustomIndicator = (props) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(CustomSelectInputIndicator, { marginTop: 1, ...props });
3709
3843
  var SelectTable = ({ data, onSelect }) => {
3710
3844
  const keyDetails = getKeyDetails_default(data);
3711
3845
  const getSelectItems = () => {
@@ -3722,7 +3856,7 @@ var SelectTable = ({ data, onSelect }) => {
3722
3856
  index,
3723
3857
  isSelected
3724
3858
  }) => {
3725
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3859
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3726
3860
  TableRow_default,
3727
3861
  {
3728
3862
  data: data[index],
@@ -3732,9 +3866,9 @@ var SelectTable = ({ data, onSelect }) => {
3732
3866
  index
3733
3867
  );
3734
3868
  };
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)(
3869
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_ink28.Box, { flexDirection: "column", children: [
3870
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(TableHead_default, { keyDetails, marginLeft: 2 }),
3871
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3738
3872
  import_ink_select_input2.default,
3739
3873
  {
3740
3874
  indicatorComponent: CustomIndicator,
@@ -3743,7 +3877,7 @@ var SelectTable = ({ data, onSelect }) => {
3743
3877
  onSelect
3744
3878
  }
3745
3879
  ),
3746
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TableEnd_default, { keyDetails, marginLeft: 2 })
3880
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(TableEnd_default, { keyDetails, marginLeft: 2 })
3747
3881
  ] });
3748
3882
  };
3749
3883
  SelectTable.propTypes = {
@@ -3760,12 +3894,49 @@ SelectTable.propTypes = {
3760
3894
  );
3761
3895
  }
3762
3896
  },
3763
- onSelect: import_prop_types21.default.func
3897
+ onSelect: import_prop_types18.default.func
3764
3898
  };
3765
3899
  var SelectTable_default = SelectTable;
3766
3900
 
3901
+ // src/utilities/getBuildById.ts
3902
+ async function getBuildById({
3903
+ appId,
3904
+ buildId,
3905
+ userId
3906
+ }) {
3907
+ logger_default.debug({ appId, buildId }, "getBuildById");
3908
+ const snapshot = await firestore_default.doc(`users/${userId}/applications/${appId}/builds/${buildId}`).get();
3909
+ if (!snapshot.exists) {
3910
+ return null;
3911
+ }
3912
+ return snapshot.data();
3913
+ }
3914
+
3915
+ // src/utilities/getLatestReleasedBuild.ts
3916
+ async function getLatestReleasedBuild({
3917
+ appId,
3918
+ userId
3919
+ }) {
3920
+ var _a, _b;
3921
+ const appRef = firestore_default.doc(`users/${userId}/applications/${appId}`);
3922
+ const appSnapshot = await appRef.get();
3923
+ const app = appSnapshot.exists ? appSnapshot.data() : void 0;
3924
+ if (!app) {
3925
+ throw new Error(`Application with ID of ${appId} doesn't exist.`);
3926
+ }
3927
+ if ((_a = app.meta) == null ? void 0 : _a.latestReleaseBuildId) {
3928
+ return await getBuildById({
3929
+ buildId: (_b = app.meta) == null ? void 0 : _b.latestReleaseBuildId,
3930
+ appId,
3931
+ userId
3932
+ }) || void 0;
3933
+ }
3934
+ const buildsResult = await appRef.collection("builds").orderBy("releasedAt", "desc").limit(1).get();
3935
+ return buildsResult.empty ? void 0 : buildsResult.docs[0];
3936
+ }
3937
+
3767
3938
  // src/components/BuildPicker.tsx
3768
- var import_jsx_runtime33 = require("react/jsx-runtime");
3939
+ var import_jsx_runtime32 = require("react/jsx-runtime");
3769
3940
  function BuildPicker({
3770
3941
  buildFilter,
3771
3942
  buildId,
@@ -3773,13 +3944,14 @@ function BuildPicker({
3773
3944
  commandUsed,
3774
3945
  configPath,
3775
3946
  loadData = loadPickerData,
3947
+ noBuildsDescription = null,
3776
3948
  question = "Which build would you like to test?"
3777
3949
  }) {
3778
3950
  const exit = useExit_default();
3779
3951
  const onInput = useInput_default();
3780
3952
  const isRealIdPassed = buildId && buildId !== "latest";
3781
3953
  const [state, setState] = (0, import_react18.useState)({
3782
- selectedBuildId: isRealIdPassed ? buildId : null,
3954
+ selectedBuildId: isRealIdPassed ? buildId : void 0,
3783
3955
  state: isRealIdPassed ? "selected" : "loading"
3784
3956
  });
3785
3957
  (0, import_react18.useEffect)(() => {
@@ -3789,44 +3961,49 @@ function BuildPicker({
3789
3961
  configPath,
3790
3962
  state: state.state,
3791
3963
  updateState
3792
- });
3964
+ }).catch(logger_default.error);
3793
3965
  }, []);
3794
- onInput((input, key) => {
3966
+ onInput((_, key) => {
3795
3967
  if (key.escape && ["show-builds", "loading"].includes(state.state)) {
3796
3968
  exit();
3797
3969
  }
3798
3970
  });
3799
3971
  function updateState(changes) {
3800
- setState((previousState) => ({ ...previousState, ...changes }));
3972
+ setState(
3973
+ (previousState) => ({ ...previousState, ...changes })
3974
+ );
3975
+ if (changes.state && ["error", "no-builds"].includes(changes.state)) {
3976
+ setTimeout(() => exit(), 10);
3977
+ }
3801
3978
  }
3802
3979
  function onSelect(item) {
3803
3980
  updateState({ selectedBuildId: item.value.ID, state: "selected" });
3804
3981
  }
3805
3982
  switch (state.state) {
3806
3983
  case "error":
3807
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ErrorDisplay_default, { commandUsed, error: state.error });
3984
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ErrorDisplay, { commandUsed, error: state.error });
3808
3985
  case "loading":
3809
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(LoadingText_default, {});
3986
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(LoadingText_default, {});
3810
3987
  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)" })
3988
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink29.Box, { children: [
3989
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Text, { children: "No eligible builds found " }),
3990
+ noBuildsDescription && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Text, { dimColor: true, children: noBuildsDescription })
3814
3991
  ] });
3815
3992
  case "selected":
3816
3993
  return children(state.selectedBuildId);
3817
3994
  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: [
3995
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
3996
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Text, { children: question }) }),
3997
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(SelectTable_default, { data: state.builds, onSelect }),
3998
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink29.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink29.Text, { dimColor: true, children: [
3822
3999
  "Showing the latest ",
3823
4000
  state.builds.length,
3824
4001
  " unreleased successful builds"
3825
4002
  ] }) })
3826
4003
  ] });
3827
4004
  default:
3828
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3829
- ErrorDisplay_default,
4005
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4006
+ ErrorDisplay,
3830
4007
  {
3831
4008
  commandUsed,
3832
4009
  error: new Error(`Unknown state ${state.state}`)
@@ -3859,8 +4036,8 @@ async function loadPickerData({
3859
4036
  state: builds.length > 0 ? "show-builds" : "no-builds"
3860
4037
  });
3861
4038
  }
3862
- } catch (error) {
3863
- updateState({ error, state: "error" });
4039
+ } catch (e) {
4040
+ updateState({ error: CliError.from(e), state: "error" });
3864
4041
  }
3865
4042
  }
3866
4043
  async function getLatestBuild({
@@ -3868,7 +4045,7 @@ async function getLatestBuild({
3868
4045
  }) {
3869
4046
  const { id: appId } = getProjectConfig(configPath).config;
3870
4047
  const { id: userId } = await findAppUserId_default(appId);
3871
- return getLatestBuildId_default({ appId, userId });
4048
+ return getLatestBuildId({ appId, userId });
3872
4049
  }
3873
4050
  async function getBuildItems({
3874
4051
  buildFilter = () => true,
@@ -3876,13 +4053,14 @@ async function getBuildItems({
3876
4053
  }) {
3877
4054
  const { id: appId } = getProjectConfig(configPath).config;
3878
4055
  const { id: userId, label: userName } = await findAppUserId_default(appId);
4056
+ const latestReleasedBuild = await getLatestReleasedBuild({ appId, userId });
3879
4057
  const rawBuilds = await getBuilds({
3880
4058
  addWhereClauses: (query) => query.where("status", "==", "succeeded"),
3881
4059
  appId,
3882
4060
  limit: 50,
3883
4061
  userId
3884
4062
  });
3885
- return rawBuilds.filter(buildFilter).slice(0, 5).map((build) => ({
4063
+ return rawBuilds.filter((build) => buildFilter({ build, latestReleasedBuild })).slice(0, 5).map((build) => ({
3886
4064
  ID: build.id,
3887
4065
  Version: build.appVersion || "unknown",
3888
4066
  "Creation date": getRelativeDateFromDateString_default(build.createdAt),
@@ -3890,47 +4068,9 @@ async function getBuildItems({
3890
4068
  }));
3891
4069
  }
3892
4070
 
3893
- // src/commands/release/components/ReleaseBuild.tsx
3894
- var import_ink31 = require("ink");
4071
+ // src/commands/release/components/ReleaseBuildLogic.tsx
3895
4072
  var import_react19 = require("react");
3896
4073
 
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
4074
  // src/utilities/getBuildAttributes.ts
3935
4075
  async function getBuildAttributes({
3936
4076
  buildId,
@@ -3939,7 +4079,8 @@ async function getBuildAttributes({
3939
4079
  const {
3940
4080
  id: appId,
3941
4081
  nodeVersion,
3942
- npmVersion
4082
+ npmVersion,
4083
+ pnpmVersion
3943
4084
  } = getProjectConfig(configPath).config;
3944
4085
  const { id: userId } = await findAppUserId_default(appId);
3945
4086
  const { uid: contextUserId } = currentUser();
@@ -3951,6 +4092,7 @@ async function getBuildAttributes({
3951
4092
  contextUserId,
3952
4093
  nodeVersion,
3953
4094
  npmVersion,
4095
+ pnpmVersion,
3954
4096
  userId
3955
4097
  };
3956
4098
  }
@@ -3963,151 +4105,150 @@ async function fetchBuild({
3963
4105
  if (!build) {
3964
4106
  throw new Error(`No such build ${buildId} for application ${appId}`);
3965
4107
  }
3966
- return build;
4108
+ return build;
4109
+ }
4110
+
4111
+ // src/commands/release/utilities/releaseBuild.ts
4112
+ async function releaseBuild({
4113
+ appId,
4114
+ buildId
4115
+ }) {
4116
+ try {
4117
+ await getCallableFirebaseFunction_default("releaseBuild")({ appId, buildId });
4118
+ } catch (e) {
4119
+ if (isFirebaseFunctionError(e) && (e.code === "failed-precondition" || e.code === "not-found")) {
4120
+ throw CliError.from(e, "build-error");
4121
+ } else {
4122
+ throw new CliError("Unexpected internal error while releasing build");
4123
+ }
4124
+ }
4125
+ }
4126
+ function isFirebaseFunctionError(error) {
4127
+ return Boolean(
4128
+ typeof error === "object" && error && "code" in error && error.code === "string"
4129
+ );
4130
+ }
4131
+
4132
+ // src/commands/release/components/ReleaseBuildView.tsx
4133
+ var import_ink31 = require("ink");
4134
+
4135
+ // src/components/BuildError.tsx
4136
+ var import_ink30 = require("ink");
4137
+ var import_ink_link4 = __toESM(require("ink-link"));
4138
+ var import_jsx_runtime33 = require("react/jsx-runtime");
4139
+ function BuildError({
4140
+ build,
4141
+ description = "Can't release",
4142
+ message: message2
4143
+ }) {
4144
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ink30.Box, { flexDirection: "column", children: [
4145
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ink30.Text, { bold: true, color: "red", children: [
4146
+ description,
4147
+ " ",
4148
+ build.appName,
4149
+ " v",
4150
+ build.appVersion
4151
+ ] }),
4152
+ /* @__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 }) }),
4153
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ink30.Text, { bold: true, children: "See web UI for more information: " }),
4154
+ /* @__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 }) })
4155
+ ] });
4156
+ }
4157
+
4158
+ // src/commands/release/components/ReleaseBuildView.tsx
4159
+ var import_jsx_runtime34 = require("react/jsx-runtime");
4160
+ function ReleaseBuildView({
4161
+ commandUsed,
4162
+ state
4163
+ }) {
4164
+ var _a, _b;
4165
+ switch (state.state) {
4166
+ case "build-error": {
4167
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4168
+ BuildError,
4169
+ {
4170
+ build: state.build,
4171
+ description: "Can't release build",
4172
+ message: ((_a = state.error) == null ? void 0 : _a.message) || "Internal build error"
4173
+ }
4174
+ );
4175
+ }
4176
+ case "complete":
4177
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink31.Text, { bold: true, color: "greenBright", children: "Released!" });
4178
+ case "error":
4179
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4180
+ ErrorDisplay,
4181
+ {
4182
+ commandUsed,
4183
+ error: { stack: (_b = state.error) == null ? void 0 : _b.stack }
4184
+ }
4185
+ );
4186
+ case "loading":
4187
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LoadingText_default, {});
4188
+ case "releasing":
4189
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LoadingText_default, { text: "Releasing" });
4190
+ default:
4191
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4192
+ ErrorDisplay,
4193
+ {
4194
+ commandUsed,
4195
+ error: new Error(`Unknown state ${state}`)
4196
+ }
4197
+ );
4198
+ }
3967
4199
  }
3968
4200
 
3969
- // src/commands/release/components/ReleaseBuild.tsx
4201
+ // src/commands/release/components/ReleaseBuildLogic.tsx
3970
4202
  var import_jsx_runtime35 = require("react/jsx-runtime");
3971
- function ReleaseBuild({
4203
+ function ReleaseBuildLogic({
4204
+ buildId,
3972
4205
  commandUsed,
3973
- id,
3974
4206
  configPath
3975
4207
  }) {
3976
4208
  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]);
4209
+ const [state, setState] = (0, import_react19.useState)({ state: "loading" });
4071
4210
  (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);
4211
+ releaseBuildWorkflow({ configPath, buildId, updateState }).catch(
4212
+ logger_default.error
4213
+ );
4214
+ }, []);
4215
+ function updateState(changes) {
4216
+ setState(
4217
+ (previousState) => ({ ...previousState, ...changes })
4218
+ );
4219
+ if (changes.state && ["build-error", "complete"].includes(changes.state)) {
4220
+ setTimeout(() => exit(), 10);
4078
4221
  }
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
4222
  }
4089
- if (isComplete) {
4090
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ink31.Text, { bold: true, color: "greenBright", children: "Released!" });
4223
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReleaseBuildView, { commandUsed, state });
4224
+ }
4225
+ async function releaseBuildWorkflow({
4226
+ buildId,
4227
+ configPath,
4228
+ updateState
4229
+ }) {
4230
+ try {
4231
+ const { appId, build } = await getBuildAttributes({
4232
+ buildId,
4233
+ configPath
4234
+ });
4235
+ updateState({ build });
4236
+ updateState({ state: "releasing" });
4237
+ await releaseBuild({ appId, buildId });
4238
+ logForCI_default("Released!");
4239
+ updateState({ state: "complete" });
4240
+ } catch (e) {
4241
+ const error = CliError.from(e);
4242
+ updateState({ error, state: error.type });
4091
4243
  }
4092
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ink31.Text, { children: "..." });
4093
4244
  }
4094
4245
 
4095
4246
  // src/commands/release/components/ReleaseConfirmation.tsx
4096
- var import_ink33 = require("ink");
4097
- var import_react20 = require("react");
4247
+ var import_ink34 = require("ink");
4248
+ var import_react21 = require("react");
4098
4249
 
4099
4250
  // src/commands/smoke-test/utilities/build.ts
4100
4251
  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
4252
  var MIN_RUNTIME_VERSION = "1.2.1-1";
4112
4253
  function isBuildTestable(build) {
4113
4254
  try {
@@ -4173,17 +4314,17 @@ function makeProgress(build) {
4173
4314
  }
4174
4315
  function validateBuild(build) {
4175
4316
  if (build.status !== "succeeded") {
4176
- throw new SmokeError(
4317
+ throw new CliError(
4177
4318
  `The build must be completed successfully. Actual build status: ${build.status}`
4178
4319
  );
4179
4320
  }
4180
4321
  if (!build.todesktopRuntimeVersionSpecified) {
4181
- throw new SmokeError(
4322
+ throw new CliError(
4182
4323
  "The build has no todesktopRuntimeVersionSpecified set. Please make sure @todesktop/runtime is installed as a dependency and make a new build"
4183
4324
  );
4184
4325
  }
4185
4326
  if (!isRuntimeVerRangeAllowed(build.todesktopRuntimeVersionSpecified)) {
4186
- throw new SmokeError(
4327
+ throw new CliError(
4187
4328
  `This build should have @todesktop/runtime version ${MIN_RUNTIME_VERSION} or later to run smoke tests.`
4188
4329
  );
4189
4330
  }
@@ -4197,10 +4338,115 @@ function isRuntimeVerRangeAllowed(runtimeVerRange) {
4197
4338
  });
4198
4339
  }
4199
4340
 
4200
- // src/commands/release/components/YesNoConfirmation.tsx
4341
+ // src/commands/release/utilities/analyzeRelease.ts
4342
+ var import_semver2 = __toESM(require("semver"));
4343
+ var MIN_RUNTIME_VERSION_FOR_DOWNGRADE = "1.4.1-1";
4344
+ function analyzeRelease({
4345
+ build,
4346
+ latestReleasedBuild
4347
+ }) {
4348
+ const result = {
4349
+ downgradeAllowed: Boolean(
4350
+ (latestReleasedBuild == null ? void 0 : latestReleasedBuild.todesktopRuntimeVersionUsed) && import_semver2.default.gte(
4351
+ latestReleasedBuild.todesktopRuntimeVersionUsed,
4352
+ MIN_RUNTIME_VERSION_FOR_DOWNGRADE
4353
+ )
4354
+ ),
4355
+ error: "",
4356
+ isAlreadyReleased: Boolean(build.releasedAt),
4357
+ isAlreadyTheLatest: build.id === (latestReleasedBuild == null ? void 0 : latestReleasedBuild.id),
4358
+ isOlderThanLatest: Boolean(
4359
+ latestReleasedBuild && latestReleasedBuild.appVersion && build.appVersion && import_semver2.default.gt(latestReleasedBuild.appVersion, build.appVersion)
4360
+ ),
4361
+ isReleasable: false,
4362
+ isSucceeded: build.status === "succeeded",
4363
+ isTheSameVersionAsLatest: Boolean(
4364
+ build.appVersion && build.appVersion === (latestReleasedBuild == null ? void 0 : latestReleasedBuild.appVersion)
4365
+ )
4366
+ };
4367
+ if (!result.isSucceeded) {
4368
+ return {
4369
+ ...result,
4370
+ error: `The build must have completed successfully. Actual build status: ${build.status}.`
4371
+ };
4372
+ }
4373
+ if (result.isAlreadyTheLatest) {
4374
+ return {
4375
+ ...result,
4376
+ error: "This build is already the latest released build."
4377
+ };
4378
+ }
4379
+ if (result.isTheSameVersionAsLatest) {
4380
+ return {
4381
+ ...result,
4382
+ error: `This build has the same version as the latest released build (build ID: ${latestReleasedBuild == null ? void 0 : latestReleasedBuild.id}, version: ${build.appVersion}).`
4383
+ };
4384
+ }
4385
+ if (!result.downgradeAllowed && result.isAlreadyReleased) {
4386
+ return { ...result, error: "This build has already been released." };
4387
+ }
4388
+ if (!result.downgradeAllowed && result.isOlderThanLatest && latestReleasedBuild) {
4389
+ return {
4390
+ ...result,
4391
+ error: `App version (${build.appVersion}) is not newer than latest released version (${latestReleasedBuild.appVersion}) (build ID: ${latestReleasedBuild.id}).`
4392
+ };
4393
+ }
4394
+ return { ...result, isReleasable: true };
4395
+ }
4396
+
4397
+ // src/commands/release/components/DowngradeConfirmation.tsx
4201
4398
  var import_ink32 = require("ink");
4202
- var import_ink_select_input3 = __toESM(require("ink-select-input"));
4399
+ var import_ink_text_input2 = __toESM(require("ink-text-input"));
4400
+ var import_react20 = require("react");
4203
4401
  var import_jsx_runtime36 = require("react/jsx-runtime");
4402
+ function DowngradeConfirmation({
4403
+ build,
4404
+ latestReleasedBuild,
4405
+ confirmDowngrade: confirmDowngrade2
4406
+ }) {
4407
+ const [confirmText, setConfirmText] = (0, import_react20.useState)("");
4408
+ const [showError, setShowError] = (0, import_react20.useState)(false);
4409
+ const buildVersion = build.appVersion;
4410
+ const latestVersion2 = latestReleasedBuild.appVersion;
4411
+ function onType(text) {
4412
+ setShowError(false);
4413
+ setConfirmText(text);
4414
+ }
4415
+ function onSubmit(text) {
4416
+ if (text.trim() === buildVersion) {
4417
+ confirmDowngrade2();
4418
+ } else {
4419
+ setShowError(true);
4420
+ }
4421
+ }
4422
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ink32.Box, { flexDirection: "column", children: [
4423
+ /* @__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: [
4424
+ "You're trying to release the build ",
4425
+ buildVersion,
4426
+ ", which is older than the latest published version ",
4427
+ latestVersion2,
4428
+ ". 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",
4429
+ MIN_RUNTIME_VERSION_FOR_DOWNGRADE,
4430
+ "."
4431
+ ] }) }),
4432
+ /* @__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?" }) }),
4433
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ink32.Text, { children: [
4434
+ "To continue, type the text ",
4435
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { bold: true, children: buildVersion }),
4436
+ " below to confirm:"
4437
+ ] }) }),
4438
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ink32.Box, { children: [
4439
+ /* @__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:" }) }),
4440
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink_text_input2.default, { value: confirmText, onChange: onType, onSubmit })
4441
+ ] }),
4442
+ 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" }) })
4443
+ ] });
4444
+ }
4445
+
4446
+ // src/commands/release/components/YesNoConfirmation.tsx
4447
+ var import_ink33 = require("ink");
4448
+ var import_ink_select_input3 = __toESM(require("ink-select-input"));
4449
+ var import_jsx_runtime37 = require("react/jsx-runtime");
4204
4450
  function YesNoConfirmation({
4205
4451
  onYes,
4206
4452
  title,
@@ -4218,107 +4464,175 @@ function YesNoConfirmation({
4218
4464
  setTimeout(() => exit(), 10);
4219
4465
  }
4220
4466
  }
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)(
4467
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ink33.Box, { flexDirection: "column", children: [
4468
+ /* @__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 }),
4469
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ink33.Box, { marginBottom: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4224
4470
  import_ink_select_input3.default,
4225
4471
  {
4226
4472
  indicatorComponent: CustomSelectInputIndicator,
4227
4473
  initialIndex: 1,
4228
- itemComponent: CustomSelectInputItem_default,
4474
+ itemComponent: CustomSelectInputItem,
4229
4475
  items,
4230
4476
  onSelect
4231
4477
  }
4232
4478
  ) }),
4233
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { dimColor: true, children: footer })
4479
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ink33.Text, { dimColor: true, children: footer })
4234
4480
  ] });
4235
4481
  }
4236
4482
 
4237
4483
  // src/commands/release/components/ReleaseConfirmation.tsx
4238
- var import_jsx_runtime37 = require("react/jsx-runtime");
4484
+ var import_jsx_runtime38 = require("react/jsx-runtime");
4239
4485
  function ReleaseConfirmation({
4240
4486
  buildId,
4241
4487
  children,
4242
4488
  configPath,
4243
4489
  disabled = false,
4244
- loadBuild = getBuildAttributes
4490
+ loadData = loadBuildsData
4245
4491
  }) {
4246
- const [state, setState] = (0, import_react20.useState)({
4492
+ var _a;
4493
+ const [state, setState] = (0, import_react21.useState)({
4247
4494
  state: disabled ? "bypass" : "loading"
4248
4495
  });
4249
- (0, import_react20.useEffect)(() => {
4496
+ (0, import_react21.useEffect)(() => {
4250
4497
  confirmationWorkflow({
4251
4498
  buildId,
4252
4499
  configPath,
4253
- loadBuild,
4254
- setState,
4255
- state
4256
- }).catch((error) => {
4257
- setState({ state: "error", error });
4258
- });
4500
+ loadData,
4501
+ state,
4502
+ updateState
4503
+ }).catch(logger_default.error);
4259
4504
  }, []);
4260
4505
  function updateState(changes) {
4261
- setState((previousState) => ({ ...previousState, ...changes }));
4506
+ setState(
4507
+ (previousState) => ({ ...previousState, ...changes })
4508
+ );
4262
4509
  }
4263
4510
  switch (state.state) {
4511
+ case "build-error": {
4512
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4513
+ BuildError,
4514
+ {
4515
+ build: state.build,
4516
+ description: "Can't release build",
4517
+ message: ((_a = state.error) == null ? void 0 : _a.message) || "Internal build error"
4518
+ }
4519
+ );
4520
+ }
4264
4521
  case "bypass":
4265
4522
  return children;
4523
+ case "confirm-downgrade":
4524
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4525
+ DowngradeConfirmation,
4526
+ {
4527
+ build: state.build,
4528
+ latestReleasedBuild: state.latestReleasedBuild,
4529
+ confirmDowngrade: state.onConfirm
4530
+ }
4531
+ );
4266
4532
  case "confirm-release":
4267
4533
  if (!state.build) {
4268
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ErrorDisplay_default, { error: new Error("state.build is empty") });
4534
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ErrorDisplay, { error: new Error("state.build is empty") });
4269
4535
  }
4270
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4536
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4271
4537
  YesNoConfirmation,
4272
4538
  {
4273
- title: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
4274
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ink33.Text, { children: [
4539
+ title: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
4540
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_ink34.Text, { children: [
4275
4541
  "This will release build ",
4276
4542
  state.build.id,
4277
4543
  " as "
4278
4544
  ] }),
4279
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ink33.Text, { bold: true, children: [
4545
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_ink34.Text, { bold: true, children: [
4280
4546
  state.build.appName,
4281
4547
  " v",
4282
4548
  state.build.appVersion
4283
4549
  ] }),
4284
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ink33.Text, { children: ", are you sure?" })
4550
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ink34.Text, { children: ", are you sure?" })
4285
4551
  ] }),
4286
- onYes: () => updateState({ state: "bypass" })
4552
+ onYes: state.onConfirm
4287
4553
  }
4288
4554
  );
4289
4555
  case "confirm-test":
4290
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4556
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4291
4557
  YesNoConfirmation,
4292
4558
  {
4293
- title: state.testConfirmationText,
4294
- onYes: () => updateState({ state: "confirm-release" })
4559
+ title: state.confirmationText,
4560
+ onYes: state.onConfirm
4295
4561
  }
4296
4562
  );
4297
4563
  case "error":
4298
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ErrorDisplay_default, { error: state.error });
4564
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ErrorDisplay, { error: state.error });
4299
4565
  case "loading":
4300
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LoadingText_default, {});
4566
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LoadingText_default, {});
4301
4567
  default:
4302
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ErrorDisplay_default, { error: new Error(`Unknown state ${state.state}`) });
4568
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4569
+ ErrorDisplay,
4570
+ {
4571
+ error: new Error(`Unknown state ${state.state}`)
4572
+ }
4573
+ );
4303
4574
  }
4304
4575
  }
4305
4576
  async function confirmationWorkflow({
4306
4577
  buildId,
4307
4578
  configPath,
4308
- loadBuild,
4309
- setState,
4579
+ loadData,
4580
+ updateState,
4310
4581
  state
4311
4582
  }) {
4312
- if (state.state === "bypass") {
4583
+ try {
4584
+ const { build, latestReleasedBuild } = await loadData({
4585
+ buildId,
4586
+ configPath
4587
+ });
4588
+ updateState({ build, latestReleasedBuild });
4589
+ const report = analyzeRelease({ build, latestReleasedBuild });
4590
+ if (!report.isReleasable) {
4591
+ updateState({ error: new Error(report.error), state: "build-error" });
4592
+ return;
4593
+ }
4594
+ if (state.state !== "bypass") {
4595
+ await confirmDowngrade({ report, updateState });
4596
+ await confirmSmokeTest({ build, updateState });
4597
+ await confirmRelease({ updateState });
4598
+ }
4599
+ updateState({ state: "bypass" });
4600
+ } catch (e) {
4601
+ updateState({ state: "error", error: CliError.from(e) });
4602
+ }
4603
+ }
4604
+ async function confirmDowngrade({
4605
+ report,
4606
+ updateState
4607
+ }) {
4608
+ if (!report.isOlderThanLatest) {
4313
4609
  return;
4314
4610
  }
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
- });
4611
+ let onConfirm;
4612
+ const downgradePromise = new Promise((r) => onConfirm = r);
4613
+ updateState({ onConfirm, state: "confirm-downgrade" });
4614
+ await downgradePromise;
4615
+ }
4616
+ async function confirmSmokeTest({
4617
+ build,
4618
+ updateState
4619
+ }) {
4620
+ const confirmationText = getTestConfirmation(build);
4621
+ if (confirmationText === "") {
4622
+ return;
4623
+ }
4624
+ let onConfirm;
4625
+ const downgradePromise = new Promise((r) => onConfirm = r);
4626
+ updateState({ onConfirm, state: "confirm-test", confirmationText });
4627
+ await downgradePromise;
4628
+ }
4629
+ async function confirmRelease({
4630
+ updateState
4631
+ }) {
4632
+ let onConfirm;
4633
+ const downgradePromise = new Promise((r) => onConfirm = r);
4634
+ updateState({ onConfirm, state: "confirm-release" });
4635
+ await downgradePromise;
4322
4636
  }
4323
4637
  function getTestConfirmation(build) {
4324
4638
  const continueText = "Are you sure you want to release?";
@@ -4340,9 +4654,20 @@ function getTestConfirmation(build) {
4340
4654
  return "";
4341
4655
  }
4342
4656
  }
4657
+ async function loadBuildsData({
4658
+ buildId,
4659
+ configPath
4660
+ }) {
4661
+ const { appId, build, userId } = await getBuildAttributes({
4662
+ buildId,
4663
+ configPath
4664
+ });
4665
+ const latestReleasedBuild = await getLatestReleasedBuild({ appId, userId });
4666
+ return { build, latestReleasedBuild };
4667
+ }
4343
4668
 
4344
4669
  // src/commands/release/ReleaseCommand.tsx
4345
- var import_jsx_runtime38 = require("react/jsx-runtime");
4670
+ var import_jsx_runtime39 = require("react/jsx-runtime");
4346
4671
  function ReleaseCommand({
4347
4672
  buildId,
4348
4673
  configPath,
@@ -4358,26 +4683,27 @@ function ReleaseCommand({
4358
4683
  if (buildId) {
4359
4684
  command += " " + (buildId === "latest" ? "--latest" : "<id>");
4360
4685
  }
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)(
4686
+ 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
4687
  BuildPicker,
4363
4688
  {
4364
4689
  buildFilter: isBuildReleasable,
4365
4690
  buildId,
4366
4691
  commandUsed: command,
4367
4692
  configPath,
4693
+ noBuildsDescription: "(i.e. unreleased and successful)",
4368
4694
  question: "Which build would you like to release?",
4369
- children: (id) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4695
+ children: (id) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4370
4696
  ReleaseConfirmation,
4371
4697
  {
4372
4698
  buildId: id,
4373
4699
  configPath,
4374
4700
  disabled: !shouldConfirm,
4375
- children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4376
- ReleaseBuild,
4701
+ children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4702
+ ReleaseBuildLogic,
4377
4703
  {
4704
+ buildId: id,
4378
4705
  commandUsed: command,
4379
- configPath,
4380
- id
4706
+ configPath
4381
4707
  }
4382
4708
  )
4383
4709
  }
@@ -4385,37 +4711,37 @@ function ReleaseCommand({
4385
4711
  }
4386
4712
  ) }) });
4387
4713
  }
4388
- function isBuildReleasable(build) {
4389
- return !build.releasedAt;
4714
+ function isBuildReleasable(opts) {
4715
+ return analyzeRelease(opts).isReleasable;
4390
4716
  }
4391
4717
 
4392
4718
  // src/commands/smoke-test/SmokeTestCommand.tsx
4393
- var import_react22 = require("react");
4719
+ var import_react23 = require("react");
4394
4720
 
4395
4721
  // 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");
4722
+ var import_ink35 = require("ink");
4723
+ var import_react22 = require("react");
4724
+ var import_jsx_runtime40 = require("react/jsx-runtime");
4399
4725
  function Cancellation({
4400
4726
  children,
4401
4727
  disabled = false,
4402
4728
  exitWhenCanceled = true,
4403
4729
  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: [
4730
+ renderCanceling = () => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LoadingText_default, { text: `Canceling ${subject}` }),
4731
+ renderCanceled = () => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_ink35.Text, { children: [
4406
4732
  upperCaseFirstChar(subject),
4407
4733
  " canceled."
4408
4734
  ] }),
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]:" }),
4735
+ renderCancelText = () => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_ink35.Text, { color: "gray", children: [
4736
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ink35.Text, { bold: true, children: "[esc]:" }),
4411
4737
  " cancel ",
4412
4738
  subject
4413
4739
  ] }),
4414
- renderError = (e) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ErrorDisplay_default, { error: e }),
4740
+ renderError = (e) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ErrorDisplay, { error: e }),
4415
4741
  subject
4416
4742
  }) {
4417
- const [state, setState] = (0, import_react21.useState)("normal");
4418
- const [error, setError] = (0, import_react21.useState)();
4743
+ const [state, setState] = (0, import_react22.useState)("normal");
4744
+ const [error, setError] = (0, import_react22.useState)();
4419
4745
  const onInput = useInput_default();
4420
4746
  const exit = useExit_default();
4421
4747
  onInput(async (input, key) => {
@@ -4447,9 +4773,9 @@ function Cancellation({
4447
4773
  case "error":
4448
4774
  return renderError(error);
4449
4775
  default:
4450
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
4776
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
4451
4777
  children,
4452
- !disabled && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ink34.Box, { children: renderCancelText() })
4778
+ !disabled && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ink35.Box, { children: renderCancelText() })
4453
4779
  ] });
4454
4780
  }
4455
4781
  }
@@ -4458,11 +4784,11 @@ function upperCaseFirstChar(str) {
4458
4784
  }
4459
4785
 
4460
4786
  // src/commands/smoke-test/components/SmokeTestView.tsx
4461
- var import_ink36 = require("ink");
4787
+ var import_ink37 = require("ink");
4462
4788
 
4463
4789
  // src/commands/smoke-test/components/ProgressBar.tsx
4464
- var import_ink35 = require("ink");
4465
- var import_jsx_runtime40 = require("react/jsx-runtime");
4790
+ var import_ink36 = require("ink");
4791
+ var import_jsx_runtime41 = require("react/jsx-runtime");
4466
4792
  function ProgressBar2({
4467
4793
  color = "white",
4468
4794
  label,
@@ -4473,28 +4799,28 @@ function ProgressBar2({
4473
4799
  }) {
4474
4800
  const percentage = progress > 0 ? Math.round(progress).toString().padStart(2, "0") + "%" : "";
4475
4801
  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: [
4802
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_ink36.Box, { marginBottom, children: [
4803
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ink36.Box, { width: labelWidth, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_ink36.Text, { children: [
4478
4804
  label,
4479
4805
  ":"
4480
4806
  ] }) }),
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 }) })
4807
+ /* @__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} ` }) }),
4808
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ink36.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ink36.Text, { children: displayedText }) })
4483
4809
  ] });
4484
4810
  }
4485
4811
 
4486
4812
  // src/commands/smoke-test/components/TestProgress.tsx
4487
- var import_jsx_runtime41 = require("react/jsx-runtime");
4813
+ var import_jsx_runtime42 = require("react/jsx-runtime");
4488
4814
  function TestProgress({
4489
4815
  progress
4490
4816
  }) {
4491
4817
  if (!progress) {
4492
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(LoadingText_default, { text: "Loading test stats" });
4818
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(LoadingText_default, { text: "Loading test stats" });
4493
4819
  }
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 })
4820
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
4821
+ !progress.windows.shouldSkip && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(OsProgress, { os: "Windows", progress: progress.windows }),
4822
+ !progress.mac.shouldSkip && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(OsProgress, { os: "macOS", progress: progress.mac }),
4823
+ !progress.linux.shouldSkip && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(OsProgress, { os: "Linux", progress: progress.linux })
4498
4824
  ] });
4499
4825
  }
4500
4826
  function OsProgress({
@@ -4508,7 +4834,7 @@ function OsProgress({
4508
4834
  skipped: "yellow"
4509
4835
  };
4510
4836
  const text = progress.message + (progress.state === "progress" ? "..." : "");
4511
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4837
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4512
4838
  ProgressBar2,
4513
4839
  {
4514
4840
  color: colors[progress.state],
@@ -4520,7 +4846,7 @@ function OsProgress({
4520
4846
  }
4521
4847
 
4522
4848
  // src/commands/smoke-test/components/SmokeTestView.tsx
4523
- var import_jsx_runtime42 = require("react/jsx-runtime");
4849
+ var import_jsx_runtime43 = require("react/jsx-runtime");
4524
4850
  function SmokeTestView({
4525
4851
  commandUsed = "",
4526
4852
  state
@@ -4528,7 +4854,7 @@ function SmokeTestView({
4528
4854
  var _a, _b, _c, _d;
4529
4855
  switch (state.state) {
4530
4856
  case "build-error": {
4531
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4857
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4532
4858
  BuildError,
4533
4859
  {
4534
4860
  build: state.build,
@@ -4538,25 +4864,25 @@ function SmokeTestView({
4538
4864
  );
4539
4865
  }
4540
4866
  case "canceled":
4541
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ink36.Text, { children: "Smoke test canceled." });
4867
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ink37.Text, { children: "Smoke test canceled." });
4542
4868
  case "complete":
4543
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ink36.Text, { bold: true, color: "greenBright", children: "Smoke test passed." });
4869
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ink37.Text, { bold: true, color: "greenBright", children: "Smoke test passed." });
4544
4870
  case "error":
4545
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4546
- ErrorDisplay_default,
4871
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4872
+ ErrorDisplay,
4547
4873
  {
4548
4874
  commandUsed,
4549
4875
  error: { stack: (_b = state.error) == null ? void 0 : _b.stack }
4550
4876
  }
4551
4877
  );
4552
4878
  case "loading":
4553
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(LoadingText_default, { text: "Preparing smoke test" });
4879
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(LoadingText_default, { text: "Preparing smoke test" });
4554
4880
  case "progress":
4555
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(TestProgress, { progress: state.progress });
4881
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(TestProgress, { progress: state.progress });
4556
4882
  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)(
4883
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
4884
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(TestProgress, { progress: state.progress }),
4885
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4560
4886
  BuildError,
4561
4887
  {
4562
4888
  build: state.build,
@@ -4566,8 +4892,8 @@ function SmokeTestView({
4566
4892
  )
4567
4893
  ] });
4568
4894
  default:
4569
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4570
- ErrorDisplay_default,
4895
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4896
+ ErrorDisplay,
4571
4897
  {
4572
4898
  commandUsed,
4573
4899
  error: new Error(`Unknown state ${state}`)
@@ -4596,6 +4922,7 @@ async function queueSmokeTest({
4596
4922
  contextUserId,
4597
4923
  nodeVersion,
4598
4924
  npmVersion,
4925
+ pnpmVersion,
4599
4926
  userId
4600
4927
  }) {
4601
4928
  try {
@@ -4604,15 +4931,13 @@ async function queueSmokeTest({
4604
4931
  buildId,
4605
4932
  nodeVersion,
4606
4933
  npmVersion,
4934
+ pnpmVersion,
4607
4935
  userId,
4608
4936
  contextUserId
4609
4937
  });
4610
4938
  } 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
- }
4939
+ logger_default.error({ error: e });
4940
+ throw new CliError(`Unexpected internal error while testing build`);
4616
4941
  }
4617
4942
  }
4618
4943
 
@@ -4644,7 +4969,7 @@ async function waitUntilFinished({
4644
4969
  }
4645
4970
 
4646
4971
  // src/commands/smoke-test/SmokeTestCommand.tsx
4647
- var import_jsx_runtime43 = require("react/jsx-runtime");
4972
+ var import_jsx_runtime44 = require("react/jsx-runtime");
4648
4973
  function SmokeTestCommand({
4649
4974
  buildId,
4650
4975
  configPath
@@ -4652,14 +4977,15 @@ function SmokeTestCommand({
4652
4977
  checkIfReactIsUsable_default();
4653
4978
  useAnalyticsCommand("smoke-test", { buildId, configPath });
4654
4979
  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)(
4980
+ 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
4981
  BuildPicker,
4657
4982
  {
4658
- buildFilter: isBuildTestable,
4983
+ buildFilter: ({ build }) => isBuildTestable(build),
4659
4984
  buildId,
4660
4985
  commandUsed: command,
4661
4986
  configPath,
4662
- children: (id) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4987
+ noBuildsDescription: "(candidates for smoke-test must be built with @todesktop/runtime@1.2.1 or later)",
4988
+ children: (id) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
4663
4989
  SmokeTestContainer,
4664
4990
  {
4665
4991
  commandUsed: command,
@@ -4676,18 +5002,20 @@ function SmokeTestContainer({
4676
5002
  configPath
4677
5003
  }) {
4678
5004
  const exit = useExit_default();
4679
- const [state, setState] = (0, import_react22.useState)({
5005
+ const [state, setState] = (0, import_react23.useState)({
4680
5006
  buildId,
4681
5007
  state: "loading"
4682
5008
  });
4683
- const [abortController] = (0, import_react22.useState)(new AbortController());
5009
+ const [abortController] = (0, import_react23.useState)(new AbortController());
4684
5010
  const abortSignal = abortController.signal;
4685
- (0, import_react22.useEffect)(() => {
4686
- smokeTestWorkflow({ abortSignal, buildId, configPath, updateState });
5011
+ (0, import_react23.useEffect)(() => {
5012
+ smokeTestWorkflow({ abortSignal, buildId, configPath, updateState }).catch(
5013
+ logger_default.error
5014
+ );
4687
5015
  }, []);
4688
5016
  function updateState(changes) {
4689
5017
  setState((previousState) => ({ ...previousState, ...changes }));
4690
- if (["build-error", "complete", "progress-error"].includes(changes.state)) {
5018
+ if (changes.state && ["build-error", "complete", "progress-error"].includes(changes.state)) {
4691
5019
  setTimeout(() => exit(), 10);
4692
5020
  }
4693
5021
  }
@@ -4697,13 +5025,13 @@ function SmokeTestContainer({
4697
5025
  await cancelSmokeTest(state);
4698
5026
  }
4699
5027
  }
4700
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5028
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
4701
5029
  Cancellation,
4702
5030
  {
4703
5031
  disabled: !isCancelable(state),
4704
5032
  onCancel,
4705
5033
  subject: "smoke test",
4706
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SmokeTestView, { commandUsed, state })
5034
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SmokeTestView, { commandUsed, state })
4707
5035
  }
4708
5036
  );
4709
5037
  }
@@ -4714,7 +5042,15 @@ async function smokeTestWorkflow({
4714
5042
  updateState
4715
5043
  }) {
4716
5044
  try {
4717
- const { appId, build, contextUserId, nodeVersion, npmVersion, userId } = await getBuildAttributes({
5045
+ const {
5046
+ appId,
5047
+ build,
5048
+ contextUserId,
5049
+ nodeVersion,
5050
+ npmVersion,
5051
+ pnpmVersion,
5052
+ userId
5053
+ } = await getBuildAttributes({
4718
5054
  buildId,
4719
5055
  configPath
4720
5056
  });
@@ -4726,6 +5062,7 @@ async function smokeTestWorkflow({
4726
5062
  buildId,
4727
5063
  nodeVersion,
4728
5064
  npmVersion,
5065
+ pnpmVersion,
4729
5066
  userId,
4730
5067
  contextUserId
4731
5068
  });
@@ -4744,8 +5081,9 @@ async function smokeTestWorkflow({
4744
5081
  canceled: "canceled"
4745
5082
  };
4746
5083
  updateState({ state: stateMap[total.state] });
4747
- } catch (error) {
4748
- updateState({ error, state: error.type || "error" });
5084
+ } catch (e) {
5085
+ const error = CliError.from(e);
5086
+ updateState({ error, state: error.type });
4749
5087
  }
4750
5088
  }
4751
5089
  function buildCanBeCanceled(build) {
@@ -4761,39 +5099,39 @@ function isCancelable(state) {
4761
5099
  }
4762
5100
 
4763
5101
  // 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");
5102
+ var import_react24 = require("react");
5103
+ var import_ink38 = require("ink");
5104
+ var import_jsx_runtime45 = require("react/jsx-runtime");
4767
5105
  var WhoAmI = () => {
4768
5106
  const exit = useExit_default();
4769
5107
  checkIfReactIsUsable_default();
4770
5108
  const auth = getAuthConfig();
4771
5109
  const { hasAttemptedTracking } = useAnalyticsCommand("whoami", {}, {});
4772
- (0, import_react23.useEffect)(() => {
5110
+ (0, import_react24.useEffect)(() => {
4773
5111
  if (hasAttemptedTracking) {
4774
5112
  exit();
4775
5113
  }
4776
5114
  }, [exit, hasAttemptedTracking]);
4777
5115
  if (!auth || !auth.email) {
4778
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ink37.Text, { children: "You're not signed in" });
5116
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ink38.Text, { children: "You're not signed in" });
4779
5117
  }
4780
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ink37.Text, { children: auth.email });
5118
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ink38.Text, { children: auth.email });
4781
5119
  };
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, {}) }) });
5120
+ 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
5121
  var WhoamiCommand_default = WhoAmIWrapper;
4784
5122
 
4785
5123
  // src/utilities/exitIfCLIOutOfDate.ts
4786
5124
  var import_chalk3 = __toESM(require("chalk"));
4787
5125
  var import_is_installed_globally = __toESM(require("is-installed-globally"));
4788
5126
  var import_latest_version = __toESM(require("latest-version"));
4789
- var import_semver2 = __toESM(require("semver"));
5127
+ var import_semver3 = __toESM(require("semver"));
4790
5128
  var exitIfCLIOutOfDate_default = () => {
4791
5129
  if (process.env.AVA_PATH) {
4792
5130
  return;
4793
5131
  }
4794
5132
  const pkg = getToDesktopPackageJson();
4795
5133
  (0, import_latest_version.default)(pkg.name).then((latest) => {
4796
- if (import_semver2.default.gt(latest, pkg.version)) {
5134
+ if (import_semver3.default.gt(latest, pkg.version)) {
4797
5135
  const commandToUpdate = import_chalk3.default.greenBright(
4798
5136
  `npm install ${import_is_installed_globally.default ? "--location=global" : "--save-dev"} @todesktop/cli`
4799
5137
  );
@@ -4801,7 +5139,7 @@ var exitIfCLIOutOfDate_default = () => {
4801
5139
  `Your version of @todesktop/cli is out of date.
4802
5140
  Run ${commandToUpdate} to update to v${latest}.`
4803
5141
  );
4804
- if (!import_semver2.default.satisfies(latest, `^${pkg.version}`)) {
5142
+ if (!import_semver3.default.satisfies(latest, `^${pkg.version}`)) {
4805
5143
  console.log(`CLI is exiting because it is out out of date.`);
4806
5144
  process.exit(1);
4807
5145
  }
@@ -4820,7 +5158,7 @@ var package_default = {
4820
5158
  access: "public"
4821
5159
  },
4822
5160
  name: "@todesktop/cli",
4823
- version: "1.7.6",
5161
+ version: "1.8.0",
4824
5162
  license: "MIT",
4825
5163
  author: "Dave Jeffery <dave@todesktop.com> (http://www.todesktop.com/)",
4826
5164
  homepage: "https://todesktop.com/cli",
@@ -4900,7 +5238,7 @@ var package_default = {
4900
5238
  "xdg-basedir": "^4.0.0"
4901
5239
  },
4902
5240
  devDependencies: {
4903
- "@todesktop/shared": "^7.186.6",
5241
+ "@todesktop/shared": "^7.186.18",
4904
5242
  "@types/bunyan": "^1.8.6",
4905
5243
  "@types/node": "^20.8.4",
4906
5244
  "@types/react": "^18.0.26",
@@ -4925,11 +5263,12 @@ var package_default = {
4925
5263
  husky: "^4.3.0",
4926
5264
  "ink-testing-library": "^2.1.0",
4927
5265
  "lint-staged": "^10.2.11",
5266
+ "package-json-type": "^1.0.3",
4928
5267
  prettier: "^2.8.1",
4929
5268
  proxyquire: "^2.1.3",
4930
5269
  sinon: "^9.0.3",
4931
5270
  typescript: "^4.9.4",
4932
- "typescript-strict-plugin": "^2.1.0"
5271
+ "typescript-strict-plugin": "^2.2.1"
4933
5272
  },
4934
5273
  ava: {
4935
5274
  extensions: [
@@ -4940,6 +5279,7 @@ var package_default = {
4940
5279
  "test/**/*.ts",
4941
5280
  "**/*.test.ts",
4942
5281
  "**/*.test.tsx",
5282
+ "!build/**/*",
4943
5283
  "!test/fixtures/**/*",
4944
5284
  "!test/utilities/**/*"
4945
5285
  ],
@@ -5046,17 +5386,30 @@ function parseCount(value) {
5046
5386
  var configOption = new import_commander.Option(
5047
5387
  "--config [string]",
5048
5388
  "Path to a different configuration file. If not specified, `todesktop.json` in the project root will be used"
5049
- );
5389
+ ).argParser((value) => {
5390
+ if (typeof value === "string" && value === "") {
5391
+ throw new import_commander.InvalidArgumentError("Provided config path is empty");
5392
+ }
5393
+ return value;
5394
+ });
5050
5395
  import_commander.program.name("todekstop").version(getCliVersion_default());
5051
5396
  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."
5397
+ "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
5398
  ).option(
5054
5399
  "--code-sign [bool]",
5055
5400
  "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
5401
+ ).option(
5402
+ "--async",
5403
+ "Upload your app for building on our servers but exit after the upload is complete. Do not wait until the app is actually built"
5404
+ ).option(
5405
+ "--webhook [string]",
5406
+ "Send POST request to the webhook URL after building is finished"
5407
+ ).addOption(configOption).action(({ async, codeSign, config: config2, webhook }) => {
5408
+ runCommand(BuildCommand, {
5409
+ configPath: config2,
5410
+ exitAfterUploading: async === true,
5411
+ onBuildFinishedWebhook: webhook,
5412
+ shouldCodeSign: codeSign !== "false"
5060
5413
  });
5061
5414
  });
5062
5415
  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 +5448,7 @@ import_commander.program.command("whoami").description("Prints the email of the
5095
5448
  });
5096
5449
  var runCommand = (component, props = null, { exitIfOutOfDate = true } = {}) => {
5097
5450
  onCommand_default({ exitIfOutOfDate });
5098
- const { waitUntilExit } = (0, import_ink38.render)((0, import_react24.createElement)(component, props));
5451
+ const { waitUntilExit } = (0, import_ink39.render)((0, import_react25.createElement)(component, props));
5099
5452
  waitUntilExit().catch((error) => {
5100
5453
  console.error(error.stack);
5101
5454
  });