@todesktop/cli 1.7.6 → 1.8.0

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