@todesktop/cli 1.5.1 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,4430 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
18
+ mod
19
+ ));
20
+
21
+ // src/index.ts
22
+ var import_commander = require("commander");
23
+ var import_ink33 = require("ink");
24
+ var import_react21 = require("react");
25
+ var import_register = require("source-map-support/register");
26
+
27
+ // src/components/Build.tsx
28
+ var import_react6 = require("react");
29
+ var import_prop_types9 = __toESM(require("prop-types"));
30
+
31
+ // src/components/BuildProgress.tsx
32
+ var import_ink4 = require("ink");
33
+ var import_prop_types3 = __toESM(require("prop-types"));
34
+ var import_react2 = require("react");
35
+ var import_lodash = __toESM(require("lodash.throttle"));
36
+
37
+ // src/components/BuildCompleteMessage.tsx
38
+ var import_ink2 = require("ink");
39
+ var import_ink_link = __toESM(require("ink-link"));
40
+ var import_prop_types = __toESM(require("prop-types"));
41
+ var import_react = require("react");
42
+
43
+ // src/utilities/logForCI.ts
44
+ var import_is_ci = __toESM(require("is-ci"));
45
+
46
+ // src/utilities/logger.ts
47
+ var import_bunyan = __toESM(require("bunyan"));
48
+ var fs = __toESM(require("fs"));
49
+ var os = __toESM(require("os"));
50
+ var path = __toESM(require("path"));
51
+ var Sentry = __toESM(require("@sentry/node"));
52
+ var logger;
53
+ try {
54
+ const name = "todesktop-cli";
55
+ let parentDirectory;
56
+ const homePath = os.homedir();
57
+ if (process.platform === "darwin") {
58
+ parentDirectory = path.join(homePath, "Library", "Logs", name);
59
+ } else if (process.platform === "win32") {
60
+ parentDirectory = path.join(
61
+ process.env.APPDATA || path.join(homePath, "AppData", "Roaming"),
62
+ name,
63
+ "logs"
64
+ );
65
+ } else {
66
+ parentDirectory = path.join(
67
+ process.env.XDG_CONFIG_HOME || path.join(homePath, ".config"),
68
+ name,
69
+ "logs"
70
+ );
71
+ }
72
+ fs.mkdirSync(parentDirectory, { recursive: true });
73
+ logger = import_bunyan.default.createLogger({
74
+ name,
75
+ src: true,
76
+ streams: [
77
+ {
78
+ level: "debug",
79
+ path: path.join(parentDirectory, "main.log")
80
+ }
81
+ ]
82
+ });
83
+ } catch (e) {
84
+ const noOp = () => {
85
+ };
86
+ logger = {
87
+ debug: noOp,
88
+ error: noOp,
89
+ fatal: noOp,
90
+ info: noOp,
91
+ trace: noOp,
92
+ warn: noOp
93
+ };
94
+ try {
95
+ Sentry.captureException(e);
96
+ } catch (err) {
97
+ }
98
+ }
99
+ var logger_default = logger;
100
+
101
+ // src/utilities/logForCI.ts
102
+ var logForCI_default = (...args2) => {
103
+ logger_default.info({ args: args2 }, "logForCI");
104
+ if (import_is_ci.default || !process.stdin.isTTY) {
105
+ console.log(...args2);
106
+ }
107
+ };
108
+
109
+ // src/utilities/useExit.ts
110
+ var import_ink = require("ink");
111
+
112
+ // src/utilities/analytics.ts
113
+ var import_os = __toESM(require("os"));
114
+ var import_uuid = require("uuid");
115
+ var import_analytics_node = __toESM(require("analytics-node"));
116
+
117
+ // src/utilities/getEnvironmentVariables.ts
118
+ var import_dotenv = __toESM(require("dotenv"));
119
+ var import_path = __toESM(require("path"));
120
+ var hasInitialized = false;
121
+ var init = () => {
122
+ if (hasInitialized) {
123
+ return;
124
+ }
125
+ import_dotenv.default.config({
126
+ path: import_path.default.resolve(__dirname, "../.env")
127
+ });
128
+ hasInitialized = true;
129
+ };
130
+ var getEnvironmentVariables_default = () => {
131
+ init();
132
+ return process.env;
133
+ };
134
+
135
+ // src/utilities/firestore.ts
136
+ var import_app = __toESM(require("firebase/app"));
137
+ var import_firestore = require("firebase/firestore");
138
+ var import_auth = require("firebase/auth");
139
+ var environmentVariables = getEnvironmentVariables_default();
140
+ var firebaseDB = import_app.default.initializeApp({
141
+ apiKey: environmentVariables.TODESKTOP_CLI_FIREBASE_API_KEY,
142
+ authDomain: environmentVariables.TODESKTOP_CLI_FIREBASE_AUTH_DOMAIN,
143
+ databaseURL: environmentVariables.TODESKTOP_CLI_FIREBASE_DATABASE_URL,
144
+ projectId: environmentVariables.TODESKTOP_CLI_FIREBASE_PROJECT_ID,
145
+ storageBucket: environmentVariables.TODESKTOP_CLI_FIREBASE_STORAGE_BUCKET,
146
+ messagingSenderId: environmentVariables.TODESKTOP_CLI_FIREBASE_MESSAGING_SENDER_ID,
147
+ appId: environmentVariables.TODESKTOP_CLI_FIREBASE_ID
148
+ });
149
+ var currentUser = () => import_app.default.auth().currentUser;
150
+ var signInWithCustomToken = async (token) => {
151
+ return import_app.default.auth().signInWithCustomToken(token);
152
+ };
153
+ var onUserAuth = (handler) => import_app.default.auth().onAuthStateChanged((user) => {
154
+ handler(user || {});
155
+ });
156
+ var firestore_default = firebaseDB.firestore();
157
+
158
+ // src/utilities/configStore.ts
159
+ var fs2 = __toESM(require("fs"));
160
+ var import_del = __toESM(require("del"));
161
+ var import_conf = __toESM(require("conf"));
162
+ var import_xdg_basedir = __toESM(require("xdg-basedir"));
163
+ var config = new import_conf.default({ configName: "todesktop-cli" });
164
+ var accessTokenConfigKey = "accessToken";
165
+ var emailConfigKey = "email";
166
+ var jwtTokenConfigKey = "jwtToken";
167
+ var setConfig = (key, value) => config.set(key, value);
168
+ var getConfig = (key) => config.get(key);
169
+ var setAuthConfig = (email, accessToken, jwtToken) => {
170
+ setConfig(emailConfigKey, email);
171
+ setConfig(accessTokenConfigKey, accessToken);
172
+ setConfig(jwtTokenConfigKey, jwtToken);
173
+ };
174
+ var getAuthConfig = () => {
175
+ const accessToken = getConfig(accessTokenConfigKey);
176
+ const jwtToken = getConfig(jwtTokenConfigKey);
177
+ const email = getConfig(emailConfigKey);
178
+ return { accessToken, jwtToken, email };
179
+ };
180
+ var deleteAuthConfig = () => {
181
+ config.delete(emailConfigKey);
182
+ config.delete(accessTokenConfigKey);
183
+ config.delete(jwtTokenConfigKey);
184
+ };
185
+ var oldConfigPath = `${import_xdg_basedir.default.config}/configstore/config.json.json`;
186
+ if (fs2.existsSync(oldConfigPath)) {
187
+ try {
188
+ const { email, accessToken, jwtToken } = require(oldConfigPath);
189
+ setAuthConfig(email, accessToken, jwtToken);
190
+ (0, import_del.default)(oldConfigPath, { force: true });
191
+ } catch (err) {
192
+ (0, import_del.default)(oldConfigPath, { force: true });
193
+ }
194
+ }
195
+
196
+ // src/utilities/getToDesktopPackageJson.ts
197
+ var import_pkg_up = __toESM(require("pkg-up"));
198
+
199
+ // src/utilities/readJson.ts
200
+ function readJson(filePath) {
201
+ return require(filePath);
202
+ }
203
+
204
+ // src/utilities/getToDesktopPackageJson.ts
205
+ var packageJson = readJson(
206
+ import_pkg_up.default.sync({ cwd: __dirname })
207
+ );
208
+ function getToDesktopPackageJson() {
209
+ return packageJson;
210
+ }
211
+
212
+ // src/utilities/getCliVersion.ts
213
+ var cliVersion = getToDesktopPackageJson().version;
214
+ var getCliVersion_default = () => cliVersion;
215
+
216
+ // src/utilities/analytics.ts
217
+ var environmentVariables2 = getEnvironmentVariables_default();
218
+ var analytics = new import_analytics_node.default(environmentVariables2.SEGMENT_WRITE_KEY, {
219
+ flushAt: 1,
220
+ flushInterval: 1
221
+ });
222
+ var ANALYTICS_EVENT = {
223
+ CLI_COMMAND: "CLI Command"
224
+ };
225
+ function getAnalyticsBaseProperties() {
226
+ const baseProps = {
227
+ app: {
228
+ cliVersion: ""
229
+ },
230
+ client: "cli",
231
+ user: {
232
+ email: ""
233
+ },
234
+ os: {
235
+ name: "",
236
+ version: ""
237
+ },
238
+ runtime: {
239
+ name: "Node.js",
240
+ version: ""
241
+ }
242
+ };
243
+ try {
244
+ baseProps.os = {
245
+ name: import_os.default.platform(),
246
+ version: import_os.default.release()
247
+ };
248
+ } catch (err) {
249
+ }
250
+ try {
251
+ baseProps.user = {
252
+ email: getAuthConfig().email
253
+ };
254
+ } catch (err) {
255
+ }
256
+ try {
257
+ baseProps.app = {
258
+ cliVersion: getCliVersion_default()
259
+ };
260
+ } catch (err) {
261
+ }
262
+ try {
263
+ baseProps.runtime = {
264
+ name: "Node.js",
265
+ version: process.version
266
+ };
267
+ } catch (err) {
268
+ }
269
+ return baseProps;
270
+ }
271
+ var anonymousId = (0, import_uuid.v4)();
272
+ var track = (event, properties = {}, callback = () => {
273
+ }) => {
274
+ try {
275
+ const user = currentUser();
276
+ analytics.track(
277
+ {
278
+ event,
279
+ userId: user ? user.uid : void 0,
280
+ anonymousId,
281
+ properties: {
282
+ ...properties,
283
+ ...getAnalyticsBaseProperties()
284
+ }
285
+ },
286
+ callback
287
+ );
288
+ } catch (err) {
289
+ }
290
+ };
291
+ var identify = (id, traits = {}, callback = () => {
292
+ }) => {
293
+ try {
294
+ analytics.identify({ userId: id, anonymousId, traits }, callback);
295
+ } catch (err) {
296
+ }
297
+ };
298
+ var flush = (callback = () => {
299
+ }) => analytics.flush(callback);
300
+
301
+ // src/utilities/useExit.ts
302
+ var useExit_default = () => {
303
+ const { exit } = (0, import_ink.useApp)();
304
+ return (error = void 0) => {
305
+ logger_default.debug({ error }, "Exit called");
306
+ firestore_default.terminate().catch(
307
+ (e) => logger_default.error(e)
308
+ );
309
+ let timeoutId;
310
+ Promise.race([
311
+ new Promise((resolve4) => flush(() => resolve4())),
312
+ new Promise(
313
+ (resolve4) => timeoutId = setTimeout(() => resolve4(), 1e3)
314
+ )
315
+ ]).catch(() => {
316
+ }).finally(() => {
317
+ clearTimeout(timeoutId);
318
+ try {
319
+ exit();
320
+ } catch (e) {
321
+ logger_default.error(e);
322
+ }
323
+ setTimeout(() => process.exit(error ? 1 : 0), 10);
324
+ });
325
+ };
326
+ };
327
+
328
+ // src/components/BuildCompleteMessage.tsx
329
+ var import_path2 = __toESM(require("path"));
330
+ var import_jsx_runtime = require("react/jsx-runtime");
331
+ var addTrailingSlash = (url) => {
332
+ return url.endsWith("/") ? url : `${url}/`;
333
+ };
334
+ var BuildCompleteMessage = ({ build }) => {
335
+ const exit = useExit_default();
336
+ let url = build.standardUniversalDownloadUrl;
337
+ if (process.platform === "darwin") {
338
+ url = new URL(
339
+ import_path2.default.posix.join("mac", "zip", process.arch),
340
+ addTrailingSlash(url)
341
+ ).href;
342
+ }
343
+ logForCI_default(`Build complete! ${url}`);
344
+ (0, import_react.useEffect)(() => {
345
+ if (url) {
346
+ setTimeout(exit, 10);
347
+ }
348
+ }, [exit, url]);
349
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink2.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Text, { children: [
350
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink2.Text, { bold: true, children: "Build complete!" }),
351
+ " ",
352
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink_link.default, { fallback: false, url, children: url || "" })
353
+ ] }) });
354
+ };
355
+ BuildCompleteMessage.propTypes = {
356
+ build: import_prop_types.default.object.isRequired
357
+ };
358
+ var BuildCompleteMessage_default = BuildCompleteMessage;
359
+
360
+ // src/components/PlatformProgress.tsx
361
+ var import_prop_types2 = __toESM(require("prop-types"));
362
+ var import_ink3 = require("ink");
363
+ var import_ink_link2 = __toESM(require("ink-link"));
364
+ var import_path3 = __toESM(require("path"));
365
+ var import_jsx_runtime2 = require("react/jsx-runtime");
366
+ var preLabelMaxWidth = "More info: ".length;
367
+ var getProgressPercentageLabel = (percentage, activityType) => {
368
+ let backgroundColor = "white";
369
+ let color = "black";
370
+ if (activityType === "error") {
371
+ backgroundColor = "red";
372
+ } else if (percentage === 100) {
373
+ backgroundColor = "green";
374
+ color = "black";
375
+ }
376
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
377
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { backgroundColor, color, children: [
378
+ " ",
379
+ Math.round(percentage).toString().padStart(2, "0"),
380
+ "%",
381
+ " "
382
+ ] }),
383
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { children: " " })
384
+ ] });
385
+ };
386
+ var PlatformProgress = ({
387
+ activityName,
388
+ activityType,
389
+ downloadUrl,
390
+ percent,
391
+ platform,
392
+ shouldShowReadyPrefix
393
+ }) => {
394
+ let progressIndicator;
395
+ if (activityType === "done") {
396
+ if (process.platform === "darwin") {
397
+ downloadUrl = import_path3.default.posix.join(downloadUrl, "mac", "zip", process.arch);
398
+ }
399
+ progressIndicator = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: [
400
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { children: [
401
+ getProgressPercentageLabel(100),
402
+ shouldShowReadyPrefix ? "Ready!" : ""
403
+ ] }),
404
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink_link2.default, { fallback: false, url: downloadUrl, children: downloadUrl })
405
+ ] });
406
+ } else {
407
+ let activityNameToDisplay = activityName.replace(
408
+ /(?:\r\n|\r|\n)\s*/g,
409
+ "\u21B5 "
410
+ );
411
+ if (activityNameToDisplay.length > 64) {
412
+ activityNameToDisplay = `${activityNameToDisplay.substr(0, 61)}...`;
413
+ }
414
+ progressIndicator = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { children: [
415
+ ["preparation", "queue"].includes(activityType) ? void 0 : getProgressPercentageLabel(percent, activityType),
416
+ activityNameToDisplay,
417
+ activityType === "error" ? "" : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { children: "..." })
418
+ ] }) }) });
419
+ }
420
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { children: [
421
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { width: preLabelMaxWidth, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { children: [
422
+ platform[0].toUpperCase() + platform.substr(1),
423
+ ": "
424
+ ] }) }),
425
+ progressIndicator
426
+ ] });
427
+ };
428
+ PlatformProgress.propTypes = {
429
+ activityName: import_prop_types2.default.string.isRequired,
430
+ activityType: import_prop_types2.default.string.isRequired,
431
+ downloadUrl: import_prop_types2.default.string,
432
+ percent: import_prop_types2.default.number.isRequired,
433
+ platform: import_prop_types2.default.string.isRequired,
434
+ shouldShowReadyPrefix: import_prop_types2.default.bool
435
+ };
436
+ var PlatformProgress_default = PlatformProgress;
437
+
438
+ // src/utilities/buildStatus.ts
439
+ var hasBuildKickedOff = (build) => {
440
+ if (!build) {
441
+ return false;
442
+ }
443
+ return build.status && build.status !== "preparation";
444
+ };
445
+ var isBuildCancellable = (build) => hasBuildKickedOff(build) && isBuildRunning(build);
446
+ var isBuildRunning = (build) => {
447
+ if (!build) {
448
+ return false;
449
+ }
450
+ return !["cancelled", "succeeded"].includes(build.status) && ["linux", "mac", "windows"].some(
451
+ (platform) => isPlatformBuildRunning(build[platform])
452
+ );
453
+ };
454
+ var isPlatformBuildRunning = (platformBuild) => {
455
+ if (!platformBuild) {
456
+ return false;
457
+ }
458
+ return !platformBuild.shouldSkip && !["cancelled", "succeeded"].includes(platformBuild.status) && ("failed" !== platformBuild.status || platformBuild.numberOfAttemptedBuilds < 2);
459
+ };
460
+
461
+ // src/components/BuildProgress.tsx
462
+ var import_jsx_runtime3 = require("react/jsx-runtime");
463
+ var logForCIThrottled = (0, import_lodash.default)(logForCI_default, 60 * 1e3, { trailing: true });
464
+ var BuildProgress = ({ build, onBuildFailure }) => {
465
+ logger_default.debug("BuildProgress component: render");
466
+ const exit = useExit_default();
467
+ const [{ hasKickedOff }, setState] = (0, import_react2.useState)({
468
+ hasKickedOff: false
469
+ });
470
+ const platformsNotSkipped = ["windows", "mac", "linux"].filter(
471
+ (platform) => !build[platform].shouldSkip
472
+ );
473
+ (0, import_react2.useEffect)(() => {
474
+ setState((previousState) => ({
475
+ ...previousState,
476
+ hasKickedOff: hasBuildKickedOff(build)
477
+ }));
478
+ }, [build]);
479
+ (0, import_react2.useEffect)(() => {
480
+ if (build.status === "failed") {
481
+ onBuildFailure(build.errorMessage);
482
+ } else if (build.status === "cancelled") {
483
+ setTimeout(exit, 10);
484
+ } else {
485
+ const ciPlatformProgress = platformsNotSkipped.map(
486
+ (platform) => `${platform.charAt(0).toUpperCase() + platform.slice(1)}: ${build[platform].progressActivityName} (${build[platform].progressPercentage}%)`
487
+ );
488
+ logForCIThrottled(ciPlatformProgress.join(", "));
489
+ }
490
+ }, [build, exit, onBuildFailure, platformsNotSkipped]);
491
+ if (build.status === "succeeded") {
492
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BuildCompleteMessage_default, { build });
493
+ }
494
+ if (build.status === "cancelled") {
495
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { children: "Build cancelled" }) });
496
+ }
497
+ if (!hasKickedOff) {
498
+ return null;
499
+ }
500
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Box, { flexDirection: "column", children: platformsNotSkipped.map((platform) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Box, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
501
+ PlatformProgress_default,
502
+ {
503
+ activityName: build[platform].status === "failed" ? build[platform].errorMessage : build[platform].progressActivityName,
504
+ activityType: build[platform].status === "failed" ? "error" : build[platform].progressActivityType,
505
+ downloadUrl: build[platform].standardDownloadUrl,
506
+ percent: build[platform].progressPercentage,
507
+ platform,
508
+ shouldShowReadyPrefix: !(build && ["failed", "succeeded"].includes(build.status))
509
+ }
510
+ ) }, platform)) }) });
511
+ };
512
+ BuildProgress.propTypes = {
513
+ build: import_prop_types3.default.object.isRequired,
514
+ onBuildFailure: import_prop_types3.default.func.isRequired
515
+ };
516
+ var BuildProgress_default = BuildProgress;
517
+
518
+ // src/components/ErrorDisplay.tsx
519
+ var import_ink5 = require("ink");
520
+ var import_prop_types4 = __toESM(require("prop-types"));
521
+ var import_react3 = require("react");
522
+ var import_jsx_runtime4 = require("react/jsx-runtime");
523
+ var ErrorDisplay = ({ commandUsed, error }) => {
524
+ const exit = useExit_default();
525
+ logger_default.error({ error });
526
+ (0, import_react3.useEffect)(() => {
527
+ if (!error) {
528
+ return;
529
+ }
530
+ setTimeout(() => exit(error), 10);
531
+ }, [exit, error]);
532
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
533
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { backgroundColor: "red", color: "white", children: [
534
+ " ",
535
+ "ERROR",
536
+ " "
537
+ ] }),
538
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: error.message || error.stack || error.toString() }),
539
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginTop: 1, children: [
540
+ commandUsed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { dimColor: true, color: "gray", children: [
541
+ "Command: ",
542
+ commandUsed
543
+ ] }) : null,
544
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { dimColor: true, color: "gray", children: [
545
+ "@todesktop/cli: ",
546
+ getCliVersion_default()
547
+ ] }),
548
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { dimColor: true, color: "gray", children: [
549
+ "Node: ",
550
+ process.version
551
+ ] }),
552
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { dimColor: true, color: "gray", children: [
553
+ "cwd: ",
554
+ process.cwd()
555
+ ] })
556
+ ] })
557
+ ] });
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;
637
+
638
+ // src/components/MainLayout.tsx
639
+ var import_ink13 = require("ink");
640
+ var import_is_ci3 = __toESM(require("is-ci"));
641
+ var import_react5 = require("react");
642
+
643
+ // src/components/CancelBuild.tsx
644
+ var import_ink9 = require("ink");
645
+ var import_react4 = require("react");
646
+
647
+ // src/utilities/getCallableFirebaseFunction.ts
648
+ var import_firebase = __toESM(require("firebase"));
649
+ var import_functions = require("firebase/functions");
650
+ if (getEnvironmentVariables_default().TODESKTOP_CLI_ENV === "development") {
651
+ import_firebase.default.functions().useFunctionsEmulator("http://localhost:5000");
652
+ }
653
+ var getCallableFirebaseFunction_default = (functionName) => import_firebase.default.functions().httpsCallable(functionName);
654
+
655
+ // src/components/CancelBuild.tsx
656
+ var import_jsx_runtime9 = require("react/jsx-runtime");
657
+ var cancelBuild = getCallableFirebaseFunction_default("cancelBuild");
658
+ var CancelBuild = ({
659
+ appId,
660
+ children: postCancelContents,
661
+ commandUsed,
662
+ id
663
+ }) => {
664
+ logger_default.debug({ appId, buildId: id }, "CancelBuild component: render");
665
+ const exit = useExit_default();
666
+ const [
667
+ { arbitraryMessageComponent, error, hasCompleted, hasStarted },
668
+ setState
669
+ ] = (0, import_react4.useState)({
670
+ arbitraryMessageComponent: null,
671
+ error: null,
672
+ hasCompleted: false,
673
+ hasStarted: false
674
+ });
675
+ (0, import_react4.useEffect)(() => {
676
+ if (hasCompleted || hasStarted) {
677
+ return;
678
+ }
679
+ setState((previousState) => ({ ...previousState, hasStarted: true }));
680
+ logForCI_default("Cancelling build...");
681
+ cancelBuild({ appId, buildId: id }).then((result) => {
682
+ logger_default.debug(
683
+ { appId, buildId: id, result },
684
+ "CancelBuild component: cancellation request complete"
685
+ );
686
+ setState((previousState) => ({ ...previousState, hasCompleted: true }));
687
+ }).catch((e) => {
688
+ logger_default.debug(
689
+ { appId, buildId: id, code: e.code },
690
+ "CancelBuild component: cancellation request failed"
691
+ );
692
+ logger_default.error(e);
693
+ const stateUpdates = {
694
+ arbitraryMessageComponent: void 0,
695
+ error: void 0,
696
+ hasCompleted: true
697
+ };
698
+ if (e.code === "internal") {
699
+ stateUpdates.error = e;
700
+ } 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: [
703
+ "Cannot cancel build (",
704
+ id,
705
+ ")."
706
+ ] }),
707
+ " ",
708
+ e.message
709
+ ] });
710
+ }
711
+ setState((prevState) => ({
712
+ ...prevState,
713
+ ...stateUpdates
714
+ }));
715
+ });
716
+ }, [appId, id, hasStarted, hasCompleted]);
717
+ (0, import_react4.useEffect)(() => {
718
+ if (hasCompleted && !postCancelContents) {
719
+ logger_default.debug(
720
+ "CancelBuild component: exiting now that build is cancelled"
721
+ );
722
+ exit();
723
+ }
724
+ }, [postCancelContents, exit, hasCompleted]);
725
+ if (error) {
726
+ error.message = `Failed to cancel build (${id}). ${error.message}`;
727
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ErrorDisplay_default, { commandUsed, error });
728
+ }
729
+ if (arbitraryMessageComponent) {
730
+ return arbitraryMessageComponent;
731
+ }
732
+ 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" });
734
+ }
735
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { children: [
736
+ "Cancelling build (",
737
+ id,
738
+ ")..."
739
+ ] });
740
+ };
741
+ var CancelBuild_default = CancelBuild;
742
+
743
+ // src/components/Footer.tsx
744
+ var import_ink10 = require("ink");
745
+ 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");
748
+ var Footer = ({
749
+ hasBuildEverFailed,
750
+ shouldShowCancelBuildInstructions,
751
+ uiUrl
752
+ }) => {
753
+ if (!uiUrl) {
754
+ return null;
755
+ }
756
+ let buildFailedMessage;
757
+ if (hasBuildEverFailed) {
758
+ buildFailedMessage = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "red", children: "An error has occurred. " });
759
+ }
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: [
763
+ buildFailedMessage,
764
+ "See web UI for more information: "
765
+ ] }),
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 }) })
767
+ ] }),
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]:" }),
770
+ " cancel build"
771
+ ] }) : null
772
+ ] });
773
+ };
774
+ Footer.propTypes = {
775
+ hasBuildEverFailed: import_prop_types7.default.bool,
776
+ shouldShowCancelBuildInstructions: import_prop_types7.default.bool,
777
+ uiUrl: import_prop_types7.default.string
778
+ };
779
+ var Footer_default = Footer;
780
+
781
+ // 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");
785
+ var getText = (build, name, version) => {
786
+ const suffix = name + (version ? ` v${version}` : "");
787
+ if (build && ["cancelled", "succeeded", "failed"].includes(build.status)) {
788
+ if (build.status === "succeeded") {
789
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { children: [
790
+ "\u2705 ",
791
+ suffix
792
+ ] });
793
+ } else if (build.status === "failed") {
794
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { children: [
795
+ "\u274C ",
796
+ suffix
797
+ ] });
798
+ } 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" }),
801
+ " ",
802
+ suffix
803
+ ] });
804
+ }
805
+ } else {
806
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { children: suffix });
807
+ }
808
+ };
809
+ var Header = ({ build, name, version }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginBottom: 1, children: getText(build, name, version) });
810
+ Header.propTypes = {
811
+ build: import_prop_types8.default.object,
812
+ name: import_prop_types8.default.string.isRequired,
813
+ version: import_prop_types8.default.string
814
+ };
815
+ var Header_default = Header;
816
+
817
+ // src/utilities/useInput.ts
818
+ var import_is_ci2 = __toESM(require("is-ci"));
819
+ var import_ink12 = require("ink");
820
+ var hasSubscribed = false;
821
+ var ctrlCSubscriptions = [];
822
+ var onStdin = (data, exit) => {
823
+ let input = String(data);
824
+ let wasCtrlPressed = false;
825
+ if (input <= "" && input !== "\r") {
826
+ input = String.fromCharCode(input.charCodeAt(0) + "a".charCodeAt(0) - 1);
827
+ wasCtrlPressed = true;
828
+ }
829
+ if (wasCtrlPressed && input === "c") {
830
+ ctrlCSubscriptions.forEach((subscription) => subscription());
831
+ setTimeout(exit, 10);
832
+ }
833
+ };
834
+ var useInput_default = () => {
835
+ const exit = useExit_default();
836
+ const { stdin, isRawModeSupported } = (0, import_ink12.useStdin)();
837
+ if (import_is_ci2.default || !isRawModeSupported) {
838
+ return () => () => {
839
+ };
840
+ }
841
+ return (callback, useInputOptions = {}, { onCtrlCPressed = null } = {}) => {
842
+ const onStdinData = (data) => onStdin(data, exit);
843
+ if (onCtrlCPressed) {
844
+ ctrlCSubscriptions.push(onCtrlCPressed);
845
+ }
846
+ if (!hasSubscribed) {
847
+ hasSubscribed = true;
848
+ stdin.on("data", onStdinData);
849
+ }
850
+ const cleanUpInputHook = (0, import_ink12.useInput)(callback, useInputOptions);
851
+ return () => {
852
+ if (hasSubscribed) {
853
+ stdin.off("data", onStdinData);
854
+ }
855
+ cleanUpInputHook == null ? void 0 : cleanUpInputHook();
856
+ };
857
+ };
858
+ };
859
+
860
+ // src/components/MainLayout.tsx
861
+ var import_jsx_runtime12 = require("react/jsx-runtime");
862
+ var MainLayout = ({
863
+ appId,
864
+ appName,
865
+ appVersion,
866
+ build,
867
+ children,
868
+ commandUsed,
869
+ hasBuildEverFailed
870
+ }) => {
871
+ logger_default.debug("MainLayout component: render");
872
+ const onInput = useInput_default();
873
+ const { isRawModeSupported } = (0, import_ink13.useStdin)();
874
+ const [
875
+ { canCancelBuild, hasKickedOff, isCancellingBuild, wasCtrlCPressed },
876
+ setState
877
+ ] = (0, import_react5.useState)({
878
+ canCancelBuild: null,
879
+ hasKickedOff: false,
880
+ isCancellingBuild: false,
881
+ wasCtrlCPressed: false
882
+ });
883
+ (0, import_react5.useEffect)(() => {
884
+ setState((previousState) => ({
885
+ ...previousState,
886
+ hasKickedOff: hasBuildKickedOff(build)
887
+ }));
888
+ }, [build]);
889
+ (0, import_react5.useEffect)(() => {
890
+ setState((previousState) => ({
891
+ ...previousState,
892
+ canCancelBuild: !import_is_ci3.default && isRawModeSupported && !isCancellingBuild && isBuildCancellable(build)
893
+ }));
894
+ }, [build, isCancellingBuild]);
895
+ onInput(
896
+ async (input, key) => {
897
+ if (key.escape && canCancelBuild) {
898
+ logger_default.debug("MainLayout component: esc pressed, cancelling build");
899
+ setState((previousState) => ({
900
+ ...previousState,
901
+ isCancellingBuild: true
902
+ }));
903
+ }
904
+ },
905
+ {},
906
+ {
907
+ onCtrlCPressed: () => {
908
+ setState((previousState) => ({
909
+ ...previousState,
910
+ wasCtrlCPressed: true
911
+ }));
912
+ }
913
+ }
914
+ );
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)(
919
+ Footer_default,
920
+ {
921
+ hasBuildEverFailed,
922
+ shouldShowCancelBuildInstructions: canCancelBuild && !wasCtrlCPressed,
923
+ uiUrl: build ? build.url : null
924
+ }
925
+ ),
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: [
929
+ "todesktop builds ",
930
+ build.id
931
+ ] }) })
932
+ ] }) : null
933
+ ] });
934
+ };
935
+ var MainLayout_default = MainLayout;
936
+
937
+ // src/utilities/runBuild.ts
938
+ var import_pretty_bytes = __toESM(require("pretty-bytes"));
939
+
940
+ // src/utilities/getVersionControlInfo.ts
941
+ var gitRevSync = __toESM(require("git-rev-sync"));
942
+ var getVersionControlInfo_default = async (directory) => {
943
+ let result = {};
944
+ 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
+ } catch (e) {
958
+ }
959
+ return result;
960
+ };
961
+
962
+ // src/utilities/projectConfig/getProjectConfig.ts
963
+ var import_path5 = require("path");
964
+ var import_fs = require("fs");
965
+ var import_find_up = __toESM(require("find-up"));
966
+
967
+ // src/utilities/projectConfig/loadConfig.ts
968
+ function loadConfig(configPath) {
969
+ return require(configPath);
970
+ }
971
+
972
+ // src/utilities/projectConfig/resolveConfigPaths.ts
973
+ var path5 = __toESM(require("path"));
974
+ function resolveConfigPaths({
975
+ config: config2,
976
+ projectRoot
977
+ }) {
978
+ const appRoot = config2.appPath ? path5.isAbsolute(config2.appPath) ? config2.appPath : path5.join(projectRoot, config2.appPath) : projectRoot;
979
+ const transformIfExists = (value, transformer) => value ? transformer(value) : void 0;
980
+ const resolvePath = (filePath) => path5.isAbsolute(filePath) ? filePath : path5.join(projectRoot, filePath);
981
+ const result = {
982
+ ...config2,
983
+ appPath: appRoot,
984
+ icon: resolvePath(config2.icon)
985
+ };
986
+ if (config2.extraContentFiles) {
987
+ result.extraContentFiles = transformIfExists(
988
+ config2.extraContentFiles,
989
+ (extraContentFiles) => extraContentFiles.map((extraContentFile) => ({
990
+ ...extraContentFile,
991
+ from: resolvePath(extraContentFile.from)
992
+ }))
993
+ );
994
+ }
995
+ if (config2.extraResources) {
996
+ result.extraResources = transformIfExists(
997
+ config2.extraResources,
998
+ (extraResources) => extraResources.map((extraResource) => ({
999
+ ...extraResource,
1000
+ from: resolvePath(extraResource.from)
1001
+ }))
1002
+ );
1003
+ }
1004
+ if (config2.linux) {
1005
+ result.linux = { ...config2.linux };
1006
+ if (config2.linux.icon) {
1007
+ result.linux.icon = resolvePath(config2.linux.icon);
1008
+ }
1009
+ }
1010
+ if (config2.mac) {
1011
+ result.mac = { ...config2.mac };
1012
+ if (config2.mac.entitlements) {
1013
+ result.mac.entitlements = resolvePath(config2.mac.entitlements);
1014
+ }
1015
+ if (config2.mac.icon) {
1016
+ result.mac.icon = resolvePath(config2.mac.icon);
1017
+ }
1018
+ }
1019
+ if (config2.dmg) {
1020
+ if (config2.dmg.background) {
1021
+ result.dmg.background = resolvePath(config2.dmg.background);
1022
+ }
1023
+ }
1024
+ if (config2.windows) {
1025
+ result.windows = { ...config2.windows };
1026
+ if (config2.windows.icon) {
1027
+ result.windows.icon = resolvePath(config2.windows.icon);
1028
+ }
1029
+ }
1030
+ return result;
1031
+ }
1032
+
1033
+ // src/utilities/projectConfig/validateConfig.ts
1034
+ var import_ajv3 = __toESM(require("ajv"));
1035
+ var import_ajv_formats2 = __toESM(require("ajv-formats"));
1036
+ var import_better_ajv_errors2 = __toESM(require("better-ajv-errors"));
1037
+
1038
+ // src/utilities/projectConfig/addCustomKeywords.ts
1039
+ var import_ajv2 = require("ajv");
1040
+ var import_email_regex = __toESM(require("email-regex"));
1041
+ var fs3 = __toESM(require("fs"));
1042
+ var path6 = __toESM(require("path"));
1043
+ var import_parse_author = __toESM(require("parse-author"));
1044
+ var semver = __toESM(require("semver"));
1045
+
1046
+ // src/utilities/projectConfig/validatePackageJSON.ts
1047
+ var import_ajv = __toESM(require("ajv"));
1048
+ var import_ajv_formats = __toESM(require("ajv-formats"));
1049
+ var import_better_ajv_errors = __toESM(require("better-ajv-errors"));
1050
+
1051
+ // src/utilities/projectConfig/schema/packageJSON.ts
1052
+ var packageJSON_default = (context) => {
1053
+ return {
1054
+ type: "object",
1055
+ required: [
1056
+ "author",
1057
+ "dependencies",
1058
+ "devDependencies",
1059
+ "homepage",
1060
+ "name",
1061
+ "version"
1062
+ ],
1063
+ properties: {
1064
+ author: {
1065
+ npmAuthor: {}
1066
+ },
1067
+ dependencies: {
1068
+ type: "object",
1069
+ required: ["@todesktop/runtime"],
1070
+ excludedDependencies: {
1071
+ dependencyKey: "dependencies",
1072
+ blacklist: ["@todesktop/cli"]
1073
+ },
1074
+ properties: {
1075
+ "@todesktop/runtime": {
1076
+ type: "string"
1077
+ }
1078
+ }
1079
+ },
1080
+ devDependencies: {
1081
+ type: "object",
1082
+ required: ["electron"],
1083
+ properties: {
1084
+ electron: {
1085
+ type: "string",
1086
+ semanticVersion: {
1087
+ packageName: "electron",
1088
+ mustBeExact: true
1089
+ }
1090
+ }
1091
+ }
1092
+ },
1093
+ name: {
1094
+ type: "string",
1095
+ minLength: 1
1096
+ },
1097
+ scripts: {
1098
+ type: "object",
1099
+ properties: {
1100
+ "todesktop:beforeInstall": {
1101
+ type: "string",
1102
+ file: {
1103
+ from: context.projectRoot,
1104
+ mustBeFile: true
1105
+ }
1106
+ },
1107
+ "todesktop:afterPack": {
1108
+ type: "string",
1109
+ file: {
1110
+ from: context.projectRoot,
1111
+ mustBeFile: true
1112
+ }
1113
+ }
1114
+ }
1115
+ },
1116
+ version: {
1117
+ type: "string",
1118
+ semanticVersion: {}
1119
+ }
1120
+ }
1121
+ };
1122
+ };
1123
+
1124
+ // src/utilities/projectConfig/validatePackageJSON.ts
1125
+ var validatePackageJSON_default = (pkg, pkgPath, context) => {
1126
+ const ajv = new import_ajv.default({ allErrors: true });
1127
+ (0, import_ajv_formats.default)(ajv);
1128
+ addCustomKeywords_default(ajv, context);
1129
+ const schema = packageJSON_default(context);
1130
+ const validate = ajv.compile(schema);
1131
+ if (!validate(pkg)) {
1132
+ const output = (0, import_better_ajv_errors.default)(schema, pkg, validate.errors, {
1133
+ indent: 2
1134
+ });
1135
+ const error = new Error(`package.json invalid (${pkgPath});
1136
+ ${output}`);
1137
+ error.isValidationError = true;
1138
+ throw error;
1139
+ }
1140
+ };
1141
+
1142
+ // src/utilities/projectConfig/addCustomKeywords.ts
1143
+ var addCustomKeywords_default = (ajv, context) => {
1144
+ const addKeyword = (def) => {
1145
+ const validate = (schema, data) => {
1146
+ try {
1147
+ return def.validate(schema, data);
1148
+ } catch (e) {
1149
+ if (e instanceof import_ajv2.ValidationError) {
1150
+ validate.errors = e.errors;
1151
+ return false;
1152
+ }
1153
+ throw e;
1154
+ }
1155
+ };
1156
+ ajv.addKeyword({ ...def, validate });
1157
+ };
1158
+ addKeyword({
1159
+ keyword: "excludedDependencies",
1160
+ validate: (schema, data) => {
1161
+ const matchingPackages = schema.blacklist.filter(
1162
+ (packageName) => data[packageName]
1163
+ );
1164
+ if (matchingPackages.length) {
1165
+ throw new import_ajv2.ValidationError([
1166
+ {
1167
+ keyword: "Dependency",
1168
+ message: `${matchingPackages.join(", ")} should not be in ${schema.dependencyKey}`,
1169
+ instancePath: `/${schema.dependencyKey}`
1170
+ }
1171
+ ]);
1172
+ }
1173
+ return true;
1174
+ }
1175
+ });
1176
+ addKeyword({
1177
+ keyword: "email",
1178
+ type: "string",
1179
+ validate: (schema, data) => {
1180
+ if (!(0, import_email_regex.default)({ exact: true }).test(data)) {
1181
+ throw new import_ajv2.ValidationError([
1182
+ {
1183
+ keyword: "email",
1184
+ message: "invalid"
1185
+ }
1186
+ ]);
1187
+ }
1188
+ return true;
1189
+ }
1190
+ });
1191
+ addKeyword({
1192
+ keyword: "file",
1193
+ type: "string",
1194
+ validate: (schema, data) => {
1195
+ const mustBeDirectory = schema.mustBeDirectory || schema.mustBeElectronApp;
1196
+ if (!data.trim().length) {
1197
+ if (schema.isOptional) {
1198
+ return true;
1199
+ }
1200
+ throw new import_ajv2.ValidationError([
1201
+ {
1202
+ keyword: schema.mustBeDirectory ? "Directory" : "File",
1203
+ message: "is empty"
1204
+ }
1205
+ ]);
1206
+ }
1207
+ const filePath = path6.isAbsolute(data) ? data : path6.join(schema.from, data);
1208
+ if (path6.relative(schema.from, filePath).startsWith("..")) {
1209
+ throw new import_ajv2.ValidationError([
1210
+ {
1211
+ keyword: mustBeDirectory ? "Directory" : "File",
1212
+ message: `is not in project (${filePath})`
1213
+ }
1214
+ ]);
1215
+ }
1216
+ if (schema.extensions && !schema.extensions.includes(path6.extname(filePath).substr(1))) {
1217
+ throw new import_ajv2.ValidationError([
1218
+ {
1219
+ keyword: "File extension",
1220
+ message: `invalid. ${path6.extname(data)} Must be${schema.extensions > 1 ? "one of the following:" : ""} "${schema.extensions.join('", "')}"`
1221
+ }
1222
+ ]);
1223
+ }
1224
+ if (!fs3.existsSync(filePath)) {
1225
+ throw new import_ajv2.ValidationError([
1226
+ {
1227
+ keyword: mustBeDirectory ? "Directory" : "File",
1228
+ message: `does not exist (${filePath})`
1229
+ }
1230
+ ]);
1231
+ }
1232
+ const stats = fs3.statSync(filePath);
1233
+ if (mustBeDirectory && stats.isFile()) {
1234
+ throw new import_ajv2.ValidationError([
1235
+ {
1236
+ keyword: "Directory",
1237
+ message: `must be a directory, not a file (${filePath})`
1238
+ }
1239
+ ]);
1240
+ } else if (schema.mustBeFile && stats.isDirectory()) {
1241
+ throw new import_ajv2.ValidationError([
1242
+ {
1243
+ keyword: "File",
1244
+ message: `must be a file, not a directory (${filePath})`
1245
+ }
1246
+ ]);
1247
+ }
1248
+ if (schema.mustBeElectronApp) {
1249
+ const appRoot = path6.resolve(filePath);
1250
+ const pkgPath = path6.join(appRoot, "package.json");
1251
+ if (!fs3.existsSync(pkgPath)) {
1252
+ throw new import_ajv2.ValidationError([
1253
+ {
1254
+ keyword: "App",
1255
+ message: `invalid. There is no package.json at ${pkgPath}`
1256
+ }
1257
+ ]);
1258
+ }
1259
+ let pkg;
1260
+ try {
1261
+ pkg = require(pkgPath);
1262
+ } catch (e) {
1263
+ throw new import_ajv2.ValidationError([
1264
+ {
1265
+ keyword: "App",
1266
+ message: `invalid. Invalid JSON in package.json (${pkgPath})`
1267
+ }
1268
+ ]);
1269
+ }
1270
+ context.projectRoot = appRoot;
1271
+ validatePackageJSON_default(pkg, pkgPath, context);
1272
+ const mainFilePath = pkg.main;
1273
+ if (mainFilePath) {
1274
+ const resolvedMainFilePath = path6.join(appRoot, mainFilePath);
1275
+ if (!fs3.existsSync(resolvedMainFilePath)) {
1276
+ throw new import_ajv2.ValidationError([
1277
+ {
1278
+ keyword: "App",
1279
+ message: `invalid. The "main" file specified in your package.json (${mainFilePath}) does not exist (${resolvedMainFilePath})`
1280
+ }
1281
+ ]);
1282
+ }
1283
+ } else {
1284
+ if (!fs3.existsSync(path6.join(appRoot, "index.js"))) {
1285
+ throw new import_ajv2.ValidationError([
1286
+ {
1287
+ keyword: "App",
1288
+ message: 'invalid. There is neither a "main" property in your package.json nor an index.js at the root of your app'
1289
+ }
1290
+ ]);
1291
+ }
1292
+ }
1293
+ }
1294
+ return true;
1295
+ },
1296
+ errors: true
1297
+ });
1298
+ addKeyword({
1299
+ keyword: "npmAuthor",
1300
+ validate: (schema, data) => {
1301
+ const dataType = typeof data;
1302
+ if (dataType === "undefined") {
1303
+ return true;
1304
+ } else if (dataType === "string") {
1305
+ const parsedAuthor = (0, import_parse_author.default)(data);
1306
+ if (!parsedAuthor || !parsedAuthor.email) {
1307
+ throw new import_ajv2.ValidationError([
1308
+ {
1309
+ keyword: "author",
1310
+ message: `invalid. If you're using a string, it must look something like "Barney Rubble <b@rubble.com>", see https://docs.npmjs.com/files/package.json#people-fields-author-contributors`
1311
+ }
1312
+ ]);
1313
+ }
1314
+ } else if (dataType === "object") {
1315
+ if (typeof data.name === "undefined") {
1316
+ throw new import_ajv2.ValidationError([
1317
+ {
1318
+ keyword: "author",
1319
+ message: `invalid. "name" property is missing.`
1320
+ }
1321
+ ]);
1322
+ } else {
1323
+ if (typeof data.name !== "string") {
1324
+ throw new import_ajv2.ValidationError([
1325
+ {
1326
+ keyword: "author",
1327
+ message: `.name invalid. It must be a string`
1328
+ }
1329
+ ]);
1330
+ }
1331
+ if (typeof data.email === "undefined") {
1332
+ throw new import_ajv2.ValidationError([
1333
+ {
1334
+ keyword: "author",
1335
+ message: `invalid. "email" property is missing.`
1336
+ }
1337
+ ]);
1338
+ } else {
1339
+ if (typeof data.email === "string") {
1340
+ if (!(0, import_email_regex.default)({ exact: true }).test(data.email)) {
1341
+ throw new import_ajv2.ValidationError([
1342
+ {
1343
+ keyword: "author",
1344
+ message: `.email invalid. It does not look like an email.`
1345
+ }
1346
+ ]);
1347
+ }
1348
+ } else {
1349
+ throw new import_ajv2.ValidationError([
1350
+ {
1351
+ keyword: "author",
1352
+ message: `.email invalid. It must be a string.`
1353
+ }
1354
+ ]);
1355
+ }
1356
+ }
1357
+ }
1358
+ } else {
1359
+ throw new import_ajv2.ValidationError([
1360
+ {
1361
+ keyword: "author",
1362
+ message: "invalid. It must either be an object or string. See https://docs.npmjs.com/files/package.json#people-fields-author-contributors"
1363
+ }
1364
+ ]);
1365
+ }
1366
+ return true;
1367
+ },
1368
+ errors: true
1369
+ });
1370
+ addKeyword({
1371
+ keyword: "semanticVersion",
1372
+ type: "string",
1373
+ validate: (schema, data) => {
1374
+ const keyword = (schema.packageName ? `${schema.packageName} ` : "") + "version";
1375
+ if (!(semver.valid(data) || semver.validRange(data))) {
1376
+ throw new import_ajv2.ValidationError([
1377
+ {
1378
+ keyword,
1379
+ message: "invalid. It must be a semantic version (see https://semver.org/)"
1380
+ }
1381
+ ]);
1382
+ }
1383
+ if (schema.mustBeExact && (data.startsWith("~") || data.startsWith("^"))) {
1384
+ throw new import_ajv2.ValidationError([
1385
+ {
1386
+ keyword,
1387
+ message: "invalid. It must be an exact version. Good: 9.0.0. Bad: ^9.0.0"
1388
+ }
1389
+ ]);
1390
+ }
1391
+ return true;
1392
+ }
1393
+ });
1394
+ };
1395
+
1396
+ // src/utilities/projectConfig/schema/full.ts
1397
+ var getItemOrArrayOfItemsSchema = (itemSchema) => {
1398
+ return {
1399
+ oneOf: [
1400
+ itemSchema,
1401
+ {
1402
+ type: "array",
1403
+ items: itemSchema
1404
+ }
1405
+ ]
1406
+ };
1407
+ };
1408
+ var full_default = (context) => {
1409
+ const getIconSchema = (additionalAllowedExtensions = []) => {
1410
+ return {
1411
+ type: "string",
1412
+ file: {
1413
+ from: context.projectRoot,
1414
+ extensions: ["icns", "png", ...additionalAllowedExtensions],
1415
+ mustBeFile: true
1416
+ },
1417
+ minLength: 3
1418
+ };
1419
+ };
1420
+ return {
1421
+ type: "object",
1422
+ required: ["id", "icon", "schemaVersion"],
1423
+ properties: {
1424
+ appId: { type: "string", minLength: 1 },
1425
+ appFiles: {
1426
+ type: "array",
1427
+ items: {
1428
+ type: "string",
1429
+ minLength: 1
1430
+ },
1431
+ minItems: 1
1432
+ },
1433
+ appProtocolScheme: { type: "string", minLength: 1 },
1434
+ appPath: {
1435
+ type: "string",
1436
+ file: {
1437
+ from: context.projectRoot,
1438
+ isOptional: true,
1439
+ mustBeElectronApp: true
1440
+ }
1441
+ },
1442
+ asarUnpack: {
1443
+ oneOf: [
1444
+ { type: "boolean" },
1445
+ {
1446
+ type: "array",
1447
+ items: {
1448
+ type: "string",
1449
+ minLength: 1
1450
+ },
1451
+ minItems: 1
1452
+ }
1453
+ ]
1454
+ },
1455
+ copyright: { type: "string", minLength: 1 },
1456
+ electronMirror: {
1457
+ type: "string",
1458
+ format: "uri"
1459
+ },
1460
+ electronVersion: {
1461
+ type: "string",
1462
+ minLength: 1
1463
+ },
1464
+ extraContentFiles: {
1465
+ type: "array",
1466
+ items: {
1467
+ type: "object",
1468
+ required: ["from"],
1469
+ properties: {
1470
+ from: {
1471
+ type: "string",
1472
+ file: { from: context.projectRoot }
1473
+ },
1474
+ to: { type: "string" }
1475
+ }
1476
+ }
1477
+ },
1478
+ extraResources: {
1479
+ type: "array",
1480
+ items: {
1481
+ type: "object",
1482
+ required: ["from"],
1483
+ properties: {
1484
+ from: {
1485
+ type: "string",
1486
+ file: { from: context.projectRoot }
1487
+ },
1488
+ to: { type: "string" }
1489
+ }
1490
+ }
1491
+ },
1492
+ fileAssociations: {
1493
+ type: "array",
1494
+ items: {
1495
+ type: "object",
1496
+ required: ["ext"],
1497
+ properties: {
1498
+ ext: getItemOrArrayOfItemsSchema({
1499
+ type: "string",
1500
+ minLength: 1
1501
+ }),
1502
+ description: {
1503
+ type: "string",
1504
+ minLength: 1
1505
+ },
1506
+ name: {
1507
+ type: "string",
1508
+ minLength: 1
1509
+ },
1510
+ mimeType: {
1511
+ type: "string",
1512
+ minLength: 1
1513
+ },
1514
+ icon: {
1515
+ type: "string",
1516
+ minLength: 1
1517
+ },
1518
+ role: {
1519
+ type: "string",
1520
+ minLength: 1
1521
+ },
1522
+ isPackage: {
1523
+ type: "boolean"
1524
+ },
1525
+ rank: {
1526
+ type: "string",
1527
+ minLength: 1
1528
+ }
1529
+ }
1530
+ },
1531
+ minItems: 1
1532
+ },
1533
+ filesForDistribution: {
1534
+ type: "array",
1535
+ items: {
1536
+ type: "string",
1537
+ minLength: 1
1538
+ },
1539
+ minItems: 1
1540
+ },
1541
+ icon: getIconSchema(),
1542
+ linux: {
1543
+ type: "object",
1544
+ properties: {
1545
+ category: { type: "string", minLength: 1 },
1546
+ icon: getIconSchema(),
1547
+ noSandbox: { type: "boolean" }
1548
+ }
1549
+ },
1550
+ id: { type: "string", minLength: 1 },
1551
+ mac: {
1552
+ type: "object",
1553
+ properties: {
1554
+ category: { type: "string", minLength: 1 },
1555
+ additionalBinariesToSign: {
1556
+ type: "array",
1557
+ items: {
1558
+ type: "string",
1559
+ minLength: 1
1560
+ }
1561
+ },
1562
+ entitlements: {
1563
+ type: "string",
1564
+ file: {
1565
+ from: context.projectRoot,
1566
+ extensions: ["plist"],
1567
+ mustBeFile: true
1568
+ },
1569
+ minLength: 1
1570
+ },
1571
+ extendInfo: {
1572
+ type: "object"
1573
+ },
1574
+ icon: getIconSchema()
1575
+ }
1576
+ },
1577
+ dmg: {
1578
+ type: "object",
1579
+ properties: {
1580
+ background: {
1581
+ type: "string",
1582
+ file: {
1583
+ from: context.projectRoot,
1584
+ extensions: ["tiff"],
1585
+ mustBeFile: true
1586
+ },
1587
+ minLength: 1
1588
+ },
1589
+ artifactName: { type: "string", minLength: 1 },
1590
+ backgroundColor: { type: "string", minLength: 1 },
1591
+ iconSize: { type: "number" },
1592
+ iconTextSize: { type: "number" },
1593
+ title: { type: "string", minLength: 1 },
1594
+ contents: {
1595
+ type: "array",
1596
+ items: {
1597
+ type: "object",
1598
+ properties: {
1599
+ x: { type: "number" },
1600
+ y: { type: "number" }
1601
+ }
1602
+ }
1603
+ },
1604
+ window: {
1605
+ type: "object",
1606
+ properties: {
1607
+ x: { type: "number" },
1608
+ y: { type: "number" },
1609
+ width: { type: "number" },
1610
+ height: { type: "number" }
1611
+ }
1612
+ }
1613
+ }
1614
+ },
1615
+ schemaVersion: { type: "number", minimum: 1, maximum: 1 },
1616
+ snap: {
1617
+ type: "object",
1618
+ properties: {
1619
+ after: {
1620
+ type: "array",
1621
+ items: {
1622
+ type: "string",
1623
+ minLength: 1
1624
+ }
1625
+ },
1626
+ appPartStage: {
1627
+ type: "array",
1628
+ items: {
1629
+ type: "string",
1630
+ minLength: 1
1631
+ }
1632
+ },
1633
+ assumes: getItemOrArrayOfItemsSchema({
1634
+ type: "string",
1635
+ minLength: 1
1636
+ }),
1637
+ autoStart: {
1638
+ type: "boolean"
1639
+ },
1640
+ buildPackages: {
1641
+ type: "array",
1642
+ items: {
1643
+ type: "string",
1644
+ minLength: 1
1645
+ }
1646
+ },
1647
+ confinement: {
1648
+ type: "string",
1649
+ enum: ["classic", "devmode", "strict"]
1650
+ },
1651
+ environment: {
1652
+ type: "object"
1653
+ },
1654
+ grade: {
1655
+ type: "string",
1656
+ enum: ["devel", "stable"]
1657
+ },
1658
+ layout: {
1659
+ type: "object"
1660
+ },
1661
+ plugs: {
1662
+ type: "array",
1663
+ items: {
1664
+ anyOf: [
1665
+ {
1666
+ type: "string",
1667
+ minLength: 1
1668
+ },
1669
+ {
1670
+ type: "object"
1671
+ }
1672
+ ]
1673
+ }
1674
+ },
1675
+ stagePackages: {
1676
+ type: "array",
1677
+ items: {
1678
+ type: "string",
1679
+ minLength: 1
1680
+ }
1681
+ },
1682
+ summary: {
1683
+ type: "string",
1684
+ maxLength: 78
1685
+ },
1686
+ useTemplateApp: {
1687
+ type: "boolean"
1688
+ }
1689
+ }
1690
+ },
1691
+ uploadSizeLimit: {
1692
+ type: "number"
1693
+ },
1694
+ windows: {
1695
+ type: "object",
1696
+ properties: {
1697
+ icon: getIconSchema(["ico"])
1698
+ }
1699
+ }
1700
+ }
1701
+ };
1702
+ };
1703
+
1704
+ // src/utilities/projectConfig/validateConfig.ts
1705
+ function validateConfig({
1706
+ config: config2,
1707
+ projectRoot
1708
+ }) {
1709
+ const context = { projectRoot };
1710
+ const schema = full_default(context);
1711
+ const ajv = new import_ajv3.default({ allErrors: true });
1712
+ (0, import_ajv_formats2.default)(ajv);
1713
+ addCustomKeywords_default(ajv, context);
1714
+ const validate = ajv.compile(schema);
1715
+ if (!validate(config2)) {
1716
+ const output = (0, import_better_ajv_errors2.default)(schema, config2, validate.errors, {
1717
+ indent: 2
1718
+ });
1719
+ throw new Error(
1720
+ `todesktop.json invalid.
1721
+ Learn more here: https://www.npmjs.com/package/@todesktop/cli#project-configuration-todesktopjson
1722
+
1723
+ ${output}`
1724
+ );
1725
+ }
1726
+ if (config2.productName) {
1727
+ throw new Error(
1728
+ `todesktop.json invalid.
1729
+
1730
+ The "productName" property is no longer supported in todesktop.json. Please remove it and add it to your app's package.json instead.
1731
+
1732
+ We made this change because Electron also uses the "productName" if it exists in your app's package.json. If you do not add it to your package.json, your app name will default to the value of the "name" property in your package.json.`
1733
+ );
1734
+ }
1735
+ }
1736
+
1737
+ // src/utilities/projectConfig/computeFullProjectConfig.ts
1738
+ var import_path4 = require("path");
1739
+ var import_lodash2 = __toESM(require("lodash.merge"));
1740
+ function computeFullProjectConfig(partialConfig, projectRoot) {
1741
+ if (!partialConfig.extends) {
1742
+ logger_default.debug("No extends field, returning partial config");
1743
+ return partialConfig;
1744
+ } else {
1745
+ logger_default.debug("Extends field found, resolving");
1746
+ const parentConfigPath = (0, import_path4.resolve)(projectRoot, partialConfig.extends);
1747
+ const parentConfig = loadConfig(parentConfigPath);
1748
+ const parentFullConfig = computeFullProjectConfig(
1749
+ parentConfig,
1750
+ projectRoot
1751
+ );
1752
+ const result = (0, import_lodash2.default)({}, parentFullConfig, partialConfig);
1753
+ delete result.extends;
1754
+ return result;
1755
+ }
1756
+ }
1757
+
1758
+ // src/utilities/projectConfig/getProjectConfig.ts
1759
+ function getProjectConfig(configPath = null) {
1760
+ if (configPath === null) {
1761
+ logger_default.debug("No config path provided, searching for one");
1762
+ configPath = import_find_up.default.sync("todesktop.json");
1763
+ if (!configPath) {
1764
+ throw new Error(
1765
+ "Can not find todesktop.json in this folder or any parent folders"
1766
+ );
1767
+ }
1768
+ } else {
1769
+ if (typeof configPath === "undefined" || configPath === "") {
1770
+ logger_default.error("Provided config path is empty");
1771
+ throw new Error("No config path provided");
1772
+ }
1773
+ configPath = (0, import_path5.resolve)(process.cwd(), configPath);
1774
+ if (!(0, import_fs.existsSync)(configPath)) {
1775
+ logger_default.error("Provided config path does not exist");
1776
+ throw new Error(`Config file not found at ${configPath}`);
1777
+ }
1778
+ }
1779
+ const projectRoot = (0, import_path5.dirname)(configPath);
1780
+ const partialConfig = loadConfig(configPath);
1781
+ partialConfig.appPath = partialConfig.appPath || ".";
1782
+ const config2 = computeFullProjectConfig(partialConfig, projectRoot);
1783
+ validateConfig({ config: config2, projectRoot });
1784
+ const result = resolveConfigPaths({ config: config2, projectRoot });
1785
+ return { config: result, unprocessedConfig: config2, projectRoot };
1786
+ }
1787
+
1788
+ // src/utilities/postToFirebaseFunction.ts
1789
+ var import_axios = __toESM(require("axios"));
1790
+ var { TODESKTOP_CLI_FIREBASE_FUNCTIONS_BASE } = getEnvironmentVariables_default();
1791
+ async function postToFirebaseFunction_default(functionName, body = {}, config2 = {}) {
1792
+ logger_default.debug({ functionName, body, config: config2 }, "postToFirebaseFunction");
1793
+ try {
1794
+ const response = await import_axios.default.post(
1795
+ `${TODESKTOP_CLI_FIREBASE_FUNCTIONS_BASE}${functionName}`,
1796
+ body,
1797
+ config2
1798
+ );
1799
+ logger_default.debug(
1800
+ { responseData: response.data },
1801
+ "postToFirebaseFunction: success"
1802
+ );
1803
+ return response.data;
1804
+ } catch (e) {
1805
+ logger_default.error({ error: e }, "postToFirebaseFunction: error");
1806
+ throw e;
1807
+ }
1808
+ }
1809
+
1810
+ // src/utilities/subscribeToFirebaseDoc.ts
1811
+ var subscribeToFirebaseDoc_default = async (key, receiver) => {
1812
+ const hasResolved = false;
1813
+ return new Promise((resolve4, reject) => {
1814
+ logger_default.debug({ key }, "subscribeToFirebaseDoc");
1815
+ firestore_default.doc(key).onSnapshot(
1816
+ (snapshot) => {
1817
+ receiver({
1818
+ snapshot,
1819
+ data: snapshot.exists ? snapshot.data() : void 0
1820
+ });
1821
+ if (!hasResolved) {
1822
+ resolve4();
1823
+ }
1824
+ },
1825
+ (err) => {
1826
+ reject(err);
1827
+ }
1828
+ );
1829
+ });
1830
+ };
1831
+
1832
+ // src/utilities/subscribeToBuild.ts
1833
+ var subscribeToBuild_default = async ({ appId, buildId, onDataReceived, userId }) => {
1834
+ try {
1835
+ return await subscribeToFirebaseDoc_default(
1836
+ `users/${userId}/applications/${appId}/builds/${buildId}`,
1837
+ async ({ data }) => {
1838
+ onDataReceived(data);
1839
+ }
1840
+ );
1841
+ } catch (e) {
1842
+ e.message = `Failed while subscribing to build; ${e.message}`;
1843
+ throw e;
1844
+ }
1845
+ };
1846
+
1847
+ // src/utilities/uploadApplicationSource.ts
1848
+ var import_fast_glob = __toESM(require("fast-glob"));
1849
+ var fs5 = __toESM(require("fs"));
1850
+ var path8 = __toESM(require("path"));
1851
+
1852
+ // src/utilities/generateS3Key.ts
1853
+ var generateS3Key_default = ({ appId, buildId, filenameSuffix }) => `${appId}/sourceArchives/${buildId}--${filenameSuffix}`;
1854
+
1855
+ // src/utilities/uploadToS3.ts
1856
+ var import_superagent = __toESM(require("superagent"));
1857
+ var import_util = __toESM(require("util"));
1858
+ var import_stream_to_array = __toESM(require("stream-to-array"));
1859
+ var { TODESKTOP_CLI_S3_BUCKET } = getEnvironmentVariables_default();
1860
+ var uploadToS3_default = async ({
1861
+ bucket = TODESKTOP_CLI_S3_BUCKET,
1862
+ inputStream,
1863
+ key,
1864
+ onProgress = () => {
1865
+ }
1866
+ }) => {
1867
+ const { data } = await getCallableFirebaseFunction_default("getSignedURL")({
1868
+ key
1869
+ });
1870
+ const body = await new Promise((resolve4) => {
1871
+ (0, import_stream_to_array.default)(inputStream).then(function(parts) {
1872
+ const buffers = parts.map(
1873
+ (part) => import_util.default.isBuffer(part) ? part : Buffer.from(part)
1874
+ );
1875
+ return resolve4(Buffer.concat(buffers));
1876
+ });
1877
+ });
1878
+ return new Promise((resolve4) => {
1879
+ import_superagent.default.put(data.signedURL).send(body).set("content-type", "application/zip").on("progress", (event) => onProgress(event)).end(() => {
1880
+ logger_default.debug({ bucket, key, url: data.uploadURL }, "uploadToS3");
1881
+ resolve4({ bucket, key, url: data.uploadURL });
1882
+ });
1883
+ return { bucket, key, url: data.uploadURL };
1884
+ });
1885
+ };
1886
+
1887
+ // src/utilities/zip.ts
1888
+ var import_archiver = __toESM(require("archiver"));
1889
+ var import_du = __toESM(require("du"));
1890
+ var import_fs2 = __toESM(require("fs"));
1891
+ var import_chalk = __toESM(require("chalk"));
1892
+ var import_path6 = __toESM(require("path"));
1893
+ async function zip_default({
1894
+ files,
1895
+ fileSizeLimit = 20,
1896
+ onError,
1897
+ onProgress,
1898
+ appPkgJson
1899
+ }) {
1900
+ logger_default.debug({ files }, "zip");
1901
+ const stream = (0, import_archiver.default)("zip");
1902
+ stream.on("warning", function(err) {
1903
+ if (err.code === "ENOENT") {
1904
+ console.warn(err);
1905
+ logger_default.warn({ err }, "zip: stream warning ENOENT");
1906
+ } else {
1907
+ onError(err);
1908
+ }
1909
+ });
1910
+ stream.on("error", function(err) {
1911
+ onError(err);
1912
+ });
1913
+ const processedFiles = await Promise.all(
1914
+ files.map(async (file) => {
1915
+ const stats = import_fs2.default.lstatSync(file.from);
1916
+ const isDirectory = stats.isDirectory();
1917
+ return {
1918
+ ...file,
1919
+ isDirectory,
1920
+ stats,
1921
+ size: isDirectory ? await (0, import_du.default)(file.from) : stats.size
1922
+ };
1923
+ })
1924
+ );
1925
+ const totalFileSize = processedFiles.reduce((sum, b) => sum + b.size, 0);
1926
+ if (totalFileSize > fileSizeLimit * 1e6) {
1927
+ onError(
1928
+ new Error(
1929
+ [
1930
+ `
1931
+
1932
+ Your app is larger than ${fileSizeLimit}MB. Your app is ${import_chalk.default.bold.red.underline(
1933
+ (totalFileSize / 1e6).toFixed(1) + "MB"
1934
+ )}.
1935
+ `,
1936
+ `You may be including unnecessary files in your app(see the \`appPath\` and \`appFiles\` options).`,
1937
+ `If not, this limit can be raised by setting the \`uploadSizeLimit\` option.`,
1938
+ `Learn more at https://www.npmjs.com/package/@todesktop/cli#uploadsizelimit---optional-number`
1939
+ ].join("\n")
1940
+ )
1941
+ );
1942
+ return stream;
1943
+ }
1944
+ processedFiles.forEach(({ from, isDirectory, stats, to }) => {
1945
+ if (isDirectory) {
1946
+ stream.directory(from, to);
1947
+ } else if (appPkgJson && to === import_path6.default.join("app", "package.json")) {
1948
+ stream.append(JSON.stringify(appPkgJson), {
1949
+ name: to
1950
+ });
1951
+ } else {
1952
+ stream.file(from, { name: to, mode: stats.mode });
1953
+ }
1954
+ });
1955
+ stream.finalize();
1956
+ stream.on(
1957
+ "progress",
1958
+ (progress) => onProgress({
1959
+ ...progress,
1960
+ percentage: progress.entries.processed / progress.entries.total * 100
1961
+ })
1962
+ );
1963
+ return stream;
1964
+ }
1965
+
1966
+ // src/utilities/uploadApplicationSource.ts
1967
+ var getAppFiles = async (globsInput, appPath, appPkgJson) => {
1968
+ const globs = ["!node_modules", "!**/node_modules", "!.git", "!**/.git"];
1969
+ if (globsInput && globsInput.length) {
1970
+ globs.push(
1971
+ ...globsInput,
1972
+ path8.join(appPath, "package.json"),
1973
+ path8.join(appPath, "package-lock.json"),
1974
+ path8.join(appPath, "yarn.lock"),
1975
+ path8.join(appPath, "pnpm-lock.yaml"),
1976
+ path8.join(appPath, "shrinkwrap.yaml")
1977
+ );
1978
+ } else {
1979
+ globs.push("**");
1980
+ }
1981
+ for (const hookName of ["todesktop:beforeInstall", "todesktop:afterPack"]) {
1982
+ if (appPkgJson.scripts && appPkgJson.scripts[hookName]) {
1983
+ globs.push(path8.join(appPath, appPkgJson.scripts[hookName]));
1984
+ }
1985
+ }
1986
+ const normalizedGlobs = globs.map((glob) => {
1987
+ const globToUse = path8.isAbsolute(glob) ? path8.relative(appPath, glob) : glob;
1988
+ return globToUse.replace(/\\/g, "/").replace(/\/+$/, "");
1989
+ }).filter((glob) => !glob.startsWith("..") && !glob.startsWith("!.."));
1990
+ let absolutePaths = await (0, import_fast_glob.default)(normalizedGlobs, {
1991
+ absolute: true,
1992
+ cwd: appPath,
1993
+ dot: true,
1994
+ followSymbolicLinks: false,
1995
+ onlyDirectories: false,
1996
+ onlyFiles: true,
1997
+ globstar: true,
1998
+ unique: true
1999
+ });
2000
+ if (process.platform === "win32") {
2001
+ absolutePaths = absolutePaths.map(
2002
+ (absolutePath) => absolutePath.replace(/\//g, path8.sep)
2003
+ );
2004
+ }
2005
+ if (!absolutePaths || !absolutePaths.length) {
2006
+ throw new Error("No files found to upload");
2007
+ } else if (!absolutePaths.filter((absolutePath) => absolutePath.endsWith(".js")).length) {
2008
+ throw new Error(
2009
+ `No .js files found to upload (${absolutePaths[0]}). There's likely an issue with 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.`
2010
+ );
2011
+ } else {
2012
+ let mainFilePath = appPath;
2013
+ if (appPkgJson.main) {
2014
+ mainFilePath = path8.join(mainFilePath, appPkgJson.main);
2015
+ }
2016
+ if (fs5.statSync(mainFilePath).isDirectory()) {
2017
+ mainFilePath = path8.join(mainFilePath, "index.js");
2018
+ }
2019
+ if (!absolutePaths.includes(mainFilePath)) {
2020
+ throw new Error(
2021
+ `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.`
2022
+ );
2023
+ }
2024
+ }
2025
+ return absolutePaths.map((absolutePath) => {
2026
+ return {
2027
+ from: absolutePath,
2028
+ to: path8.join("app", path8.relative(appPath, absolutePath))
2029
+ };
2030
+ });
2031
+ };
2032
+ var uploadApplicationSource_default = async ({ appId, appPkgJson, buildId, config: config2, onProgress }) => {
2033
+ logger_default.debug(
2034
+ {
2035
+ appId,
2036
+ appPkgJson,
2037
+ buildId,
2038
+ config: config2,
2039
+ onProgress
2040
+ },
2041
+ "uploadApplicationSource"
2042
+ );
2043
+ let totalBytes = 0;
2044
+ const files = [
2045
+ ...await getAppFiles(config2.appFiles, config2.appPath, appPkgJson),
2046
+ ...(config2.extraContentFiles || []).map(({ from, to = "." }) => {
2047
+ return {
2048
+ from,
2049
+ to: path8.join("extraContentFiles", to, path8.basename(from))
2050
+ };
2051
+ }),
2052
+ ...(config2.extraResources || []).map(({ from, to = "." }) => {
2053
+ return {
2054
+ from,
2055
+ to: path8.join("extraResources", to, path8.basename(from))
2056
+ };
2057
+ }),
2058
+ {
2059
+ from: config2.icon,
2060
+ to: path8.join("icons", "appIcon" + path8.extname(config2.icon))
2061
+ }
2062
+ ];
2063
+ if (config2.linux) {
2064
+ if (config2.linux.icon) {
2065
+ files.push({
2066
+ from: config2.linux.icon,
2067
+ to: path8.join(
2068
+ "icons",
2069
+ "appIcon-linux" + path8.extname(config2.linux.icon)
2070
+ )
2071
+ });
2072
+ }
2073
+ }
2074
+ if (config2.mac) {
2075
+ if (config2.mac.entitlements) {
2076
+ files.push({
2077
+ from: config2.mac.entitlements,
2078
+ to: path8.join("other", "mac", "entitlements.mac.plist")
2079
+ });
2080
+ }
2081
+ if (config2.mac.icon) {
2082
+ files.push({
2083
+ from: config2.mac.icon,
2084
+ to: path8.join("icons", "appIcon-mac" + path8.extname(config2.mac.icon))
2085
+ });
2086
+ }
2087
+ }
2088
+ if (config2.dmg) {
2089
+ if (config2.dmg.background) {
2090
+ files.push({
2091
+ from: config2.dmg.background,
2092
+ to: path8.join("other", "mac", "dmg-background.tiff")
2093
+ });
2094
+ }
2095
+ }
2096
+ if (config2.windows) {
2097
+ if (config2.windows.icon) {
2098
+ files.push({
2099
+ from: config2.windows.icon,
2100
+ to: path8.join(
2101
+ "icons",
2102
+ "appIcon-windows" + path8.extname(config2.windows.icon)
2103
+ )
2104
+ });
2105
+ }
2106
+ }
2107
+ return uploadToS3_default({
2108
+ bucket: config2.bucket || void 0,
2109
+ inputStream: await zip_default({
2110
+ files,
2111
+ fileSizeLimit: config2.uploadSizeLimit,
2112
+ onError: (e) => {
2113
+ throw e;
2114
+ },
2115
+ onProgress({ fs: fs6 }) {
2116
+ totalBytes = fs6.totalBytes;
2117
+ },
2118
+ appPkgJson
2119
+ }),
2120
+ key: generateS3Key_default({
2121
+ appId,
2122
+ buildId,
2123
+ filenameSuffix: `${appPkgJson.name}.zip`
2124
+ }),
2125
+ onProgress({ loaded, total }) {
2126
+ onProgress(loaded / total * 100, loaded);
2127
+ }
2128
+ });
2129
+ };
2130
+
2131
+ // src/utilities/getPackageJson.ts
2132
+ var import_lodash3 = __toESM(require("lodash.merge"));
2133
+ var import_path7 = __toESM(require("path"));
2134
+ function deleteNullDeps(dep) {
2135
+ Object.keys(dep).forEach((key) => {
2136
+ if (dep[key] === null) {
2137
+ delete dep[key];
2138
+ }
2139
+ });
2140
+ return dep;
2141
+ }
2142
+ function removeNullDependencies(pkgJson) {
2143
+ const { dependencies, devDependencies } = pkgJson;
2144
+ if (dependencies) {
2145
+ pkgJson.dependencies = deleteNullDeps(dependencies);
2146
+ }
2147
+ if (devDependencies) {
2148
+ pkgJson.devDependencies = deleteNullDeps(devDependencies);
2149
+ }
2150
+ return pkgJson;
2151
+ }
2152
+ function getAppPkgJson({ config: config2 }) {
2153
+ const packageJsonFromConfig = config2.packageJson || {};
2154
+ const extendsFrom = packageJsonFromConfig.extends || "package.json";
2155
+ const packageJsonFromFile = readJson(import_path7.default.join(config2.appPath, extendsFrom));
2156
+ return removeNullDependencies(
2157
+ (0, import_lodash3.default)({}, packageJsonFromFile, packageJsonFromConfig)
2158
+ );
2159
+ }
2160
+ var getPackageJson_default = getAppPkgJson;
2161
+
2162
+ // src/utilities/runBuild.ts
2163
+ var runBuild_default = async ({ onEvent, shouldCodeSign = true, configPath }) => {
2164
+ logForCI_default("Getting application information...");
2165
+ const primaryUserId = currentUser().uid;
2166
+ const { config: config2, unprocessedConfig } = getProjectConfig(configPath);
2167
+ const appId = config2.id;
2168
+ const appPkgJson = getPackageJson_default({ config: config2 });
2169
+ onEvent("progress", {
2170
+ appId,
2171
+ appPkg: appPkgJson,
2172
+ preparationProgress: 0.02
2173
+ });
2174
+ logForCI_default("Preparing...");
2175
+ let buildId;
2176
+ try {
2177
+ const prepareResult = await postToFirebaseFunction_default("prepareNewBuild", {
2178
+ appPkgName: appPkgJson.name,
2179
+ appPkgProductName: appPkgJson.productName,
2180
+ appVersion: appPkgJson.version,
2181
+ id: config2.id,
2182
+ projectConfig: unprocessedConfig,
2183
+ shouldCodeSign: shouldCodeSign !== false,
2184
+ shouldRelease: false,
2185
+ userId: primaryUserId,
2186
+ versionControlInfo: await getVersionControlInfo_default(config2.appPath)
2187
+ });
2188
+ buildId = prepareResult.appData.meta.currentBuildProgress.id;
2189
+ const firebaseUnsubscribe = await subscribeToBuild_default({
2190
+ appId,
2191
+ buildId,
2192
+ onDataReceived: (data) => onEvent("progress", { build: data }),
2193
+ userId: prepareResult.userId
2194
+ });
2195
+ onEvent("firebase-subscribed", { firebaseUnsubscribe });
2196
+ } catch (e) {
2197
+ e.message = `Failed while preparing new build; ${e.message}`;
2198
+ throw e;
2199
+ }
2200
+ onEvent("progress", {
2201
+ preparationProgress: 0.05,
2202
+ preparationStageLabel: "Uploading"
2203
+ });
2204
+ logForCI_default("Uploading...");
2205
+ let sourceArchiveDetails;
2206
+ try {
2207
+ const uploadAppSourceResult = await uploadApplicationSource_default({
2208
+ appId,
2209
+ appPkgJson,
2210
+ buildId,
2211
+ config: config2,
2212
+ onProgress(progress, uploadedSize) {
2213
+ const dataToEmit = {
2214
+ preparationProgress: 0.05 + progress / 100 * 0.95,
2215
+ preparationUploadedSize: ""
2216
+ };
2217
+ if (uploadedSize) {
2218
+ dataToEmit.preparationUploadedSize = (0, import_pretty_bytes.default)(uploadedSize);
2219
+ }
2220
+ onEvent("progress", dataToEmit);
2221
+ }
2222
+ });
2223
+ sourceArchiveDetails = uploadAppSourceResult;
2224
+ } catch (e) {
2225
+ e.message = `Failed while uploading application source; ${e.message}`;
2226
+ throw e;
2227
+ }
2228
+ onEvent("progress", { isPreparationComplete: true });
2229
+ logForCI_default("Kicking off build...");
2230
+ try {
2231
+ await postToFirebaseFunction_default("kickOffBuild", {
2232
+ appId,
2233
+ appPkgName: appPkgJson.name,
2234
+ appVersion: appPkgJson.version,
2235
+ buildId,
2236
+ userId: primaryUserId,
2237
+ sourceArchiveDetails
2238
+ });
2239
+ } catch (e) {
2240
+ e.message = `Failed while kicking off build; ${e.message}`;
2241
+ throw e;
2242
+ }
2243
+ };
2244
+
2245
+ // src/utilities/shouldExitOnBuildFailure.ts
2246
+ var shouldExitOnBuildFailure_default = (build) => {
2247
+ const hasBuildSettled = ["linux", "mac", "windows"].every(
2248
+ (platform) => build[platform].shouldSkip || "succeeded" === build[platform].status || "failed" === build[platform].status && build[platform].numberOfAttemptedBuilds === 2
2249
+ );
2250
+ const hasSettledWithError = ["linux", "mac", "windows"].some(
2251
+ (platform) => "failed" === build[platform].status
2252
+ );
2253
+ return hasBuildSettled && hasSettledWithError;
2254
+ };
2255
+
2256
+ // src/components/Build.tsx
2257
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2258
+ var initalState = {
2259
+ appId: null,
2260
+ appPkg: null,
2261
+ build: null,
2262
+ error: null,
2263
+ hasBuildEverFailed: false,
2264
+ isPreparationComplete: false,
2265
+ preparationStageLabel: "Preparing",
2266
+ preparationProgress: 0,
2267
+ preparationUploadedSize: null
2268
+ };
2269
+ var Build = ({ commandUsed, shouldCodeSign = true, configPath }) => {
2270
+ const exit = useExit_default();
2271
+ const [
2272
+ {
2273
+ appId,
2274
+ appPkg,
2275
+ build,
2276
+ error,
2277
+ hasBuildEverFailed,
2278
+ isPreparationComplete,
2279
+ preparationStageLabel,
2280
+ preparationProgress,
2281
+ preparationUploadedSize
2282
+ },
2283
+ setState
2284
+ ] = (0, import_react6.useState)(initalState);
2285
+ const onError = (e) => {
2286
+ const error2 = e.response ? e.response.data : e;
2287
+ logForCI_default(error2);
2288
+ setState((prevState) => ({
2289
+ ...prevState,
2290
+ error: error2
2291
+ }));
2292
+ };
2293
+ (0, import_react6.useEffect)(() => {
2294
+ let firebaseUnsubscribe;
2295
+ runBuild_default({
2296
+ onEvent: (eventName, data) => {
2297
+ logger_default.debug({ eventName, data }, "Build component: runBuild.onEvent");
2298
+ if (eventName === "firebase-subscribed") {
2299
+ firebaseUnsubscribe = data.firebaseUnsubscribe;
2300
+ } else if (eventName === "progress") {
2301
+ setState((prevState) => ({ ...prevState, ...data }));
2302
+ }
2303
+ },
2304
+ shouldCodeSign,
2305
+ configPath
2306
+ }).catch(onError);
2307
+ return () => {
2308
+ if (firebaseUnsubscribe) {
2309
+ firebaseUnsubscribe();
2310
+ }
2311
+ };
2312
+ }, [commandUsed, shouldCodeSign, configPath]);
2313
+ (0, import_react6.useEffect)(() => {
2314
+ if (hasBuildEverFailed && shouldExitOnBuildFailure_default(build)) {
2315
+ setTimeout(() => exit(new Error("Build has failed")), 10);
2316
+ }
2317
+ }, [build, exit, hasBuildEverFailed]);
2318
+ if (error) {
2319
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ErrorDisplay_default, { commandUsed, error });
2320
+ }
2321
+ if (!appPkg) {
2322
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InitialLoadingState_default, {});
2323
+ }
2324
+ const onBuildFailure = () => {
2325
+ if (hasBuildEverFailed) {
2326
+ return;
2327
+ }
2328
+ setState((prevState) => ({
2329
+ ...prevState,
2330
+ hasBuildEverFailed: true
2331
+ }));
2332
+ };
2333
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2334
+ MainLayout_default,
2335
+ {
2336
+ appId,
2337
+ appName: build && build.appName || appPkg.name,
2338
+ appVersion: build && build.appVersion || appPkg.version,
2339
+ build,
2340
+ commandUsed,
2341
+ hasBuildEverFailed,
2342
+ children: isPreparationComplete ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BuildProgress_default, { build, onBuildFailure }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2343
+ Preparation_default,
2344
+ {
2345
+ progressPercentage: preparationProgress,
2346
+ stageLabel: preparationStageLabel,
2347
+ uploadedSize: preparationUploadedSize
2348
+ }
2349
+ )
2350
+ }
2351
+ );
2352
+ };
2353
+ Build.propTypes = {
2354
+ commandUsed: import_prop_types9.default.string.isRequired,
2355
+ shouldCodeSign: import_prop_types9.default.bool,
2356
+ configPath: import_prop_types9.default.string
2357
+ };
2358
+ var Build_default = Build;
2359
+
2360
+ // src/utilities/checkIfReactIsUsable.ts
2361
+ var import_react7 = require("react");
2362
+ var checkIfReactIsUsable_default = ({ cons = console, proc = process } = {}) => {
2363
+ try {
2364
+ (0, import_react7.useState)(null);
2365
+ } catch (e) {
2366
+ cons.error(e);
2367
+ proc.exit(1);
2368
+ }
2369
+ };
2370
+
2371
+ // src/components/LoginHOC.tsx
2372
+ var import_ink16 = require("ink");
2373
+ var import_react10 = require("react");
2374
+ var import_prop_types11 = __toESM(require("prop-types"));
2375
+ var import_is_ci4 = __toESM(require("is-ci"));
2376
+
2377
+ // src/components/Login.tsx
2378
+ var import_ink14 = require("ink");
2379
+ var import_prop_types10 = __toESM(require("prop-types"));
2380
+ var import_react9 = require("react");
2381
+ var import_react_final_form = require("react-final-form");
2382
+
2383
+ // src/components/TextInput.tsx
2384
+ var import_ink_text_input = __toESM(require("ink-text-input"));
2385
+ var import_react8 = __toESM(require("react"));
2386
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2387
+ var TextInput = ({ onBlur, onFocus, ...props }) => {
2388
+ import_react8.default.useEffect(() => {
2389
+ onFocus();
2390
+ return onBlur;
2391
+ }, [onFocus, onBlur]);
2392
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink_text_input.default, { ...props, showCursor: true });
2393
+ };
2394
+ var TextInput_default = TextInput;
2395
+
2396
+ // src/components/Login.tsx
2397
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2398
+ var loginFields = [
2399
+ {
2400
+ name: "email",
2401
+ label: "Your email",
2402
+ placeholder: "name@email.com",
2403
+ Input: TextInput_default,
2404
+ validate: (value) => {
2405
+ if (!value)
2406
+ return "Required";
2407
+ }
2408
+ },
2409
+ {
2410
+ name: "accessToken",
2411
+ label: "Your access token",
2412
+ placeholder: "********",
2413
+ Input: TextInput_default,
2414
+ validate: (value) => {
2415
+ if (!value)
2416
+ return "Required";
2417
+ },
2418
+ mask: "*"
2419
+ }
2420
+ ];
2421
+ var Login = ({ setIsLoggedIn }) => {
2422
+ const [activeField, setActiveField] = (0, import_react9.useState)(0);
2423
+ const [error, setError] = (0, import_react9.useState)(null);
2424
+ const [failureMessage, setFailureMessage] = (0, import_react9.useState)(null);
2425
+ const [isSubmittingForm, setIsSubmittingForm] = (0, import_react9.useState)(false);
2426
+ const onFailure = (message2) => {
2427
+ setIsSubmittingForm(false);
2428
+ setFailureMessage(message2);
2429
+ setActiveField(0);
2430
+ };
2431
+ const onSubmitLogin = async ({ email, accessToken }) => {
2432
+ setFailureMessage(null);
2433
+ setIsSubmittingForm(true);
2434
+ try {
2435
+ const jwtToken = await postToFirebaseFunction_default("loginWithAccessToken", {
2436
+ email,
2437
+ accessToken
2438
+ });
2439
+ if (jwtToken) {
2440
+ await signInWithCustomToken(jwtToken);
2441
+ setAuthConfig(email, accessToken, jwtToken);
2442
+ setIsLoggedIn(true);
2443
+ } else {
2444
+ onFailure("Incorrrect credentials. Try again");
2445
+ }
2446
+ } catch (err) {
2447
+ if (err.code === 500) {
2448
+ setError(err);
2449
+ } else {
2450
+ onFailure(`Login unsuccessful: ${err.message}`);
2451
+ }
2452
+ }
2453
+ };
2454
+ if (error)
2455
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ErrorDisplay_default, { error });
2456
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2457
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { children: "You are not currently logged in." }),
2458
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { children: "Log in below:" }),
2459
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { marginBottom: 1 }),
2460
+ /* @__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(
2461
+ ({ 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: [
2462
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { bold: activeField === index, children: [
2463
+ label,
2464
+ ": "
2465
+ ] }),
2466
+ activeField === index ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2467
+ Input,
2468
+ {
2469
+ ...input,
2470
+ mask,
2471
+ placeholder,
2472
+ onSubmit: () => {
2473
+ if (meta.valid && !validating) {
2474
+ setActiveField((value) => value + 1);
2475
+ if (activeField === loginFields.length - 1) {
2476
+ handleSubmit();
2477
+ }
2478
+ } else {
2479
+ input.onBlur();
2480
+ }
2481
+ }
2482
+ }
2483
+ ) : 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 }),
2484
+ 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" }) }),
2485
+ meta.error && meta.touched && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Error2, { errorMessage: meta.error })
2486
+ ] }) : null }) }, name)
2487
+ ) }) }),
2488
+ 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..." }) }),
2489
+ failureMessage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Error2, { errorMessage: failureMessage, marginTop: true })
2490
+ ] });
2491
+ };
2492
+ 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 }) });
2493
+ Error2.propTypes = {
2494
+ errorMessage: import_prop_types10.default.string.isRequired,
2495
+ marginTop: import_prop_types10.default.bool
2496
+ };
2497
+ Login.propTypes = {
2498
+ setIsLoggedIn: import_prop_types10.default.func.isRequired
2499
+ };
2500
+ var Login_default = Login;
2501
+
2502
+ // src/components/LoadingText.tsx
2503
+ var import_ink15 = require("ink");
2504
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2505
+ var LoadingText = () => {
2506
+ const { stdout } = (0, import_ink15.useStdout)();
2507
+ const stdOutRedirected = !stdout.isTTY;
2508
+ if (stdOutRedirected) {
2509
+ return null;
2510
+ }
2511
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink15.Text, { children: "Loading..." });
2512
+ };
2513
+ var LoadingText_default = LoadingText;
2514
+
2515
+ // src/components/LoginHOC.tsx
2516
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2517
+ var LoginHOC = ({ children, isInteractive = true }) => {
2518
+ const [isLoggedIn, setIsLoggedIn] = (0, import_react10.useState)(false);
2519
+ const [isEffectDone, setEffectDone] = (0, import_react10.useState)(false);
2520
+ const [error, setError] = (0, import_react10.useState)(null);
2521
+ const { isRawModeSupported } = (0, import_ink16.useStdin)();
2522
+ const { stdout } = (0, import_ink16.useStdout)();
2523
+ const stdOutRedirected = !stdout.isTTY;
2524
+ const onFailure = (message2, err = {}) => {
2525
+ setError({
2526
+ ...err,
2527
+ message: message2
2528
+ });
2529
+ };
2530
+ (0, import_react10.useEffect)(() => {
2531
+ async function isAccessTokenValid() {
2532
+ const { accessToken, email, jwtToken } = getAuthConfig();
2533
+ let userCreds = null;
2534
+ const { TODESKTOP_ACCESS_TOKEN, TODESKTOP_EMAIL } = getEnvironmentVariables_default();
2535
+ try {
2536
+ if (TODESKTOP_ACCESS_TOKEN && TODESKTOP_EMAIL) {
2537
+ const newJwtToken = await postToFirebaseFunction_default(
2538
+ "loginWithAccessToken",
2539
+ {
2540
+ email: TODESKTOP_EMAIL,
2541
+ accessToken: TODESKTOP_ACCESS_TOKEN
2542
+ }
2543
+ );
2544
+ if (newJwtToken) {
2545
+ userCreds = await signInWithCustomToken(newJwtToken);
2546
+ if (userCreds.user)
2547
+ setIsLoggedIn(true);
2548
+ }
2549
+ } else if (!isRawModeSupported || import_is_ci4.default) {
2550
+ onFailure(
2551
+ "ToDesktop environment variables not found. When automating builds with your CI provider, you need to specify environment variables for TODESKTOP_EMAIL and TODESKTOP_ACCESS_TOKEN."
2552
+ );
2553
+ }
2554
+ } catch (err) {
2555
+ onFailure("Log in with environment variables failed.", err);
2556
+ }
2557
+ try {
2558
+ if (!userCreds && jwtToken) {
2559
+ userCreds = await signInWithCustomToken(jwtToken);
2560
+ if (userCreds.user)
2561
+ setIsLoggedIn(true);
2562
+ }
2563
+ } catch (err) {
2564
+ }
2565
+ try {
2566
+ if (!userCreds && accessToken && email) {
2567
+ const newJwtToken = await postToFirebaseFunction_default(
2568
+ "loginWithAccessToken",
2569
+ { email, accessToken }
2570
+ );
2571
+ if (newJwtToken) {
2572
+ userCreds = await signInWithCustomToken(newJwtToken);
2573
+ setAuthConfig(email, accessToken, newJwtToken);
2574
+ if (userCreds.user)
2575
+ setIsLoggedIn(true);
2576
+ }
2577
+ }
2578
+ } catch (err) {
2579
+ }
2580
+ setEffectDone(true);
2581
+ }
2582
+ isAccessTokenValid();
2583
+ }, []);
2584
+ if (error && isInteractive) {
2585
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ErrorDisplay_default, { error });
2586
+ }
2587
+ if (!isEffectDone) {
2588
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(LoadingText_default, {});
2589
+ }
2590
+ if (!isLoggedIn && isInteractive) {
2591
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Login_default, { setIsLoggedIn });
2592
+ }
2593
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, { children });
2594
+ };
2595
+ LoginHOC.propTypes = {
2596
+ children: import_prop_types11.default.object,
2597
+ isInteractive: import_prop_types11.default.bool
2598
+ };
2599
+ var LoginHOC_default = LoginHOC;
2600
+
2601
+ // src/components/ErrorBoundary.tsx
2602
+ var import_react11 = __toESM(require("react"));
2603
+ var Sentry2 = __toESM(require("@sentry/node"));
2604
+ var ErrorBoundary = class extends import_react11.default.Component {
2605
+ constructor(props) {
2606
+ super(props);
2607
+ this.state = { eventId: null, error: null };
2608
+ }
2609
+ static getDerivedStateFromError(error) {
2610
+ return { error };
2611
+ }
2612
+ componentDidCatch(error, errorInfo) {
2613
+ Sentry2.withScope((scope) => {
2614
+ scope.setExtras(errorInfo);
2615
+ const eventId = Sentry2.captureException(error);
2616
+ this.setState({ eventId });
2617
+ });
2618
+ }
2619
+ render() {
2620
+ if (this.state.error) {
2621
+ throw this.state.error;
2622
+ }
2623
+ return this.props.children;
2624
+ }
2625
+ };
2626
+ var ErrorBoundary_default = ErrorBoundary;
2627
+
2628
+ // src/components/OngoingBuildGuard.tsx
2629
+ var import_ink20 = require("ink");
2630
+ var import_ink_select_input = __toESM(require("ink-select-input"));
2631
+ var import_prop_types15 = __toESM(require("prop-types"));
2632
+ var import_react13 = require("react");
2633
+
2634
+ // src/components/CustomSelectInputIndicator.tsx
2635
+ var import_ink17 = require("ink");
2636
+ var import_prop_types12 = __toESM(require("prop-types"));
2637
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2638
+ var CustomSelectInputIndicator = ({ isSelected, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Box, { marginRight: 1, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Text, { children: isSelected ? process.platform === "win32" ? ">" : "\u276F" : " " }) });
2639
+ CustomSelectInputIndicator.propTypes = {
2640
+ isSelected: import_prop_types12.default.bool.isRequired
2641
+ };
2642
+ var CustomSelectInputIndicator_default = CustomSelectInputIndicator;
2643
+
2644
+ // src/components/CustomSelectInputItem.tsx
2645
+ var import_ink18 = require("ink");
2646
+ var import_prop_types13 = __toESM(require("prop-types"));
2647
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2648
+ var CustomSelectInputItem = ({ isSelected, label, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { bold: isSelected, color: isSelected ? void 0 : "gray", ...props, children: label });
2649
+ CustomSelectInputItem.propTypes = {
2650
+ isSelected: import_prop_types13.default.bool.isRequired,
2651
+ label: import_prop_types13.default.string.isRequired
2652
+ };
2653
+ var CustomSelectInputItem_default = CustomSelectInputItem;
2654
+
2655
+ // src/components/ViewBuild.tsx
2656
+ var import_ink19 = require("ink");
2657
+ var import_prop_types14 = __toESM(require("prop-types"));
2658
+ var import_react12 = require("react");
2659
+
2660
+ // src/utilities/getLatestBuildId.ts
2661
+ var getLatestBuildId_default = async ({ appId, userId }) => {
2662
+ logger_default.debug({ appId }, "getLatestBuildId");
2663
+ const appRef = firestore_default.doc(`users/${userId}/applications/${appId}`);
2664
+ const appSnapshot = await appRef.get();
2665
+ if (!appSnapshot.exists) {
2666
+ throw new Error(`Application with ID of ${appId} doesn't exist.`);
2667
+ }
2668
+ const buildsResult = await appRef.collection("builds").orderBy("createdAt", "desc").limit(1).get();
2669
+ if (buildsResult.empty) {
2670
+ return null;
2671
+ } else {
2672
+ return buildsResult.docs[0].id;
2673
+ }
2674
+ };
2675
+
2676
+ // src/utilities/findAppUserId.ts
2677
+ var getCollection = (collectionRef) => {
2678
+ return collectionRef.get().then((resp) => {
2679
+ const result = [];
2680
+ resp.forEach((doc) => result.push(doc.data()));
2681
+ return result;
2682
+ });
2683
+ };
2684
+ var acceptedUsersCollection = (id) => firestore_default.collection(`users/${id}/acceptedUsers`);
2685
+ var findAppUserId = async (appId) => {
2686
+ const { uid, email } = currentUser();
2687
+ const acceptedUsers = await getCollection(acceptedUsersCollection(uid));
2688
+ const primaryUser = { id: uid, label: email };
2689
+ const users = [
2690
+ primaryUser,
2691
+ ...acceptedUsers.map((user) => ({ id: user.id, label: user.email }))
2692
+ ];
2693
+ for (const user of users) {
2694
+ try {
2695
+ const doc = await firestore_default.doc(`users/${user.id}/applications/${appId}`).get();
2696
+ if (doc.exists)
2697
+ return user;
2698
+ } catch (err) {
2699
+ }
2700
+ }
2701
+ return primaryUser;
2702
+ };
2703
+ var findAppUserId_default = findAppUserId;
2704
+
2705
+ // src/components/ViewBuild.tsx
2706
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2707
+ var ViewBuild = ({ commandUsed, id, configPath }) => {
2708
+ const exit = useExit_default();
2709
+ const [
2710
+ {
2711
+ appId,
2712
+ arbitraryMessageComponent,
2713
+ build,
2714
+ error,
2715
+ hasBuildEverFailed,
2716
+ isLoading
2717
+ },
2718
+ setState
2719
+ ] = (0, import_react12.useState)({
2720
+ appId: null,
2721
+ arbitraryMessageComponent: null,
2722
+ build: null,
2723
+ error: null,
2724
+ hasBuildEverFailed: false,
2725
+ isLoading: true
2726
+ });
2727
+ const onError = (e) => {
2728
+ const error2 = e.response ? e.response.data : e;
2729
+ logForCI_default(error2);
2730
+ setState((prevState) => ({
2731
+ ...prevState,
2732
+ error: error2
2733
+ }));
2734
+ };
2735
+ (0, import_react12.useEffect)(() => {
2736
+ let firebaseUnsubscribe;
2737
+ async function viewBuild() {
2738
+ let config2;
2739
+ try {
2740
+ config2 = getProjectConfig(configPath).config;
2741
+ } catch (e) {
2742
+ setState((previousState) => ({ ...previousState, error: e }));
2743
+ return;
2744
+ }
2745
+ const { id: userId } = await findAppUserId_default(config2.id);
2746
+ const subscribe = (buildId) => {
2747
+ subscribeToBuild_default({
2748
+ appId: config2.id,
2749
+ buildId,
2750
+ onDataReceived: (data) => {
2751
+ if (!data) {
2752
+ onError(
2753
+ new Error(
2754
+ `No such build exists (${buildId}). Please contact us if this is an error`
2755
+ )
2756
+ );
2757
+ }
2758
+ setState((prevState) => ({
2759
+ ...prevState,
2760
+ appId: config2.id,
2761
+ build: data,
2762
+ isLoading: false
2763
+ }));
2764
+ },
2765
+ userId
2766
+ }).then((firebaseUnsubscribeResult) => {
2767
+ firebaseUnsubscribe = firebaseUnsubscribeResult;
2768
+ }).catch(onError);
2769
+ };
2770
+ if (id) {
2771
+ subscribe(id);
2772
+ } else {
2773
+ getLatestBuildId_default({ appId: config2.id, userId }).catch(onError).then((latestBuildId) => {
2774
+ if (!latestBuildId) {
2775
+ setState((previousState) => ({
2776
+ ...previousState,
2777
+ arbitraryMessageComponent: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { children: "There are no builds yet" })
2778
+ }));
2779
+ return;
2780
+ }
2781
+ return subscribe(latestBuildId);
2782
+ });
2783
+ }
2784
+ }
2785
+ viewBuild();
2786
+ return () => {
2787
+ if (firebaseUnsubscribe) {
2788
+ firebaseUnsubscribe();
2789
+ }
2790
+ };
2791
+ }, [id]);
2792
+ (0, import_react12.useEffect)(() => {
2793
+ if (hasBuildEverFailed && shouldExitOnBuildFailure_default(build)) {
2794
+ setTimeout(() => exit(new Error("Build has failed")), 10);
2795
+ }
2796
+ }, [build, exit, hasBuildEverFailed]);
2797
+ (0, import_react12.useEffect)(() => {
2798
+ if (arbitraryMessageComponent) {
2799
+ setTimeout(() => exit(new Error("Validation failed")), 10);
2800
+ }
2801
+ }, [arbitraryMessageComponent, exit]);
2802
+ if (error) {
2803
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ErrorDisplay_default, { commandUsed, error });
2804
+ }
2805
+ if (arbitraryMessageComponent) {
2806
+ return arbitraryMessageComponent;
2807
+ }
2808
+ if (isLoading) {
2809
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LoadingText_default, {});
2810
+ }
2811
+ const onBuildFailure = () => {
2812
+ if (hasBuildEverFailed) {
2813
+ return;
2814
+ }
2815
+ setState((prevState) => ({
2816
+ ...prevState,
2817
+ hasBuildEverFailed: true
2818
+ }));
2819
+ };
2820
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2821
+ MainLayout_default,
2822
+ {
2823
+ appId,
2824
+ appName: build.appName,
2825
+ appVersion: build.appVersion,
2826
+ build,
2827
+ commandUsed,
2828
+ hasBuildEverFailed,
2829
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BuildProgress_default, { build, onBuildFailure })
2830
+ }
2831
+ );
2832
+ };
2833
+ ViewBuild.propTypes = {
2834
+ id: (props, propName, componentName) => {
2835
+ if ([props.id, props.shouldViewLatest].filter(Boolean).length !== 1) {
2836
+ return new Error(
2837
+ `Exactly one of 'id' and 'shouldViewLatest' must be specified in '${componentName}'`
2838
+ );
2839
+ }
2840
+ const type = typeof props.id;
2841
+ if (!["string", "undefined"].includes(type)) {
2842
+ return new Error(
2843
+ `'id' is a '${type}', not a string, in '${componentName}'.`
2844
+ );
2845
+ }
2846
+ },
2847
+ commandUsed: import_prop_types14.default.string.isRequired,
2848
+ shouldViewLatest: (props, propName, componentName) => {
2849
+ if ([props.id, props.shouldViewLatest].filter(Boolean).length !== 1) {
2850
+ return new Error(
2851
+ `Exactly one of 'id' and 'shouldViewLatest' must be specified in '${componentName}'`
2852
+ );
2853
+ }
2854
+ const type = typeof props.shouldViewLatest;
2855
+ if (!["boolean", "undefined"].includes(type)) {
2856
+ return new Error(
2857
+ `'shouldViewLatest' is a '${type}', not a boolean, in '${componentName}'.`
2858
+ );
2859
+ }
2860
+ },
2861
+ configPath: import_prop_types14.default.string
2862
+ };
2863
+ var ViewBuild_default = ViewBuild;
2864
+
2865
+ // src/components/OngoingBuildGuard.tsx
2866
+ var import_is_ci5 = __toESM(require("is-ci"));
2867
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2868
+ var OngoingBuildGuard = ({ children, commandUsed, configPath }) => {
2869
+ const { isRawModeSupported } = (0, import_ink20.useStdin)();
2870
+ const onInput = useInput_default();
2871
+ const exit = useExit_default();
2872
+ const [{ appId, builds, error, isLoading, itemChosen }, setState] = (0, import_react13.useState)({
2873
+ appId: null,
2874
+ builds: [],
2875
+ error: null,
2876
+ isLoading: true,
2877
+ itemChosen: null
2878
+ });
2879
+ (0, import_react13.useEffect)(() => {
2880
+ if (!isLoading) {
2881
+ return;
2882
+ }
2883
+ (async () => {
2884
+ try {
2885
+ const applicationId = getProjectConfig(configPath).config.id;
2886
+ const { id } = await findAppUserId_default(applicationId);
2887
+ const buildsResult = await firestore_default.doc(`users/${id}/applications/${applicationId}`).collection("builds").orderBy("createdAt", "desc").limit(10).get();
2888
+ const stateUpdates = {
2889
+ appId: applicationId,
2890
+ isLoading: false,
2891
+ builds: []
2892
+ };
2893
+ if (!buildsResult.empty) {
2894
+ const latestBuild = buildsResult.docs[0].data();
2895
+ const isRunning = isBuildRunning(latestBuild) && latestBuild.status !== "preparation";
2896
+ logger_default.debug(
2897
+ { isRunning },
2898
+ "OngoingBuildGuard component: got latest build"
2899
+ );
2900
+ stateUpdates.builds = buildsResult.docs.map((doc) => doc.data()).filter((build) => {
2901
+ if (isBuildRunning(build) && build.status !== "preparation") {
2902
+ return true;
2903
+ }
2904
+ return false;
2905
+ });
2906
+ }
2907
+ setState((previousState) => ({
2908
+ ...previousState,
2909
+ ...stateUpdates
2910
+ }));
2911
+ } catch (e) {
2912
+ setState((previousState) => ({
2913
+ ...previousState,
2914
+ error: e
2915
+ }));
2916
+ }
2917
+ })();
2918
+ }, [builds, itemChosen, isLoading, isRawModeSupported]);
2919
+ if (isRawModeSupported) {
2920
+ onInput(() => {
2921
+ });
2922
+ }
2923
+ if (error) {
2924
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ErrorDisplay_default, { commandUsed, error });
2925
+ }
2926
+ if (isLoading) {
2927
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { children: "..." });
2928
+ }
2929
+ if (itemChosen) {
2930
+ logger_default.debug({ itemChosen }, "OngoingBuildGuard component: item chosen");
2931
+ const build = builds.find(Boolean);
2932
+ if (itemChosen.value === "view") {
2933
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ViewBuild_default, { commandUsed, id: build.id });
2934
+ } else if (itemChosen.value === "cancel") {
2935
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CancelBuild_default, { appId, commandUsed, id: build.id, children });
2936
+ } else if (itemChosen.value === "concurrent") {
2937
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
2938
+ } else {
2939
+ setTimeout(exit, 10);
2940
+ }
2941
+ }
2942
+ if (builds.length && import_is_ci5.default === false && isRawModeSupported) {
2943
+ const latestBuild = builds.find(Boolean);
2944
+ const multiple = builds.length > 1;
2945
+ const items = [
2946
+ {
2947
+ label: `View ${multiple ? "latest" : "it"}`,
2948
+ value: "view"
2949
+ }
2950
+ ];
2951
+ if (builds.some((build) => isBuildCancellable(build))) {
2952
+ items.push({
2953
+ label: `Cancel ${multiple ? "latest" : "it"} and start a new build`,
2954
+ value: "cancel"
2955
+ });
2956
+ items.push({
2957
+ label: "Start a concurrent build",
2958
+ value: "concurrent"
2959
+ });
2960
+ }
2961
+ items.push({
2962
+ label: "Exit",
2963
+ value: "exit"
2964
+ });
2965
+ const handleSelect = (itemChosen2) => setState((previousState) => ({ ...previousState, itemChosen: itemChosen2 }));
2966
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
2967
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_ink20.Box, { marginBottom: 1, children: [
2968
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { bold: true, children: multiple ? "There are ongoing builds " : "There is an ongoing build " }),
2969
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_ink20.Text, { children: [
2970
+ "(",
2971
+ latestBuild.appName,
2972
+ latestBuild.appVersion ? ` v${latestBuild.appVersion}` : "",
2973
+ ")"
2974
+ ] })
2975
+ ] }),
2976
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2977
+ import_ink_select_input.default,
2978
+ {
2979
+ indicatorComponent: CustomSelectInputIndicator_default,
2980
+ itemComponent: CustomSelectInputItem_default,
2981
+ items,
2982
+ onSelect: handleSelect
2983
+ }
2984
+ )
2985
+ ] });
2986
+ } else {
2987
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
2988
+ }
2989
+ };
2990
+ OngoingBuildGuard.propTypes = {
2991
+ commandUsed: import_prop_types15.default.string.isRequired,
2992
+ children: import_prop_types15.default.oneOfType([import_prop_types15.default.array, import_prop_types15.default.object]),
2993
+ configPath: import_prop_types15.default.string
2994
+ };
2995
+ var OngoingBuildGuard_default = OngoingBuildGuard;
2996
+
2997
+ // src/utilities/useAnalytics.ts
2998
+ var import_react14 = require("react");
2999
+ var useAnalyticsCommand = (command, flags = {}, properties = {}) => {
3000
+ const [hasAttemptedTracking, setTrackingAttempt] = (0, import_react14.useState)(false);
3001
+ logger_default.info({ command, flags, properties }, "useAnalyticsCommand");
3002
+ try {
3003
+ if (flags.projectPath) {
3004
+ properties = {
3005
+ ...properties,
3006
+ ...getProjectConfig()
3007
+ };
3008
+ }
3009
+ } catch (err) {
3010
+ }
3011
+ (0, import_react14.useEffect)(() => {
3012
+ const authUnsubscribe = onUserAuth(async (user) => {
3013
+ if (user.uid) {
3014
+ identify(
3015
+ user.uid,
3016
+ {
3017
+ email: user.email,
3018
+ displayName: user.displayName
3019
+ },
3020
+ () => {
3021
+ track(
3022
+ ANALYTICS_EVENT.CLI_COMMAND,
3023
+ {
3024
+ ...properties,
3025
+ flags,
3026
+ command
3027
+ },
3028
+ () => setTrackingAttempt(true)
3029
+ );
3030
+ authUnsubscribe && authUnsubscribe();
3031
+ }
3032
+ );
3033
+ }
3034
+ });
3035
+ setTimeout(() => setTrackingAttempt(true), 5e3);
3036
+ }, []);
3037
+ return { hasAttemptedTracking };
3038
+ };
3039
+
3040
+ // src/commands/BuildCommand.tsx
3041
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3042
+ var BuildCommand = ({
3043
+ shouldCodeSign = true,
3044
+ configPath = null
3045
+ }) => {
3046
+ checkIfReactIsUsable_default();
3047
+ useAnalyticsCommand("build", {
3048
+ codeSign: shouldCodeSign,
3049
+ config: configPath
3050
+ });
3051
+ const commandUsed = "todesktop build";
3052
+ 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)(
3053
+ Build_default,
3054
+ {
3055
+ commandUsed,
3056
+ shouldCodeSign,
3057
+ configPath
3058
+ }
3059
+ ) }) }) });
3060
+ };
3061
+ var BuildCommand_default = BuildCommand;
3062
+
3063
+ // src/components/ViewBuilds.tsx
3064
+ var import_ink26 = require("ink");
3065
+ var import_prop_types21 = __toESM(require("prop-types"));
3066
+ var import_react16 = require("react");
3067
+
3068
+ // src/components/Table.tsx
3069
+ var import_ink24 = require("ink");
3070
+ var import_prop_types20 = __toESM(require("prop-types"));
3071
+
3072
+ // src/components/TableEnd.tsx
3073
+ var import_ink21 = require("ink");
3074
+ var import_prop_types16 = __toESM(require("prop-types"));
3075
+ var import_jsx_runtime23 = require("react/jsx-runtime");
3076
+ var TableEnd = ({ keyDetails, ...props }) => {
3077
+ let content = "\u2514";
3078
+ content += Object.values(keyDetails).map(({ width }) => "\u2500".repeat(width + 2)).join("\u2534");
3079
+ content += "\u2518";
3080
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink21.Box, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink21.Text, { children: content }) });
3081
+ };
3082
+ TableEnd.propTypes = {
3083
+ keyDetails: import_prop_types16.default.object.isRequired
3084
+ };
3085
+ var TableEnd_default = TableEnd;
3086
+
3087
+ // src/components/TableHead.tsx
3088
+ var import_ink22 = require("ink");
3089
+ var import_prop_types17 = __toESM(require("prop-types"));
3090
+ var import_react15 = require("react");
3091
+ var import_jsx_runtime24 = require("react/jsx-runtime");
3092
+ var TableHead = ({ keyDetails, ...props }) => {
3093
+ let topLine = "\u250C";
3094
+ topLine += Object.values(keyDetails).map(({ width }) => "\u2500".repeat(width + 2)).join("\u252C");
3095
+ topLine += "\u2510";
3096
+ const contentLineElements = Object.values(keyDetails).map(
3097
+ ({ key, width }, index) => {
3098
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react15.Fragment, { children: [
3099
+ index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { children: " \u2502 " }) : null,
3100
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Box, { width, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { bold: true, children: key }) })
3101
+ ] }, key);
3102
+ }
3103
+ );
3104
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink22.Box, { flexDirection: "column", ...props, children: [
3105
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { children: topLine }) }),
3106
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink22.Box, { children: [
3107
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { children: "\u2502 " }),
3108
+ contentLineElements,
3109
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink22.Text, { children: " \u2502" })
3110
+ ] }) })
3111
+ ] });
3112
+ };
3113
+ TableHead.propTypes = {
3114
+ bottomLinePrefix: import_prop_types17.default.string,
3115
+ keyDetails: import_prop_types17.default.object.isRequired
3116
+ };
3117
+ var TableHead_default = TableHead;
3118
+
3119
+ // src/components/TableBody.tsx
3120
+ var import_prop_types19 = __toESM(require("prop-types"));
3121
+
3122
+ // src/components/TableRow.tsx
3123
+ var import_ink23 = require("ink");
3124
+ var import_prop_types18 = __toESM(require("prop-types"));
3125
+ var import_jsx_runtime25 = require("react/jsx-runtime");
3126
+ var TableRow = ({
3127
+ data,
3128
+ getCellTextProps = ({ props }) => props,
3129
+ keyDetails,
3130
+ textProps = {}
3131
+ }) => {
3132
+ let topLine = "\u251C";
3133
+ topLine += Object.values(keyDetails).map(({ width }) => "\u2500".repeat(width + 2)).join("\u253C");
3134
+ topLine += "\u2524";
3135
+ const content = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
3136
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Text, { children: "\u2502 " }),
3137
+ Object.entries(data).map(([key, value], index) => {
3138
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_ink23.Box, { children: [
3139
+ index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Text, { children: " \u2502 " }) : null,
3140
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Box, { width: keyDetails[key].width, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3141
+ import_ink23.Text,
3142
+ {
3143
+ ...getCellTextProps({
3144
+ key,
3145
+ props: textProps,
3146
+ rowData: data,
3147
+ value
3148
+ }),
3149
+ children: value
3150
+ }
3151
+ ) })
3152
+ ] }, key);
3153
+ }),
3154
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Text, { children: " \u2502" })
3155
+ ] });
3156
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_ink23.Box, { flexDirection: "column", children: [
3157
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Text, { children: topLine }),
3158
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink23.Box, { children: content })
3159
+ ] }) });
3160
+ };
3161
+ TableRow.propTypes = {
3162
+ data: import_prop_types18.default.object.isRequired,
3163
+ getCellTextProps: import_prop_types18.default.func,
3164
+ keyDetails: import_prop_types18.default.object.isRequired,
3165
+ textProps: import_prop_types18.default.object
3166
+ };
3167
+ var TableRow_default = TableRow;
3168
+
3169
+ // src/components/TableBody.tsx
3170
+ var import_jsx_runtime26 = require("react/jsx-runtime");
3171
+ var TableBody = ({ data, getCellTextProps, keyDetails }) => {
3172
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_jsx_runtime26.Fragment, { children: data.map((rowData, index) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3173
+ TableRow_default,
3174
+ {
3175
+ data: rowData,
3176
+ getCellTextProps,
3177
+ keyDetails
3178
+ },
3179
+ index
3180
+ )) });
3181
+ };
3182
+ TableBody.propTypes = {
3183
+ data: (props, propName, componentName) => {
3184
+ const type = typeof props.data;
3185
+ if (Array.isArray("array")) {
3186
+ return new Error(
3187
+ `'data' prop must be an array ('${type}' given) in ${componentName} component`
3188
+ );
3189
+ }
3190
+ if (props.data.length === 0) {
3191
+ return new Error(
3192
+ `'data' prop must not be empty, in ${componentName} component`
3193
+ );
3194
+ }
3195
+ },
3196
+ getCellTextProps: import_prop_types19.default.func,
3197
+ keyDetails: import_prop_types19.default.object.isRequired
3198
+ };
3199
+ var TableBody_default = TableBody;
3200
+
3201
+ // src/utilities/getKeyDetails.ts
3202
+ var getKeyDetails_default = (data) => {
3203
+ const result = {};
3204
+ Object.keys(data[0]).forEach((key) => {
3205
+ result[key] = {
3206
+ key,
3207
+ width: Math.max(
3208
+ key.length,
3209
+ data.map((item) => item[key].toString().length).sort((a, b) => b - a)[0]
3210
+ )
3211
+ };
3212
+ });
3213
+ return result;
3214
+ };
3215
+
3216
+ // src/components/Table.tsx
3217
+ var import_jsx_runtime27 = require("react/jsx-runtime");
3218
+ var Table = ({ data, getCellTextProps }) => {
3219
+ const keyDetails = getKeyDetails_default(data);
3220
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_ink24.Box, { flexDirection: "column", children: [
3221
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(TableHead_default, { keyDetails }),
3222
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3223
+ TableBody_default,
3224
+ {
3225
+ data,
3226
+ getCellTextProps,
3227
+ keyDetails
3228
+ }
3229
+ ),
3230
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(TableEnd_default, { keyDetails })
3231
+ ] });
3232
+ };
3233
+ Table.propTypes = {
3234
+ data: (props, propName, componentName) => {
3235
+ const type = typeof props.data;
3236
+ if (Array.isArray("array")) {
3237
+ return new Error(
3238
+ `'data' prop must be an array ('${type}' given) in ${componentName} component`
3239
+ );
3240
+ }
3241
+ if (props.data.length === 0) {
3242
+ return new Error(
3243
+ `'data' prop must not be empty, in ${componentName} component`
3244
+ );
3245
+ }
3246
+ },
3247
+ getCellTextProps: import_prop_types20.default.func
3248
+ };
3249
+ var Table_default = Table;
3250
+
3251
+ // src/utilities/capitalize.ts
3252
+ var capitalize_default = (input) => {
3253
+ if (typeof input !== "string") {
3254
+ return "";
3255
+ }
3256
+ return input.charAt(0).toUpperCase() + input.slice(1);
3257
+ };
3258
+
3259
+ // src/utilities/getBuilds.ts
3260
+ var getBuilds_default = async ({
3261
+ addWhereClauses = (query) => query,
3262
+ userId,
3263
+ appId,
3264
+ limit = 5,
3265
+ startAfter = null
3266
+ }) => {
3267
+ logger_default.debug({ appId, limit, startAfter }, "getBuilds");
3268
+ const appRef = firestore_default.doc(`users/${userId}/applications/${appId}`);
3269
+ const appSnapshot = await appRef.get();
3270
+ if (!appSnapshot.exists) {
3271
+ throw new Error(`Application with ID of ${appId} doesn't exist.`);
3272
+ }
3273
+ let query = addWhereClauses(appRef.collection("builds")).orderBy(
3274
+ "createdAt",
3275
+ "desc"
3276
+ );
3277
+ if (startAfter) {
3278
+ query = query.startAfter(startAfter);
3279
+ }
3280
+ const buildsResult = await query.limit(limit).get();
3281
+ if (buildsResult.empty) {
3282
+ return [];
3283
+ }
3284
+ return buildsResult.docs.map((doc) => doc.data());
3285
+ };
3286
+
3287
+ // src/utilities/getRelativeDateFromDateString.ts
3288
+ var dateFns = __toESM(require("date-fns"));
3289
+ var getRelativeDateFromDateString_default = (input) => dateFns.formatDistance(new Date(input), new Date()) + " ago";
3290
+
3291
+ // src/components/SyntaxHighlight.tsx
3292
+ var import_chalk2 = __toESM(require("chalk"));
3293
+ var React3 = __toESM(require("react"));
3294
+ var import_ink25 = require("ink");
3295
+ var import_util2 = __toESM(require("util"));
3296
+ var import_jsx_runtime28 = require("react/jsx-runtime");
3297
+ var supportsColor = import_chalk2.default.stderr.supportsColor;
3298
+ var SyntaxHighlight = ({
3299
+ object,
3300
+ colors = supportsColor == null ? void 0 : supportsColor.hasBasic
3301
+ }) => {
3302
+ const { stdout } = (0, import_ink25.useStdout)();
3303
+ const highlightedCode = React3.useMemo(() => {
3304
+ return stdout.isTTY ? import_util2.default.inspect(object, { colors, depth: 6 }) : JSON.stringify(object, null, 2);
3305
+ }, [colors, object, stdout.isTTY]);
3306
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ink25.Text, { wrap: "end", children: highlightedCode });
3307
+ };
3308
+ var SyntaxHighlight_default = SyntaxHighlight;
3309
+
3310
+ // src/components/ViewBuilds.tsx
3311
+ var import_jsx_runtime29 = require("react/jsx-runtime");
3312
+ var ViewBuilds = ({
3313
+ commandUsed,
3314
+ configPath,
3315
+ count,
3316
+ format,
3317
+ exit: shouldExitAfterViewingBuilds = false
3318
+ }) => {
3319
+ const exit = useExit_default();
3320
+ const [
3321
+ {
3322
+ builds,
3323
+ error,
3324
+ hasMoreToLoad,
3325
+ isInitialLoadComplete,
3326
+ isLoading,
3327
+ projectConfig,
3328
+ startAfter,
3329
+ user
3330
+ },
3331
+ setState
3332
+ ] = (0, import_react16.useState)({
3333
+ builds: [],
3334
+ error: null,
3335
+ hasMoreToLoad: true,
3336
+ isInitialLoadComplete: false,
3337
+ isLoading: false,
3338
+ projectConfig: null,
3339
+ startAfter: null,
3340
+ user: null
3341
+ });
3342
+ const onInput = useInput_default();
3343
+ onInput((input, key) => {
3344
+ if (key.escape) {
3345
+ exit();
3346
+ }
3347
+ if (input === " " || key.downArrow || key.pageDown || key.return) {
3348
+ if (!isInitialLoadComplete || !hasMoreToLoad) {
3349
+ return;
3350
+ }
3351
+ setState((previousState) => ({
3352
+ ...previousState,
3353
+ startAfter: builds[builds.length - 1].createdAt
3354
+ }));
3355
+ }
3356
+ });
3357
+ (0, import_react16.useEffect)(() => {
3358
+ async function viewBuilds() {
3359
+ if (error || isLoading || isInitialLoadComplete && (!hasMoreToLoad || !startAfter)) {
3360
+ return;
3361
+ }
3362
+ if (!["table", "json"].includes(format)) {
3363
+ setState((previousState) => ({
3364
+ ...previousState,
3365
+ error: new Error(
3366
+ `Invalid output format "${format}". Valid formats are "table" and "json".`
3367
+ )
3368
+ }));
3369
+ return;
3370
+ }
3371
+ setState((previousState) => ({
3372
+ ...previousState,
3373
+ isLoading: true
3374
+ }));
3375
+ let config2;
3376
+ try {
3377
+ config2 = projectConfig || getProjectConfig(configPath).config;
3378
+ } catch (e) {
3379
+ setState((previousState) => ({ ...previousState, error: e }));
3380
+ return;
3381
+ }
3382
+ const pageSize = count || 5;
3383
+ const user2 = await findAppUserId_default(config2.id);
3384
+ getBuilds_default({
3385
+ appId: config2.id,
3386
+ limit: pageSize + 1,
3387
+ startAfter,
3388
+ userId: user2.id
3389
+ }).then((buildsResult) => {
3390
+ setState((previousState) => {
3391
+ const stateUpdates = {
3392
+ builds: [],
3393
+ hasMoreToLoad: buildsResult.length > pageSize,
3394
+ isInitialLoadComplete: true,
3395
+ isLoading: false,
3396
+ projectConfig: config2,
3397
+ startAfter: null,
3398
+ user: user2
3399
+ };
3400
+ if (buildsResult.length) {
3401
+ stateUpdates.builds = [
3402
+ ...previousState.builds,
3403
+ ...buildsResult.slice(0, pageSize)
3404
+ ];
3405
+ }
3406
+ return {
3407
+ ...previousState,
3408
+ ...stateUpdates
3409
+ };
3410
+ });
3411
+ }).catch((e) => {
3412
+ logForCI_default(e);
3413
+ setState((previousState) => ({
3414
+ ...previousState,
3415
+ error: e,
3416
+ isLoading: false
3417
+ }));
3418
+ });
3419
+ }
3420
+ viewBuilds();
3421
+ }, [
3422
+ error,
3423
+ hasMoreToLoad,
3424
+ isInitialLoadComplete,
3425
+ isLoading,
3426
+ projectConfig,
3427
+ startAfter
3428
+ ]);
3429
+ (0, import_react16.useEffect)(() => {
3430
+ if (!hasMoreToLoad) {
3431
+ setTimeout(exit, 10);
3432
+ }
3433
+ }, [exit, hasMoreToLoad]);
3434
+ (0, import_react16.useEffect)(() => {
3435
+ if (!isLoading && isInitialLoadComplete && shouldExitAfterViewingBuilds) {
3436
+ setTimeout(exit, 10);
3437
+ }
3438
+ }, [exit, isLoading, shouldExitAfterViewingBuilds, isInitialLoadComplete]);
3439
+ if (error) {
3440
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ErrorDisplay_default, { commandUsed, error });
3441
+ }
3442
+ if (!isInitialLoadComplete) {
3443
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(LoadingText_default, {});
3444
+ }
3445
+ if (builds.length) {
3446
+ const formatData = (builds2) => builds2.map((build) => ({
3447
+ ID: build.id,
3448
+ Version: build.appVersion || "n/a",
3449
+ "Creation date": getRelativeDateFromDateString_default(build.createdAt),
3450
+ Status: capitalize_default(build.status),
3451
+ "Release date": build.releasedAt ? getRelativeDateFromDateString_default(build.releasedAt) : "Unreleased",
3452
+ Owner: user ? user.label : "unknown"
3453
+ }));
3454
+ const getCellTextProps = ({ key, props, value }) => {
3455
+ const result = { ...props };
3456
+ if (key === "Status") {
3457
+ const status = value.toLowerCase();
3458
+ if (status === "succeeded") {
3459
+ result.color = "green";
3460
+ } else if (status === "failed") {
3461
+ result.color = "red";
3462
+ } else if (status === "cancelled") {
3463
+ result.dimColor = true;
3464
+ }
3465
+ } else if (key === "Version" && value.toLowerCase() === "n/a") {
3466
+ result.dimColor = true;
3467
+ } else if (key === "Release date" && value.toLowerCase() === "unreleased") {
3468
+ result.dimColor = true;
3469
+ }
3470
+ return result;
3471
+ };
3472
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
3473
+ format === "table" ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3474
+ Table_default,
3475
+ {
3476
+ getCellTextProps,
3477
+ data: formatData(builds)
3478
+ }
3479
+ ) : null,
3480
+ format === "json" ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(SyntaxHighlight_default, { object: removeAppBuilderLibConfig(builds) }) : null,
3481
+ !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
3482
+ ] });
3483
+ } else {
3484
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ink26.Text, { children: "There are no builds yet" });
3485
+ }
3486
+ };
3487
+ var removeAppBuilderLibConfig = (builds) => {
3488
+ return builds.map((build) => {
3489
+ if (build.mac && build.mac.appBuilderLibConfig) {
3490
+ delete build.mac.appBuilderLibConfig;
3491
+ }
3492
+ if (build.windows && build.windows.appBuilderLibConfig) {
3493
+ delete build.windows.appBuilderLibConfig;
3494
+ }
3495
+ if (build.linux && build.linux.appBuilderLibConfig) {
3496
+ delete build.linux.appBuilderLibConfig;
3497
+ }
3498
+ return build;
3499
+ });
3500
+ };
3501
+ ViewBuilds.propTypes = {
3502
+ commandUsed: import_prop_types21.default.string.isRequired,
3503
+ configPath: import_prop_types21.default.string,
3504
+ count: import_prop_types21.default.number,
3505
+ format: import_prop_types21.default.string,
3506
+ exit: import_prop_types21.default.bool
3507
+ };
3508
+ var ViewBuilds_default = ViewBuilds;
3509
+
3510
+ // src/commands/BuildsCommand.tsx
3511
+ var import_jsx_runtime30 = require("react/jsx-runtime");
3512
+ var BuildsCommand = ({
3513
+ id,
3514
+ shouldViewLatest,
3515
+ configPath = null,
3516
+ count,
3517
+ format = "table",
3518
+ exit
3519
+ }) => {
3520
+ checkIfReactIsUsable_default();
3521
+ useAnalyticsCommand("builds", {
3522
+ id,
3523
+ shouldViewLatest,
3524
+ configPath,
3525
+ count,
3526
+ format,
3527
+ exit
3528
+ });
3529
+ const getContents = () => {
3530
+ let commandUsed = "todesktop builds";
3531
+ if (id) {
3532
+ commandUsed += " <id>";
3533
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ViewBuild_default, { commandUsed, id, configPath });
3534
+ } else if (shouldViewLatest) {
3535
+ commandUsed += " --latest";
3536
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3537
+ ViewBuild_default,
3538
+ {
3539
+ commandUsed,
3540
+ shouldViewLatest: true,
3541
+ configPath
3542
+ }
3543
+ );
3544
+ } else {
3545
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3546
+ ViewBuilds_default,
3547
+ {
3548
+ commandUsed,
3549
+ configPath,
3550
+ count,
3551
+ format,
3552
+ exit
3553
+ }
3554
+ );
3555
+ }
3556
+ };
3557
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(LoginHOC_default, { children: getContents() }) });
3558
+ };
3559
+ var BuildsCommand_default = BuildsCommand;
3560
+
3561
+ // src/commands/LogoutCommand.tsx
3562
+ var import_react17 = require("react");
3563
+ var import_ink27 = require("ink");
3564
+ var import_jsx_runtime31 = require("react/jsx-runtime");
3565
+ var Logout = () => {
3566
+ const exit = useExit_default();
3567
+ const { hasAttemptedTracking } = useAnalyticsCommand("logout");
3568
+ checkIfReactIsUsable_default();
3569
+ deleteAuthConfig();
3570
+ (0, import_react17.useEffect)(() => {
3571
+ if (hasAttemptedTracking) {
3572
+ exit();
3573
+ }
3574
+ }, [exit, hasAttemptedTracking]);
3575
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ink27.Text, { children: "Log out successful" });
3576
+ };
3577
+ var LogoutWrapper = () => {
3578
+ 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, {}) }) });
3579
+ };
3580
+ var LogoutCommand_default = LogoutWrapper;
3581
+
3582
+ // src/commands/ReleaseCommand.tsx
3583
+ var import_ink31 = require("ink");
3584
+
3585
+ // src/components/ReleaseBuild.tsx
3586
+ var import_ink28 = require("ink");
3587
+ var import_ink_link4 = __toESM(require("ink-link"));
3588
+ var import_ink_select_input2 = __toESM(require("ink-select-input"));
3589
+ var import_prop_types22 = __toESM(require("prop-types"));
3590
+ var import_react18 = require("react");
3591
+
3592
+ // src/utilities/getBuildById.ts
3593
+ var getBuildById_default = async ({ appId, buildId, userId }) => {
3594
+ logger_default.debug({ appId, buildId }, "getBuildById");
3595
+ const snapshot = await firestore_default.doc(`users/${userId}/applications/${appId}/builds/${buildId}`).get();
3596
+ if (!snapshot.exists) {
3597
+ return null;
3598
+ }
3599
+ return snapshot.data();
3600
+ };
3601
+
3602
+ // src/components/ReleaseBuild.tsx
3603
+ var import_jsx_runtime32 = require("react/jsx-runtime");
3604
+ var getValidationErrorMessageDisplay = (build, validationMessage) => {
3605
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink28.Box, { flexDirection: "column", children: [
3606
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink28.Text, { bold: true, color: "red", children: [
3607
+ "Can't release ",
3608
+ build.appName,
3609
+ " v",
3610
+ build.appVersion
3611
+ ] }),
3612
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Box, { marginBottom: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Text, { children: validationMessage }) }),
3613
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Text, { bold: true, children: "See web UI for more information: " }),
3614
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Text, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink_link4.default, { fallback: false, url: build.url, children: build.url }) })
3615
+ ] });
3616
+ };
3617
+ var ReleaseBuild = ({ commandUsed, id, shouldConfirm, configPath }) => {
3618
+ const exit = useExit_default();
3619
+ const [
3620
+ {
3621
+ appId,
3622
+ arbitraryMessageComponent,
3623
+ build,
3624
+ error,
3625
+ hasConfirmed,
3626
+ hasBeenValidatedSuccesfully,
3627
+ isComplete,
3628
+ isReleasing
3629
+ },
3630
+ setState
3631
+ ] = (0, import_react18.useState)({
3632
+ appId: null,
3633
+ build: null,
3634
+ error: null,
3635
+ hasConfirmed: false,
3636
+ hasBeenValidatedSuccesfully: false,
3637
+ isComplete: false,
3638
+ isReleasing: false,
3639
+ arbitraryMessageComponent: null
3640
+ });
3641
+ const onError = (e) => {
3642
+ const error2 = e.response ? e.response.data : e;
3643
+ logForCI_default(error2);
3644
+ setState((prevState) => ({
3645
+ ...prevState,
3646
+ error: error2
3647
+ }));
3648
+ };
3649
+ (0, import_react18.useEffect)(() => {
3650
+ async function releaseBuild() {
3651
+ if (build) {
3652
+ return;
3653
+ }
3654
+ const config2 = getProjectConfig(configPath).config;
3655
+ const appId2 = config2.id;
3656
+ const { id: userId } = await findAppUserId_default(appId2);
3657
+ const loadBuild = (buildId) => {
3658
+ getBuildById_default({ appId: appId2, buildId, userId }).then((buildResult) => {
3659
+ if (!buildResult) {
3660
+ throw new Error(
3661
+ `No such build ${buildId} for application ${appId2}`
3662
+ );
3663
+ }
3664
+ setState((previousState) => ({
3665
+ ...previousState,
3666
+ appId: appId2,
3667
+ build: buildResult
3668
+ }));
3669
+ }).catch(onError);
3670
+ };
3671
+ if (id) {
3672
+ loadBuild(id);
3673
+ } else {
3674
+ getLatestBuildId_default({ appId: appId2, userId }).catch(onError).then((buildId) => {
3675
+ if (!buildId) {
3676
+ setState((previousState) => ({
3677
+ ...previousState,
3678
+ arbitraryMessageComponent: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Text, { children: "There are no builds yet" })
3679
+ }));
3680
+ return;
3681
+ }
3682
+ return loadBuild(buildId);
3683
+ });
3684
+ }
3685
+ }
3686
+ releaseBuild();
3687
+ }, [build, id]);
3688
+ (0, import_react18.useEffect)(() => {
3689
+ if (!build || hasBeenValidatedSuccesfully || arbitraryMessageComponent) {
3690
+ return;
3691
+ }
3692
+ let validationMessage;
3693
+ if (build.releasedAt) {
3694
+ validationMessage = `It has already been released.`;
3695
+ } else if (build.status !== "succeeded") {
3696
+ validationMessage = `The build must have completed successfully. Actual build status: ${build.status}`;
3697
+ }
3698
+ if (validationMessage) {
3699
+ setState((previousState) => ({
3700
+ ...previousState,
3701
+ arbitraryMessageComponent: getValidationErrorMessageDisplay(
3702
+ build,
3703
+ validationMessage
3704
+ ),
3705
+ hasBeenValidatedSuccesfully: false
3706
+ }));
3707
+ } else {
3708
+ setState((previousState) => ({
3709
+ ...previousState,
3710
+ hasBeenValidatedSuccesfully: true
3711
+ }));
3712
+ }
3713
+ }, [arbitraryMessageComponent, build, hasBeenValidatedSuccesfully]);
3714
+ (0, import_react18.useEffect)(() => {
3715
+ if (!hasBeenValidatedSuccesfully || shouldConfirm && !hasConfirmed || isReleasing || isComplete) {
3716
+ return;
3717
+ }
3718
+ setState((previousState) => ({
3719
+ ...previousState,
3720
+ isReleasing: true
3721
+ }));
3722
+ getCallableFirebaseFunction_default("releaseBuild")({
3723
+ appId,
3724
+ buildId: build.id
3725
+ }).then(() => {
3726
+ logForCI_default("Released!");
3727
+ setState((previousState) => ({
3728
+ ...previousState,
3729
+ isReleasing: false,
3730
+ isComplete: true
3731
+ }));
3732
+ }).catch((e) => {
3733
+ if (["failed-precondition", "not-found"].includes(e.code)) {
3734
+ setState((previousState) => ({
3735
+ ...previousState,
3736
+ arbitraryMessageComponent: getValidationErrorMessageDisplay(
3737
+ build,
3738
+ e.message
3739
+ ),
3740
+ isReleasing: false
3741
+ }));
3742
+ } else {
3743
+ onError(new Error("Unexpected internal error while releasing build"));
3744
+ }
3745
+ });
3746
+ }, [
3747
+ appId,
3748
+ build,
3749
+ hasConfirmed,
3750
+ hasBeenValidatedSuccesfully,
3751
+ id,
3752
+ isComplete,
3753
+ isReleasing,
3754
+ shouldConfirm
3755
+ ]);
3756
+ (0, import_react18.useEffect)(() => {
3757
+ if (isComplete) {
3758
+ setTimeout(exit, 10);
3759
+ return;
3760
+ }
3761
+ if (arbitraryMessageComponent) {
3762
+ setTimeout(() => exit(new Error("Validation failed")), 10);
3763
+ }
3764
+ }, [arbitraryMessageComponent, exit, isComplete]);
3765
+ if (error) {
3766
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ErrorDisplay_default, { commandUsed, error });
3767
+ }
3768
+ if (arbitraryMessageComponent) {
3769
+ return arbitraryMessageComponent;
3770
+ }
3771
+ if (hasBeenValidatedSuccesfully && shouldConfirm && !hasConfirmed) {
3772
+ const items = [
3773
+ {
3774
+ label: "Yes",
3775
+ value: "yes"
3776
+ },
3777
+ {
3778
+ label: "No",
3779
+ value: "no"
3780
+ }
3781
+ ];
3782
+ const onSubmit = (item) => {
3783
+ if (item.value === "no") {
3784
+ setTimeout(exit, 10);
3785
+ return;
3786
+ }
3787
+ setState((previousState) => ({
3788
+ ...previousState,
3789
+ hasConfirmed: true
3790
+ }));
3791
+ };
3792
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink28.Box, { flexDirection: "column", children: [
3793
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink28.Box, { children: [
3794
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink28.Text, { children: [
3795
+ "This will release build ",
3796
+ build.id,
3797
+ " as "
3798
+ ] }),
3799
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ink28.Text, { bold: true, children: [
3800
+ build.appName,
3801
+ " v",
3802
+ build.appVersion
3803
+ ] }),
3804
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Text, { children: ", are you sure?" })
3805
+ ] }),
3806
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Box, { marginBottom: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3807
+ import_ink_select_input2.default,
3808
+ {
3809
+ indicatorComponent: CustomSelectInputIndicator_default,
3810
+ initialIndex: 1,
3811
+ itemComponent: CustomSelectInputItem_default,
3812
+ items,
3813
+ onSelect: onSubmit
3814
+ }
3815
+ ) }),
3816
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Text, { dimColor: true, children: "Your users will be auto-updated to this version. You can use --force to bypass this confirmation in future" })
3817
+ ] });
3818
+ }
3819
+ if (isReleasing) {
3820
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Text, { children: "Releasing..." });
3821
+ }
3822
+ if (isComplete) {
3823
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Text, { bold: true, color: "greenBright", children: "Released!" });
3824
+ }
3825
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ink28.Text, { children: "..." });
3826
+ };
3827
+ ReleaseBuild.propTypes = {
3828
+ id: (props, propName, componentName) => {
3829
+ if ([props.id, props.shouldReleaseLatest].filter(Boolean).length !== 1) {
3830
+ return new Error(
3831
+ `Exactly one of 'id' and 'shouldReleaseLatest' must be specified in '${componentName}'`
3832
+ );
3833
+ }
3834
+ const type = typeof props.id;
3835
+ if (!["string", "undefined"].includes(type)) {
3836
+ return new Error(
3837
+ `'id' is a '${type}', not a string, in '${componentName}'.`
3838
+ );
3839
+ }
3840
+ },
3841
+ commandUsed: import_prop_types22.default.string.isRequired,
3842
+ shouldReleaseLatest: (props, propName, componentName) => {
3843
+ if ([props.id, props.shouldReleaseLatest].filter(Boolean).length !== 1) {
3844
+ return new Error(
3845
+ `Exactly one of 'id' and 'shouldReleaseLatest' must be specified in '${componentName}'`
3846
+ );
3847
+ }
3848
+ const type = typeof props.shouldReleaseLatest;
3849
+ if (!["boolean", "undefined"].includes(type)) {
3850
+ return new Error(
3851
+ `'shouldReleaseLatest' is a '${type}', not a boolean, in '${componentName}'.`
3852
+ );
3853
+ }
3854
+ },
3855
+ shouldConfirm: import_prop_types22.default.bool.isRequired,
3856
+ configPath: import_prop_types22.default.string
3857
+ };
3858
+ var ReleaseBuild_default = ReleaseBuild;
3859
+
3860
+ // src/components/PickBuildForRelease.tsx
3861
+ var import_ink30 = require("ink");
3862
+ var import_prop_types24 = __toESM(require("prop-types"));
3863
+ var import_react19 = require("react");
3864
+
3865
+ // src/components/SelectTable.tsx
3866
+ var import_ink29 = require("ink");
3867
+ var import_ink_select_input3 = __toESM(require("ink-select-input"));
3868
+ var import_prop_types23 = __toESM(require("prop-types"));
3869
+ var import_jsx_runtime33 = require("react/jsx-runtime");
3870
+ var CustomIndicator = (props) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CustomSelectInputIndicator_default, { marginTop: 1, ...props });
3871
+ var SelectTable = ({ data, onSelect }) => {
3872
+ const keyDetails = getKeyDetails_default(data);
3873
+ const getSelectItems = () => {
3874
+ return data.map((rowData, index) => {
3875
+ return {
3876
+ index,
3877
+ key: index,
3878
+ value: rowData
3879
+ };
3880
+ });
3881
+ };
3882
+ const ItemComponent = ({
3883
+ label,
3884
+ index,
3885
+ isSelected
3886
+ }) => {
3887
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3888
+ TableRow_default,
3889
+ {
3890
+ data: data[index],
3891
+ keyDetails,
3892
+ textProps: { bold: isSelected, color: isSelected ? void 0 : "gray" }
3893
+ },
3894
+ index
3895
+ );
3896
+ };
3897
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ink29.Box, { flexDirection: "column", children: [
3898
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(TableHead_default, { keyDetails, marginLeft: 2 }),
3899
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3900
+ import_ink_select_input3.default,
3901
+ {
3902
+ indicatorComponent: CustomIndicator,
3903
+ itemComponent: ItemComponent,
3904
+ items: getSelectItems(),
3905
+ onSelect
3906
+ }
3907
+ ),
3908
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(TableEnd_default, { keyDetails, marginLeft: 2 })
3909
+ ] });
3910
+ };
3911
+ SelectTable.propTypes = {
3912
+ data: (props, propName, componentName) => {
3913
+ const type = typeof props.data;
3914
+ if (Array.isArray("array")) {
3915
+ return new Error(
3916
+ `'data' prop must be an array ('${type}' given) in ${componentName} component`
3917
+ );
3918
+ }
3919
+ if (props.data.length === 0) {
3920
+ return new Error(
3921
+ `'data' prop must not be empty, in ${componentName} component`
3922
+ );
3923
+ }
3924
+ },
3925
+ onSelect: import_prop_types23.default.func
3926
+ };
3927
+ var SelectTable_default = SelectTable;
3928
+
3929
+ // src/components/PickBuildForRelease.tsx
3930
+ var import_jsx_runtime34 = require("react/jsx-runtime");
3931
+ var PickBuildForRelease = ({ commandUsed, shouldConfirm, configPath }) => {
3932
+ const exit = useExit_default();
3933
+ const [{ error, builds, chosenBuildId, isLoading, user }, setState] = (0, import_react19.useState)({
3934
+ error: null,
3935
+ builds: null,
3936
+ chosenBuildId: null,
3937
+ isLoading: true,
3938
+ user: null
3939
+ });
3940
+ const onInput = useInput_default();
3941
+ (0, import_react19.useEffect)(() => {
3942
+ async function pickBuildForRelease() {
3943
+ if (builds) {
3944
+ return;
3945
+ }
3946
+ let config2;
3947
+ try {
3948
+ config2 = getProjectConfig(configPath).config;
3949
+ } catch (e) {
3950
+ setState((previousState) => ({ ...previousState, error: e }));
3951
+ return;
3952
+ }
3953
+ const user2 = await findAppUserId_default(config2.id);
3954
+ getBuilds_default({
3955
+ addWhereClauses: (query) => query.where("status", "==", "succeeded"),
3956
+ appId: config2.id,
3957
+ limit: 50,
3958
+ userId: user2.id
3959
+ }).then((buildsResult) => {
3960
+ setState((previousState) => ({
3961
+ ...previousState,
3962
+ user: user2,
3963
+ builds: buildsResult.filter((buildResult) => !buildResult.releasedAt).slice(0, 5),
3964
+ isLoading: false
3965
+ }));
3966
+ }).catch((e) => {
3967
+ logForCI_default(e);
3968
+ setState((previousState) => ({
3969
+ ...previousState,
3970
+ error: e,
3971
+ isLoading: false
3972
+ }));
3973
+ });
3974
+ }
3975
+ pickBuildForRelease();
3976
+ }, [builds]);
3977
+ onInput((input, key) => {
3978
+ if (!builds || !builds.length || chosenBuildId) {
3979
+ return;
3980
+ }
3981
+ if (key.escape) {
3982
+ exit();
3983
+ }
3984
+ });
3985
+ if (error) {
3986
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ErrorDisplay_default, { commandUsed, error });
3987
+ }
3988
+ if (isLoading) {
3989
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LoadingText_default, {});
3990
+ }
3991
+ if (chosenBuildId) {
3992
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
3993
+ ReleaseBuild_default,
3994
+ {
3995
+ commandUsed,
3996
+ id: chosenBuildId,
3997
+ shouldConfirm
3998
+ }
3999
+ );
4000
+ }
4001
+ if (builds.length) {
4002
+ const data = builds.map((build) => ({
4003
+ ID: build.id,
4004
+ Version: build.appVersion || "unknown",
4005
+ "Creation date": getRelativeDateFromDateString_default(build.createdAt),
4006
+ Owner: user ? user.label : "unknown"
4007
+ }));
4008
+ const onSelect = (item) => setState((previousState) => ({
4009
+ ...previousState,
4010
+ chosenBuildId: item.value.ID
4011
+ }));
4012
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
4013
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink30.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink30.Text, { children: "Which build would you like to release?" }) }),
4014
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(SelectTable_default, { data, onSelect }),
4015
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink30.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_ink30.Text, { dimColor: true, children: [
4016
+ "Showing the latest ",
4017
+ builds.length,
4018
+ " unreleased successful builds"
4019
+ ] }) })
4020
+ ] });
4021
+ } else {
4022
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_ink30.Box, { children: [
4023
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink30.Text, { children: "No elligible builds found " }),
4024
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ink30.Text, { dimColor: true, children: "(i.e. unreleased and successful)" })
4025
+ ] });
4026
+ }
4027
+ };
4028
+ PickBuildForRelease.propTypes = {
4029
+ commandUsed: import_prop_types24.default.string.isRequired,
4030
+ shouldConfirm: import_prop_types24.default.bool.isRequired,
4031
+ configPath: import_prop_types24.default.string
4032
+ };
4033
+ var PickBuildForRelease_default = PickBuildForRelease;
4034
+
4035
+ // src/commands/ReleaseCommand.tsx
4036
+ var import_jsx_runtime35 = require("react/jsx-runtime");
4037
+ var ReleaseCommand = ({
4038
+ id,
4039
+ force,
4040
+ shouldReleaseLatest,
4041
+ configPath
4042
+ }) => {
4043
+ checkIfReactIsUsable_default();
4044
+ useAnalyticsCommand("release", {
4045
+ id,
4046
+ force,
4047
+ shouldReleaseLatest,
4048
+ config: configPath
4049
+ });
4050
+ if (id && id.startsWith(".")) {
4051
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
4052
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_ink31.Box, { children: [
4053
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ink31.Text, { bold: true, color: "red", children: "todesktop release <project-path>" }),
4054
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ink31.Text, { children: " is no longer supported. Run this instead:" })
4055
+ ] }),
4056
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ink31.Text, { bold: true, color: "greenBright", children: "todesktop build && todesktop release --latest --force" })
4057
+ ] });
4058
+ }
4059
+ const getContents = () => {
4060
+ if (id) {
4061
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4062
+ ReleaseBuild_default,
4063
+ {
4064
+ configPath,
4065
+ commandUsed: "todesktop release <id>",
4066
+ id,
4067
+ shouldConfirm: !force
4068
+ }
4069
+ );
4070
+ } else if (shouldReleaseLatest) {
4071
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4072
+ ReleaseBuild_default,
4073
+ {
4074
+ configPath,
4075
+ commandUsed: "todesktop release --latest",
4076
+ shouldReleaseLatest: true,
4077
+ shouldConfirm: !force
4078
+ }
4079
+ );
4080
+ } else {
4081
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4082
+ PickBuildForRelease_default,
4083
+ {
4084
+ configPath,
4085
+ commandUsed: "todesktop builds",
4086
+ shouldConfirm: !force
4087
+ }
4088
+ );
4089
+ }
4090
+ };
4091
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(LoginHOC_default, { children: getContents() }) });
4092
+ };
4093
+ var ReleaseCommand_default = ReleaseCommand;
4094
+
4095
+ // src/commands/WhoamiCommand.tsx
4096
+ var import_react20 = require("react");
4097
+ var import_ink32 = require("ink");
4098
+ var import_jsx_runtime36 = require("react/jsx-runtime");
4099
+ var WhoAmI = () => {
4100
+ const exit = useExit_default();
4101
+ checkIfReactIsUsable_default();
4102
+ const auth = getAuthConfig();
4103
+ const { hasAttemptedTracking } = useAnalyticsCommand("whoami", {}, {});
4104
+ (0, import_react20.useEffect)(() => {
4105
+ if (hasAttemptedTracking) {
4106
+ exit();
4107
+ }
4108
+ }, [exit, hasAttemptedTracking]);
4109
+ if (!auth || !auth.email) {
4110
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { children: "You're not signed in" });
4111
+ }
4112
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ink32.Text, { children: auth.email });
4113
+ };
4114
+ var WhoAmIWrapper = () => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ErrorBoundary_default, { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(LoginHOC_default, { isInteractive: false, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(WhoAmI, {}) }) });
4115
+ var WhoamiCommand_default = WhoAmIWrapper;
4116
+
4117
+ // src/utilities/exitIfCLIOutOfDate.ts
4118
+ var import_chalk3 = __toESM(require("chalk"));
4119
+ var import_is_installed_globally = __toESM(require("is-installed-globally"));
4120
+ var import_latest_version = __toESM(require("latest-version"));
4121
+ var import_semver = __toESM(require("semver"));
4122
+ var exitIfCLIOutOfDate_default = () => {
4123
+ if (process.env.AVA_PATH) {
4124
+ return;
4125
+ }
4126
+ const pkg = getToDesktopPackageJson();
4127
+ (0, import_latest_version.default)(pkg.name).then((latest) => {
4128
+ if (import_semver.default.gt(latest, pkg.version)) {
4129
+ const commandToUpdate = import_chalk3.default.greenBright(
4130
+ `npm install ${import_is_installed_globally.default ? "--location=global" : "--save-dev"} @todesktop/cli`
4131
+ );
4132
+ console.log(
4133
+ `Your version of @todesktop/cli is out of date.
4134
+ Run ${commandToUpdate} to update to v${latest}.`
4135
+ );
4136
+ if (!import_semver.default.satisfies(latest, `^${pkg.version}`)) {
4137
+ console.log(`CLI is exiting because it is out out of date.`);
4138
+ process.exit(1);
4139
+ }
4140
+ }
4141
+ });
4142
+ };
4143
+
4144
+ // src/utilities/initSentry.ts
4145
+ var Sentry3 = __toESM(require("@sentry/node"));
4146
+ var import_os2 = __toESM(require("os"));
4147
+
4148
+ // package.json
4149
+ var package_default = {
4150
+ private: false,
4151
+ publishConfig: {
4152
+ access: "public"
4153
+ },
4154
+ name: "@todesktop/cli",
4155
+ version: "1.6.0",
4156
+ license: "MIT",
4157
+ author: "Dave Jeffery <dave@todesktop.com> (http://www.todesktop.com/)",
4158
+ homepage: "https://todesktop.com/cli",
4159
+ bugs: {
4160
+ email: "dave@todesktop.com"
4161
+ },
4162
+ bin: {
4163
+ todesktop: "./dist/cli.js"
4164
+ },
4165
+ engines: {
4166
+ node: ">=12"
4167
+ },
4168
+ scripts: {
4169
+ dev: "cp-cli dev.env .env && npm run build:dev && npm link",
4170
+ "dev:prod": "cp-cli prod.env .env && npm run build && npm link",
4171
+ build: "esbuild src/index.ts --packages=external --bundle --sourcemap --platform=node --outfile=dist/cli.js && cp-cli prod.env .env",
4172
+ "build:dev": "esbuild src/index.ts --packages=external --bundle --sourcemap --platform=node --outfile=dist/cli.js && cp-cli prod.env .env",
4173
+ lint: "npm run lint:types && npm run lint:styles",
4174
+ "lint:styles": "eslint src test .eslintrc.js",
4175
+ "lint:types": "tsc",
4176
+ "lint--fix": "eslint src test --fix",
4177
+ release: "npm run build && npx np --tag=latest",
4178
+ "release-beta": "npm run build && npx np --any-branch --no-tests --tag=beta",
4179
+ test: "ava",
4180
+ "test--watch": "npm test -- --watch"
4181
+ },
4182
+ files: [
4183
+ "dist",
4184
+ "docs",
4185
+ ".env",
4186
+ "LICENSE",
4187
+ "README.md"
4188
+ ],
4189
+ dependencies: {
4190
+ "@sentry/node": "^5.27.2",
4191
+ ajv: "^8.11.2",
4192
+ "ajv-formats": "^2.1.1",
4193
+ "analytics-node": "^4.0.1",
4194
+ archiver: "^5.2.0",
4195
+ axios: "^0.21.1",
4196
+ "better-ajv-errors": "^1.2.0",
4197
+ bunyan: "^1.8.14",
4198
+ chalk: "^4.1.0",
4199
+ commander: "^9.4.1",
4200
+ conf: "^7.1.2",
4201
+ "date-fns": "^2.28.0",
4202
+ del: "^6.0.0",
4203
+ dotenv: "^8.2.0",
4204
+ du: "^1.0.0",
4205
+ "email-regex": "^4.0.0",
4206
+ "fast-glob": "^3.2.4",
4207
+ "final-form": "^4.20.1",
4208
+ "find-up": "^5.0.0",
4209
+ firebase: "^7.24.0",
4210
+ "git-rev-sync": "^3.0.2",
4211
+ ink: "^3.2.0",
4212
+ "ink-gradient": "^2.0.0",
4213
+ "ink-link": "^2.0.0",
4214
+ "ink-select-input": "^4.2.1",
4215
+ "ink-text-input": "^4.0.1",
4216
+ "is-ci": "^3.0.1",
4217
+ "is-installed-globally": "^0.3.2",
4218
+ "latest-version": "^5.1.0",
4219
+ "lodash.merge": "^4.6.2",
4220
+ "lodash.throttle": "^4.1.1",
4221
+ "parse-author": "^2.0.0",
4222
+ "pkg-up": "^3.1.0",
4223
+ "pretty-bytes": "^5.4.1",
4224
+ "prop-types": "^15.7.2",
4225
+ react: "^17.0.2",
4226
+ "react-final-form": "^6.5.1",
4227
+ semver: "^7.3.2",
4228
+ "source-map-support": "^0.5.21",
4229
+ "stream-to-array": "^2.3.0",
4230
+ superagent: "^7.1.3",
4231
+ uuid: "^8.3.1",
4232
+ "xdg-basedir": "^4.0.0"
4233
+ },
4234
+ devDependencies: {
4235
+ "@todesktop/shared": "^7.147.0",
4236
+ "@types/bunyan": "^1.8.6",
4237
+ "@types/react": "^18.0.26",
4238
+ "@typescript-eslint/eslint-plugin": "^5.46.1",
4239
+ "@typescript-eslint/parser": "^5.46.1",
4240
+ ava: "^4.3.1",
4241
+ "cp-cli": "^2.0.0",
4242
+ esbuild: "^0.16.5",
4243
+ "esbuild-register": "^3.4.1",
4244
+ eslint: "^8.29.0",
4245
+ "eslint-config-prettier": "^8.5.0",
4246
+ "eslint-plugin-import": "^2.26.0",
4247
+ "eslint-plugin-node": "^11.1.0",
4248
+ "eslint-plugin-prettier": "^4.2.1",
4249
+ "eslint-plugin-promise": "^6.1.1",
4250
+ "eslint-plugin-react": "^7.31.11",
4251
+ "eslint-plugin-react-hooks": "^4.6.0",
4252
+ "eslint-plugin-standard": "^4.1.0",
4253
+ execa: "^4.0.3",
4254
+ "extract-zip": "^2.0.1",
4255
+ "fs-extra": "^9.0.1",
4256
+ husky: "^4.3.0",
4257
+ "lint-staged": "^10.2.11",
4258
+ prettier: "^2.8.1",
4259
+ proxyquire: "^2.1.3",
4260
+ sinon: "^9.0.3",
4261
+ typescript: "^4.9.4"
4262
+ },
4263
+ ava: {
4264
+ extensions: [
4265
+ "ts"
4266
+ ],
4267
+ files: [
4268
+ "test/**/*.ts",
4269
+ "!test/fixtures/**/*",
4270
+ "!test/utilities/**/*"
4271
+ ],
4272
+ require: [
4273
+ "esbuild-register"
4274
+ ],
4275
+ timeout: "10m"
4276
+ },
4277
+ "lint-staged": {
4278
+ "**/*.{js,ts,tsx}": [
4279
+ "eslint src --fix",
4280
+ "git add"
4281
+ ]
4282
+ },
4283
+ husky: {
4284
+ hooks: {
4285
+ "pre-commit": "lint-staged"
4286
+ }
4287
+ },
4288
+ overrides: {
4289
+ pastel: {
4290
+ "parcel-bundler": {
4291
+ deasync: "0.1.27"
4292
+ }
4293
+ }
4294
+ },
4295
+ resolutions: {
4296
+ "pastel/parcel-bundler/deasync": "0.1.24",
4297
+ ink: "3.2.0",
4298
+ react: "17.0.2"
4299
+ },
4300
+ packageExtensions: {
4301
+ "ink-progress-bar@*": {
4302
+ dependencies: {
4303
+ react: "*",
4304
+ ink: "*"
4305
+ }
4306
+ }
4307
+ }
4308
+ };
4309
+
4310
+ // src/utilities/initSentry.ts
4311
+ var initSentry_default = () => {
4312
+ const { TODESKTOP_CLI_SENTRY_DSN } = getEnvironmentVariables_default();
4313
+ if (!TODESKTOP_CLI_SENTRY_DSN) {
4314
+ return;
4315
+ }
4316
+ Sentry3.init({
4317
+ dsn: TODESKTOP_CLI_SENTRY_DSN
4318
+ });
4319
+ Sentry3.configureScope((scope) => {
4320
+ try {
4321
+ scope.setUser({ email: getAuthConfig().email });
4322
+ } catch (err) {
4323
+ }
4324
+ try {
4325
+ scope.setContext("os", {
4326
+ name: import_os2.default.platform(),
4327
+ version: import_os2.default.release()
4328
+ });
4329
+ } catch (err) {
4330
+ }
4331
+ try {
4332
+ scope.setContext("runtime", {
4333
+ name: "Node.js",
4334
+ version: process.version
4335
+ });
4336
+ } catch (err) {
4337
+ }
4338
+ try {
4339
+ scope.setContext("app", {
4340
+ app_name: package_default.name,
4341
+ app_version: package_default.version
4342
+ });
4343
+ } catch (err) {
4344
+ }
4345
+ try {
4346
+ const configResult = getProjectConfig();
4347
+ scope.setExtra("config", configResult);
4348
+ } catch (err) {
4349
+ }
4350
+ });
4351
+ };
4352
+
4353
+ // src/utilities/onCommand.ts
4354
+ var onCommand_default = ({ sentry = true, exitIfOutOfDate = true } = {}) => {
4355
+ if (sentry) {
4356
+ initSentry_default();
4357
+ }
4358
+ if (exitIfOutOfDate) {
4359
+ exitIfCLIOutOfDate_default();
4360
+ }
4361
+ };
4362
+
4363
+ // src/index.ts
4364
+ var parseCount = (value) => {
4365
+ const parsedValue = Number.parseInt(value, 10);
4366
+ if (parsedValue > 0) {
4367
+ return parsedValue;
4368
+ }
4369
+ throw new import_commander.InvalidArgumentError("Should be a positive number");
4370
+ };
4371
+ import_commander.program.name("todekstop").version(getCliVersion_default());
4372
+ import_commander.program.command("build").description(
4373
+ "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."
4374
+ ).option(
4375
+ "--code-sign [bool]",
4376
+ "Whether or not code-signing and notarization should be performed. Disable this for quicker builds"
4377
+ ).option(
4378
+ "--config [string]",
4379
+ "Path to a different configuration file. If not specified, `todesktop.json` in the project root will be used"
4380
+ ).action(({ codeSign, config: config2 }) => {
4381
+ runCommand(BuildCommand_default, {
4382
+ shouldCodeSign: codeSign,
4383
+ configPath: config2
4384
+ });
4385
+ });
4386
+ import_commander.program.command("builds").description("View your builds").argument("[id]", "View a specific build by ID").option("--latest", "View the latest build").option(
4387
+ "--config [string]",
4388
+ "Path to a different configuration file. If not specified, `todesktop.json` in the project root will be used"
4389
+ ).option("--count [number]", "Number of builds to show per page", parseCount).addOption(
4390
+ new import_commander.Option("--format <type>", "Format to output the build details in").choices(["json", "table"]).default("table")
4391
+ ).option("--exit", "View the latest build").action((id, { config: config2, count, exit, format, latest }) => {
4392
+ runCommand(BuildsCommand_default, {
4393
+ configPath: config2,
4394
+ count,
4395
+ exit,
4396
+ format,
4397
+ id,
4398
+ shouldViewLatest: latest
4399
+ });
4400
+ });
4401
+ import_commander.program.command("logout").description("Logs you out").action(() => {
4402
+ runCommand(LogoutCommand_default, null, { exitIfOutOfDate: false });
4403
+ });
4404
+ import_commander.program.command("release").description("Release a build").argument("[id]", "A specific build ID to release").option("--force", "Skips interactive confirmation step").option("--latest", "Release the latest build").option(
4405
+ "--config [string]",
4406
+ "Path to a different configuration file. If not specified, `todesktop.json` in the project root will be used"
4407
+ ).action((id, { config: config2, force, latest }) => {
4408
+ runCommand(ReleaseCommand_default, {
4409
+ configPath: config2,
4410
+ force,
4411
+ id,
4412
+ shouldReleaseLatest: latest
4413
+ });
4414
+ });
4415
+ import_commander.program.command("whoami").description("Prints the email of the account you're signed into").action(() => {
4416
+ runCommand(WhoamiCommand_default);
4417
+ });
4418
+ var runCommand = (component, props = null, { exitIfOutOfDate = true } = {}) => {
4419
+ onCommand_default({ exitIfOutOfDate });
4420
+ const { waitUntilExit } = (0, import_ink33.render)((0, import_react21.createElement)(component, props));
4421
+ waitUntilExit().catch((error) => {
4422
+ console.error(error.stack);
4423
+ });
4424
+ };
4425
+ var args = [...process.argv];
4426
+ if (args.length === 2) {
4427
+ args.push("help");
4428
+ }
4429
+ import_commander.program.parse(args);
4430
+ //# sourceMappingURL=cli.js.map