eas-cli 20.0.0 → 20.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ import EasCommand from '../../commandUtils/EasCommand';
2
+ export default class SimulatorExec extends EasCommand {
3
+ static hidden: boolean;
4
+ static description: string;
5
+ static strict: boolean;
6
+ static contextDefinition: {
7
+ projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
8
+ };
9
+ private isRunningSubprocess;
10
+ runAsync(): Promise<void>;
11
+ protected catch(err: Error): Promise<any>;
12
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async"));
5
+ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
6
+ const env_1 = require("../../simulator/env");
7
+ class SimulatorExec extends EasCommand_1.default {
8
+ static hidden = true;
9
+ static description = `[EXPERIMENTAL] execute a simulator command with ${env_1.SIMULATOR_DOTENV_FILE_NAME} environment loaded`;
10
+ static strict = false;
11
+ static contextDefinition = {
12
+ ...this.ContextOptions.ProjectDir,
13
+ };
14
+ isRunningSubprocess = false;
15
+ async runAsync() {
16
+ const rawArgv = [...this.argv];
17
+ // Required to avoid `Warning: Command exec did not parse its arguments. Did you forget to call 'this.parse'?`
18
+ await this.parse(SimulatorExec, []);
19
+ const [command, ...args] = rawArgv;
20
+ if (typeof command !== 'string' || command.length === 0) {
21
+ throw new Error('No command provided. Run `eas simulator:exec <command> [args...]`.');
22
+ }
23
+ const { projectDir } = await this.getContextAsync(SimulatorExec, {
24
+ nonInteractive: true,
25
+ });
26
+ await (0, env_1.loadSimulatorEnvAsync)(projectDir);
27
+ this.isRunningSubprocess = true;
28
+ await (0, spawn_async_1.default)(command, args, {
29
+ stdio: 'inherit',
30
+ env: process.env,
31
+ });
32
+ }
33
+ catch(err) {
34
+ // Propagate wrapped command from spawnAsync rejection
35
+ if (this.isRunningSubprocess) {
36
+ process.exitCode = process.exitCode ?? err.status ?? 1;
37
+ return Promise.resolve();
38
+ }
39
+ return super.catch(err);
40
+ }
41
+ }
42
+ exports.default = SimulatorExec;
@@ -5,9 +5,10 @@ export default class SimulatorGet extends EasCommand {
5
5
  static flags: {
6
6
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
7
  'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
- id: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ id: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
9
  };
10
10
  static contextDefinition: {
11
+ projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
11
12
  loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
12
13
  };
13
14
  runAsync(): Promise<void>;
@@ -9,6 +9,7 @@ const generated_1 = require("../../graphql/generated");
9
9
  const DeviceRunSessionQuery_1 = require("../../graphql/queries/DeviceRunSessionQuery");
10
10
  const log_1 = tslib_1.__importStar(require("../../log"));
11
11
  const ora_1 = require("../../ora");
12
+ const env_1 = require("../../simulator/env");
12
13
  const utils_1 = require("../../simulator/utils");
13
14
  const json_1 = require("../../utils/json");
14
15
  class SimulatorGet extends EasCommand_1.default {
@@ -16,13 +17,13 @@ class SimulatorGet extends EasCommand_1.default {
16
17
  static description = '[EXPERIMENTAL] get info about a remote simulator session on EAS by its device run session ID';
17
18
  static flags = {
18
19
  id: core_1.Flags.string({
19
- description: 'Device run session ID',
20
- required: true,
20
+ description: `Device run session ID. Defaults to ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`,
21
21
  }),
22
22
  ...flags_1.EasNonInteractiveAndJsonFlags,
23
23
  };
24
24
  static contextDefinition = {
25
25
  ...this.ContextOptions.LoggedIn,
26
+ ...this.ContextOptions.ProjectDir,
26
27
  };
27
28
  async runAsync() {
28
29
  const { flags } = await this.parse(SimulatorGet);
@@ -30,17 +31,22 @@ class SimulatorGet extends EasCommand_1.default {
30
31
  if (jsonFlag) {
31
32
  (0, json_1.enableJsonOutput)();
32
33
  }
33
- const { loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorGet, {
34
+ const { projectDir, loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorGet, {
34
35
  nonInteractive,
35
36
  });
36
- const fetchSpinner = (0, ora_1.ora)(`Fetching device run session ${flags.id}`).start();
37
+ await (0, env_1.loadSimulatorEnvAsync)(projectDir);
38
+ const flagId = flags.id || process.env[env_1.EAS_SIMULATOR_SESSION_ID];
39
+ if (!flagId) {
40
+ throw new Error(`No simulator session ID provided. Pass --id, or run \`eas simulator:start\` first to write ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`);
41
+ }
42
+ const fetchSpinner = (0, ora_1.ora)(`Fetching device run session ${flagId}`).start();
37
43
  let session;
38
44
  try {
39
- session = await DeviceRunSessionQuery_1.DeviceRunSessionQuery.byIdAsync(graphqlClient, flags.id);
45
+ session = await DeviceRunSessionQuery_1.DeviceRunSessionQuery.byIdAsync(graphqlClient, flagId);
40
46
  fetchSpinner.succeed(`Fetched device run session ${session.id}`);
41
47
  }
42
48
  catch (err) {
43
- fetchSpinner.fail(`Failed to fetch device run session ${flags.id}`);
49
+ fetchSpinner.fail(`Failed to fetch device run session ${flagId}`);
44
50
  throw err;
45
51
  }
46
52
  const jobRunUrl = session.turtleJobRun
@@ -64,7 +70,7 @@ class SimulatorGet extends EasCommand_1.default {
64
70
  if (session.status === generated_1.DeviceRunSessionStatus.InProgress) {
65
71
  log_1.default.newLine();
66
72
  if (session.remoteConfig) {
67
- log_1.default.log((0, utils_1.formatRemoteSessionInstructions)(session.remoteConfig));
73
+ log_1.default.log((0, utils_1.formatRemoteSessionInstructions)(session.remoteConfig, 'env'));
68
74
  }
69
75
  else {
70
76
  log_1.default.log('⏳ Session is starting up — remote config is not available yet. Re-run this command in a moment.');
@@ -8,9 +8,12 @@ export default class SimulatorStart extends EasCommand {
8
8
  platform: import("@oclif/core/lib/interfaces").OptionFlag<"android" | "ios", import("@oclif/core/lib/interfaces").CustomOptions>;
9
9
  type: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
10
  'package-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
+ 'out-config-type': import("@oclif/core/lib/interfaces").OptionFlag<"env" | "dotenv", import("@oclif/core/lib/interfaces").CustomOptions>;
11
13
  };
12
14
  static contextDefinition: {
13
15
  loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
16
+ projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
14
17
  projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
15
18
  };
16
19
  runAsync(): Promise<void>;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const core_1 = require("@oclif/core");
5
+ const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
5
6
  const url_1 = require("../../build/utils/url");
6
7
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
7
8
  const flags_1 = require("../../commandUtils/flags");
@@ -10,12 +11,16 @@ const DeviceRunSessionMutation_1 = require("../../graphql/mutations/DeviceRunSes
10
11
  const DeviceRunSessionQuery_1 = require("../../graphql/queries/DeviceRunSessionQuery");
11
12
  const log_1 = tslib_1.__importStar(require("../../log"));
12
13
  const ora_1 = require("../../ora");
14
+ const env_1 = require("../../simulator/env");
13
15
  const utils_1 = require("../../simulator/utils");
14
16
  const json_1 = require("../../utils/json");
15
17
  const promise_1 = require("../../utils/promise");
16
- const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
17
18
  const POLL_INTERVAL_MS = 5_000; // 5 seconds
18
19
  const POLL_TIMEOUT_MS = 15 * 60 * 1_000; // 15 minutes
20
+ const OUT_CONFIG_TYPE_VALUES = {
21
+ Env: 'env',
22
+ Dotenv: 'dotenv',
23
+ };
19
24
  class SimulatorStart extends EasCommand_1.default {
20
25
  static hidden = true;
21
26
  static description = '[EXPERIMENTAL] start a remote simulator session on EAS and get instructions to connect to it';
@@ -33,10 +38,21 @@ class SimulatorStart extends EasCommand_1.default {
33
38
  'package-version': core_1.Flags.string({
34
39
  description: 'Version of the package backing the device run session (e.g. "0.1.3-alpha.3"). Defaults to "latest" when omitted.',
35
40
  }),
41
+ force: core_1.Flags.boolean({
42
+ description: '[default: true] Create a new device session even when an existing simulator session is present in the environment.',
43
+ default: true,
44
+ allowNo: true,
45
+ }),
46
+ 'out-config-type': core_1.Flags.option({
47
+ description: `How to output simulator connection configuration. Use "env" to print shell exports, or "dotenv" to write ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`,
48
+ options: Object.values(OUT_CONFIG_TYPE_VALUES),
49
+ default: OUT_CONFIG_TYPE_VALUES.Dotenv,
50
+ })(),
36
51
  ...flags_1.EasNonInteractiveAndJsonFlags,
37
52
  };
38
53
  static contextDefinition = {
39
54
  ...this.ContextOptions.ProjectId,
55
+ ...this.ContextOptions.ProjectDir,
40
56
  ...this.ContextOptions.LoggedIn,
41
57
  };
42
58
  async runAsync() {
@@ -45,9 +61,20 @@ class SimulatorStart extends EasCommand_1.default {
45
61
  if (jsonFlag) {
46
62
  (0, json_1.enableJsonOutput)();
47
63
  }
48
- const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStart, {
64
+ const { projectId, projectDir, loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStart, {
49
65
  nonInteractive,
50
66
  });
67
+ await (0, env_1.loadSimulatorEnvAsync)(projectDir);
68
+ const existingDeviceRunSessionId = process.env[env_1.EAS_SIMULATOR_SESSION_ID];
69
+ if (existingDeviceRunSessionId && !flags.force) {
70
+ throw new Error(`Existing simulator session in environment. Use --force to create a new device session.`);
71
+ }
72
+ if (existingDeviceRunSessionId) {
73
+ log_1.default.warn(` Overwriting previous simulator session (id: ${existingDeviceRunSessionId}). ` +
74
+ `The previous remote session will continue running until stopped. ` +
75
+ `To stop it, run: eas simulator:stop --id ${existingDeviceRunSessionId}`);
76
+ log_1.default.newLine();
77
+ }
51
78
  const platform = flags.platform === 'android' ? generated_1.AppPlatform.Android : generated_1.AppPlatform.Ios;
52
79
  const createSpinner = (0, ora_1.ora)('🚀 Creating device run session').start();
53
80
  let deviceRunSessionId;
@@ -62,7 +89,12 @@ class SimulatorStart extends EasCommand_1.default {
62
89
  deviceRunSessionId = session.id;
63
90
  const jobRunId = (0, nullthrows_1.default)(session.turtleJobRun?.id, 'Expected device run session to start');
64
91
  jobRunUrl = (0, url_1.getBareJobRunUrl)(session.app.ownerAccount.name, session.app.slug, jobRunId);
65
- createSpinner.succeed(`Device run session created (id: ${deviceRunSessionId}) ${(0, log_1.link)(jobRunUrl)}`);
92
+ const simulatorEnvWritten = !jsonFlag && flags['out-config-type'] === OUT_CONFIG_TYPE_VALUES.Dotenv
93
+ ? await writeSimulatorEnvSafelyAsync(projectDir, {
94
+ [env_1.EAS_SIMULATOR_SESSION_ID]: deviceRunSessionId,
95
+ })
96
+ : false;
97
+ createSpinner.succeed(`Device run session created (id: ${deviceRunSessionId}${simulatorEnvWritten ? `, saved to ${env_1.SIMULATOR_DOTENV_FILE_NAME}` : ''}) ${(0, log_1.link)(jobRunUrl)}`);
66
98
  }
67
99
  catch (err) {
68
100
  createSpinner.fail('Failed to create device run session');
@@ -102,6 +134,12 @@ class SimulatorStart extends EasCommand_1.default {
102
134
  await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
103
135
  throw new Error(`Timed out after ${Math.round(POLL_TIMEOUT_MS / 1000)}s waiting for ${flags.type} session to be ready. ${(0, log_1.link)(jobRunUrl)}`);
104
136
  }
137
+ if (flags['out-config-type'] === OUT_CONFIG_TYPE_VALUES.Dotenv) {
138
+ await writeSimulatorEnvSafelyAsync(projectDir, {
139
+ ...(0, utils_1.getRemoteSessionEnvironmentVariables)(remoteConfig),
140
+ [env_1.EAS_SIMULATOR_SESSION_ID]: deviceRunSessionId,
141
+ });
142
+ }
105
143
  if (jsonFlag) {
106
144
  (0, json_1.printJsonOnlyOutput)({
107
145
  id: deviceRunSessionId,
@@ -112,7 +150,7 @@ class SimulatorStart extends EasCommand_1.default {
112
150
  return;
113
151
  }
114
152
  log_1.default.newLine();
115
- log_1.default.log((0, utils_1.formatRemoteSessionInstructions)(remoteConfig));
153
+ log_1.default.log((0, utils_1.formatRemoteSessionInstructions)(remoteConfig, flags['out-config-type']));
116
154
  log_1.default.newLine();
117
155
  if (nonInteractive) {
118
156
  log_1.default.log(`When you are done, stop the session with: eas simulator:stop --id ${deviceRunSessionId}`);
@@ -122,11 +160,22 @@ class SimulatorStart extends EasCommand_1.default {
122
160
  graphqlClient,
123
161
  deviceRunSessionId,
124
162
  jobRunUrl,
163
+ projectDir,
125
164
  });
126
165
  }
127
166
  }
128
167
  exports.default = SimulatorStart;
129
- async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessionId, jobRunUrl, }) {
168
+ async function writeSimulatorEnvSafelyAsync(projectDir, environmentVariables) {
169
+ try {
170
+ await (0, env_1.writeSimulatorEnvAsync)(projectDir, environmentVariables);
171
+ return true;
172
+ }
173
+ catch (err) {
174
+ log_1.default.warn(`Failed to write simulator environment variables to ${env_1.SIMULATOR_DOTENV_FILE_NAME}: ${err instanceof Error ? err.message : String(err)}`);
175
+ return false;
176
+ }
177
+ }
178
+ async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessionId, jobRunUrl, projectDir, }) {
130
179
  const spinner = (0, ora_1.ora)(`Device run session active — press Ctrl+C to stop, or run \`eas simulator:stop --id ${deviceRunSessionId}\` from another shell`).start();
131
180
  const abortController = new AbortController();
132
181
  const { signal } = abortController;
@@ -166,6 +215,7 @@ async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessi
166
215
  jobRunStatus === generated_1.JobRunStatus.Canceled ||
167
216
  jobRunStatus === generated_1.JobRunStatus.Finished) {
168
217
  spinner.succeed(`Device run session ended. ${(0, log_1.link)(jobRunUrl)}`);
218
+ await resetSimulatorEnvVerboseAsync(projectDir);
169
219
  return;
170
220
  }
171
221
  await Promise.race([(0, promise_1.sleepAsync)(POLL_INTERVAL_MS), abortPromise]);
@@ -174,6 +224,7 @@ async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessi
174
224
  const stopped = await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
175
225
  if (stopped) {
176
226
  spinner.succeed('Device run session stopped');
227
+ await resetSimulatorEnvVerboseAsync(projectDir);
177
228
  }
178
229
  else {
179
230
  spinner.fail(`Could not confirm the device run session was stopped. Run \`eas simulator:stop --id ${deviceRunSessionId}\` to terminate it and avoid unexpected charges.`);
@@ -183,6 +234,15 @@ async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessi
183
234
  process.removeListener('SIGINT', sigintHandler);
184
235
  }
185
236
  }
237
+ async function resetSimulatorEnvVerboseAsync(projectDir) {
238
+ try {
239
+ await (0, env_1.resetSimulatorEnvAsync)(projectDir);
240
+ }
241
+ catch (err) {
242
+ log_1.default.error(`Failed to clean up ${env_1.SIMULATOR_DOTENV_FILE_NAME}`);
243
+ throw err;
244
+ }
245
+ }
186
246
  async function ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId) {
187
247
  try {
188
248
  await DeviceRunSessionMutation_1.DeviceRunSessionMutation.ensureDeviceRunSessionStoppedAsync(graphqlClient, deviceRunSessionId);
@@ -5,9 +5,10 @@ export default class SimulatorStop extends EasCommand {
5
5
  static flags: {
6
6
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
7
  'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
- id: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ id: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
9
  };
10
10
  static contextDefinition: {
11
+ projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
11
12
  loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
12
13
  };
13
14
  runAsync(): Promise<void>;
@@ -6,19 +6,20 @@ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasComm
6
6
  const flags_1 = require("../../commandUtils/flags");
7
7
  const DeviceRunSessionMutation_1 = require("../../graphql/mutations/DeviceRunSessionMutation");
8
8
  const ora_1 = require("../../ora");
9
+ const env_1 = require("../../simulator/env");
9
10
  const json_1 = require("../../utils/json");
10
11
  class SimulatorStop extends EasCommand_1.default {
11
12
  static hidden = true;
12
13
  static description = '[EXPERIMENTAL] stop a remote simulator session on EAS by its device run session ID';
13
14
  static flags = {
14
15
  id: core_1.Flags.string({
15
- description: 'Device run session ID',
16
- required: true,
16
+ description: `Device run session ID. Defaults to ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`,
17
17
  }),
18
18
  ...flags_1.EasNonInteractiveAndJsonFlags,
19
19
  };
20
20
  static contextDefinition = {
21
21
  ...this.ContextOptions.LoggedIn,
22
+ ...this.ContextOptions.ProjectDir,
22
23
  };
23
24
  async runAsync() {
24
25
  const { flags } = await this.parse(SimulatorStop);
@@ -26,21 +27,27 @@ class SimulatorStop extends EasCommand_1.default {
26
27
  if (jsonFlag) {
27
28
  (0, json_1.enableJsonOutput)();
28
29
  }
29
- const { loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStop, {
30
+ const { projectDir, loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStop, {
30
31
  nonInteractive,
31
32
  });
32
- const stopSpinner = (0, ora_1.ora)(`🛑 Stopping device run session ${flags.id}`).start();
33
+ await (0, env_1.loadSimulatorEnvAsync)(projectDir);
34
+ const flagId = flags.id || process.env[env_1.EAS_SIMULATOR_SESSION_ID];
35
+ if (!flagId) {
36
+ throw new Error(`No simulator session ID provided. Pass --id, or run \`eas simulator:start\` first to write ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`);
37
+ }
38
+ const stopSpinner = (0, ora_1.ora)(`🛑 Stopping device run session ${flagId}`).start();
39
+ let session;
33
40
  try {
34
- const session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.ensureDeviceRunSessionStoppedAsync(graphqlClient, flags.id);
41
+ session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.ensureDeviceRunSessionStoppedAsync(graphqlClient, flagId);
35
42
  stopSpinner.succeed(`🎉 Device run session ${session.id} is ${session.status.toLowerCase()}`);
36
- if (jsonFlag) {
37
- (0, json_1.printJsonOnlyOutput)({ id: session.id, status: session.status });
38
- }
39
43
  }
40
44
  catch (err) {
41
- stopSpinner.fail(`Failed to stop device run session ${flags.id}`);
45
+ stopSpinner.fail(`Failed to stop device run session ${flagId}`);
42
46
  throw err;
43
47
  }
48
+ if (jsonFlag) {
49
+ (0, json_1.printJsonOnlyOutput)({ id: session.id, status: session.status });
50
+ }
44
51
  }
45
52
  }
46
53
  exports.default = SimulatorStop;
@@ -0,0 +1,15 @@
1
+ import EasCommand from '../../../commandUtils/EasCommand';
2
+ export default class UpdateEmbeddedDelete extends EasCommand {
3
+ static description: string;
4
+ static args: {
5
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static flags: {
8
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ static contextDefinition: {
12
+ loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
13
+ };
14
+ runAsync(): Promise<void>;
15
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const core_1 = require("@oclif/core");
5
+ const EasCommand_1 = tslib_1.__importDefault(require("../../../commandUtils/EasCommand"));
6
+ const flags_1 = require("../../../commandUtils/flags");
7
+ const EmbeddedUpdateMutation_1 = require("../../../graphql/mutations/EmbeddedUpdateMutation");
8
+ const log_1 = tslib_1.__importDefault(require("../../../log"));
9
+ const prompts_1 = require("../../../prompts");
10
+ const json_1 = require("../../../utils/json");
11
+ class UpdateEmbeddedDelete extends EasCommand_1.default {
12
+ static description = 'delete an embedded update registered with EAS Update';
13
+ static args = {
14
+ id: core_1.Args.string({
15
+ required: true,
16
+ description: 'The ID of the embedded update (manifest UUID from app.manifest).',
17
+ }),
18
+ };
19
+ static flags = {
20
+ ...flags_1.EasNonInteractiveAndJsonFlags,
21
+ };
22
+ static contextDefinition = {
23
+ ...this.ContextOptions.LoggedIn,
24
+ };
25
+ async runAsync() {
26
+ const { args: { id: embeddedUpdateId }, flags, } = await this.parse(UpdateEmbeddedDelete);
27
+ const { json: jsonFlag, nonInteractive } = (0, flags_1.resolveNonInteractiveAndJsonFlags)(flags);
28
+ const { loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdateEmbeddedDelete, { nonInteractive });
29
+ if (jsonFlag) {
30
+ (0, json_1.enableJsonOutput)();
31
+ }
32
+ if (!nonInteractive) {
33
+ log_1.default.log(`You are about to permanently delete embedded update: "${embeddedUpdateId}". ` +
34
+ `Diff patches already generated against this bundle keep serving, but new diffs ` +
35
+ `can't be generated until you re-upload it.`);
36
+ log_1.default.newLine();
37
+ const confirmed = await (0, prompts_1.toggleConfirmAsync)({ message: 'Are you sure you wish to proceed?' });
38
+ if (!confirmed) {
39
+ log_1.default.error(`Canceled deletion of embedded update: "${embeddedUpdateId}".`);
40
+ process.exit(1);
41
+ }
42
+ }
43
+ // Best-effort delete on the server: deleting an unknown id succeeds (idempotent),
44
+ // so we don't need a not-found branch here.
45
+ await EmbeddedUpdateMutation_1.EmbeddedUpdateMutation.deleteEmbeddedUpdateAsync(graphqlClient, {
46
+ id: embeddedUpdateId,
47
+ });
48
+ if (jsonFlag) {
49
+ (0, json_1.printJsonOnlyOutput)({ id: embeddedUpdateId });
50
+ return;
51
+ }
52
+ log_1.default.withTick(`Deleted embedded update ${embeddedUpdateId}`);
53
+ }
54
+ }
55
+ exports.default = UpdateEmbeddedDelete;
@@ -0,0 +1,19 @@
1
+ import { Platform } from '@expo/eas-build-job';
2
+ import EasCommand from '../../../commandUtils/EasCommand';
3
+ export default class UpdateEmbeddedList extends EasCommand {
4
+ static description: string;
5
+ static flags: {
6
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ platform: import("@oclif/core/lib/interfaces").OptionFlag<Platform | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ 'runtime-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ channel: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ limit: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined>;
12
+ 'after-cursor': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ };
14
+ static contextDefinition: {
15
+ loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
16
+ projectId: import("../../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
17
+ };
18
+ runAsync(): Promise<void>;
19
+ }
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const eas_build_job_1 = require("@expo/eas-build-job");
5
+ const core_1 = require("@oclif/core");
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const EasCommand_1 = tslib_1.__importDefault(require("../../../commandUtils/EasCommand"));
8
+ const flags_1 = require("../../../commandUtils/flags");
9
+ const pagination_1 = require("../../../commandUtils/pagination");
10
+ const ChannelQuery_1 = require("../../../graphql/queries/ChannelQuery");
11
+ const EmbeddedUpdateQuery_1 = require("../../../graphql/queries/EmbeddedUpdateQuery");
12
+ const AppPlatform_1 = require("../../../graphql/types/AppPlatform");
13
+ const log_1 = tslib_1.__importDefault(require("../../../log"));
14
+ const prompts_1 = require("../../../prompts");
15
+ const date_1 = require("../../../utils/date");
16
+ const formatFields_1 = tslib_1.__importDefault(require("../../../utils/formatFields"));
17
+ const json_1 = require("../../../utils/json");
18
+ const DEFAULT_LIMIT = 25;
19
+ const MAX_LIMIT = 50;
20
+ const CHANNELS_LIMIT = 50;
21
+ class UpdateEmbeddedList extends EasCommand_1.default {
22
+ static description = 'list embedded updates registered with EAS Update for this project';
23
+ static flags = {
24
+ platform: core_1.Flags.option({
25
+ char: 'p',
26
+ description: 'Filter by platform',
27
+ options: [eas_build_job_1.Platform.IOS, eas_build_job_1.Platform.ANDROID],
28
+ })(),
29
+ 'runtime-version': core_1.Flags.string({
30
+ description: 'Filter by runtime version',
31
+ }),
32
+ channel: core_1.Flags.string({
33
+ description: 'Filter by channel name (pass "all" to skip the channel prompt)',
34
+ }),
35
+ limit: (0, pagination_1.getLimitFlagWithCustomValues)({ defaultTo: DEFAULT_LIMIT, limit: MAX_LIMIT }),
36
+ 'after-cursor': core_1.Flags.string({
37
+ description: 'Return items after this cursor (for pagination)',
38
+ }),
39
+ ...flags_1.EasNonInteractiveAndJsonFlags,
40
+ };
41
+ static contextDefinition = {
42
+ ...this.ContextOptions.ProjectId,
43
+ ...this.ContextOptions.LoggedIn,
44
+ };
45
+ async runAsync() {
46
+ const { flags } = await this.parse(UpdateEmbeddedList);
47
+ const { json: jsonFlag, nonInteractive } = (0, flags_1.resolveNonInteractiveAndJsonFlags)(flags);
48
+ const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdateEmbeddedList, { nonInteractive });
49
+ if (jsonFlag) {
50
+ (0, json_1.enableJsonOutput)();
51
+ }
52
+ const platform = flags.platform
53
+ ? (0, AppPlatform_1.toAppPlatform)(flags.platform)
54
+ : undefined;
55
+ // Resolve channel filter:
56
+ // - `--channel <name>`: use it
57
+ // - `--channel all` (or no flag in non-interactive / json): no channel filter
58
+ // - no flag in interactive: prompt with the project's channels + "All channels"
59
+ let channel;
60
+ if (flags.channel) {
61
+ channel = flags.channel.toLowerCase() === 'all' ? undefined : flags.channel;
62
+ }
63
+ else if (!nonInteractive && !jsonFlag) {
64
+ channel = await promptForChannelAsync(graphqlClient, projectId);
65
+ }
66
+ const filter = platform || flags['runtime-version'] || channel
67
+ ? {
68
+ platform,
69
+ runtimeVersion: flags['runtime-version'],
70
+ channel,
71
+ }
72
+ : undefined;
73
+ const limit = flags.limit ?? DEFAULT_LIMIT;
74
+ const connection = await EmbeddedUpdateQuery_1.EmbeddedUpdateQuery.viewPaginatedAsync(graphqlClient, {
75
+ appId: projectId,
76
+ filter,
77
+ first: limit,
78
+ after: flags['after-cursor'],
79
+ });
80
+ const embeddedUpdates = connection.edges.map(e => e.node);
81
+ if (jsonFlag) {
82
+ (0, json_1.printJsonOnlyOutput)({
83
+ embeddedUpdates,
84
+ pageInfo: connection.pageInfo,
85
+ });
86
+ return;
87
+ }
88
+ if (embeddedUpdates.length === 0) {
89
+ log_1.default.log('No embedded updates found.');
90
+ return;
91
+ }
92
+ log_1.default.addNewLineIfNone();
93
+ log_1.default.log(chalk_1.default.bold(`Embedded updates (${embeddedUpdates.length}${connection.pageInfo.hasNextPage ? '+' : ''}):`));
94
+ log_1.default.newLine();
95
+ log_1.default.log(embeddedUpdates.map(formatEmbeddedUpdateRow).join(`\n\n${chalk_1.default.dim('———')}\n\n`));
96
+ if (connection.pageInfo.hasNextPage && connection.pageInfo.endCursor) {
97
+ log_1.default.newLine();
98
+ log_1.default.log(chalk_1.default.dim(`Showing ${embeddedUpdates.length}. For the next page, run with --after-cursor ${connection.pageInfo.endCursor}`));
99
+ }
100
+ }
101
+ }
102
+ exports.default = UpdateEmbeddedList;
103
+ // Sentinel for the "All channels" option. We can't use `undefined` here because
104
+ // the underlying `prompts` library substitutes a choice's index when its value
105
+ // is undefined, which then leaks into the GraphQL filter.
106
+ const ALL_CHANNELS = '__embedded_update_list__all_channels__';
107
+ async function promptForChannelAsync(graphqlClient, projectId) {
108
+ const channels = await ChannelQuery_1.ChannelQuery.viewUpdateChannelsOnAppAsync(graphqlClient, {
109
+ appId: projectId,
110
+ offset: 0,
111
+ limit: CHANNELS_LIMIT,
112
+ });
113
+ if (channels.length === 0) {
114
+ // Nothing to choose from — fall back to listing everything.
115
+ return undefined;
116
+ }
117
+ const selected = await (0, prompts_1.selectAsync)('Filter embedded updates by which channel?', [
118
+ { title: 'All channels', value: ALL_CHANNELS },
119
+ ...channels.map(c => ({ title: c.name, value: c.name })),
120
+ ]);
121
+ return selected === ALL_CHANNELS ? undefined : selected;
122
+ }
123
+ function formatEmbeddedUpdateRow(embeddedUpdate) {
124
+ const createdAt = new Date(embeddedUpdate.createdAt);
125
+ return (0, formatFields_1.default)([
126
+ { label: 'ID', value: embeddedUpdate.id },
127
+ { label: 'Platform', value: embeddedUpdate.platform.toLowerCase() },
128
+ { label: 'Runtime version', value: embeddedUpdate.runtimeVersion },
129
+ { label: 'Channel', value: embeddedUpdate.channel },
130
+ { label: 'Created', value: `${(0, date_1.fromNow)(createdAt)} ago` },
131
+ ]);
132
+ }
@@ -0,0 +1,17 @@
1
+ import EasCommand from '../../../commandUtils/EasCommand';
2
+ import { EmbeddedUpdateFragment } from '../../../graphql/queries/EmbeddedUpdateQuery';
3
+ export default class UpdateEmbeddedView extends EasCommand {
4
+ static description: string;
5
+ static args: {
6
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ static contextDefinition: {
12
+ loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
13
+ projectId: import("../../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
14
+ };
15
+ runAsync(): Promise<void>;
16
+ }
17
+ export declare function formatEmbeddedUpdate(embeddedUpdate: EmbeddedUpdateFragment): string;