eas-cli 18.9.1 → 18.11.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.
Files changed (72) hide show
  1. package/README.md +230 -95
  2. package/build/build/runBuildAndSubmit.d.ts +3 -1
  3. package/build/build/runBuildAndSubmit.js +12 -4
  4. package/build/build/utils/repository.js +15 -3
  5. package/build/build/validateLockfile.d.ts +1 -0
  6. package/build/build/validateLockfile.js +37 -0
  7. package/build/commandUtils/buildFlags.d.ts +1 -0
  8. package/build/commandUtils/buildFlags.js +18 -0
  9. package/build/commandUtils/convex.d.ts +12 -0
  10. package/build/commandUtils/convex.js +77 -0
  11. package/build/commands/build/dev.d.ts +1 -0
  12. package/build/commands/build/dev.js +12 -2
  13. package/build/commands/build/resign.js +0 -1
  14. package/build/commands/build/run.d.ts +1 -0
  15. package/build/commands/build/run.js +12 -3
  16. package/build/commands/integrations/convex/connect.d.ts +24 -0
  17. package/build/commands/integrations/convex/connect.js +266 -0
  18. package/build/commands/integrations/convex/dashboard.d.ts +9 -0
  19. package/build/commands/integrations/convex/dashboard.js +42 -0
  20. package/build/commands/integrations/convex/project/delete.d.ts +13 -0
  21. package/build/commands/integrations/convex/project/delete.js +65 -0
  22. package/build/commands/integrations/convex/project.d.ts +9 -0
  23. package/build/commands/integrations/convex/project.js +28 -0
  24. package/build/commands/integrations/convex/team/delete.d.ts +17 -0
  25. package/build/commands/integrations/convex/team/delete.js +93 -0
  26. package/build/commands/integrations/convex/team/invite.d.ts +19 -0
  27. package/build/commands/integrations/convex/team/invite.js +113 -0
  28. package/build/commands/integrations/convex/team.d.ts +9 -0
  29. package/build/commands/integrations/convex/team.js +35 -0
  30. package/build/commands/observe/events.js +12 -24
  31. package/build/commands/observe/logs.d.ts +29 -0
  32. package/build/commands/observe/logs.js +163 -0
  33. package/build/commands/observe/metrics.js +11 -19
  34. package/build/commands/observe/versions.js +11 -19
  35. package/build/commands/simulator/start.js +84 -5
  36. package/build/commands/simulator/stop.js +1 -1
  37. package/build/fingerprint/cli.js +1 -0
  38. package/build/graphql/generated.d.ts +421 -7
  39. package/build/graphql/generated.js +16 -3
  40. package/build/graphql/mutations/ConvexMutation.d.ts +10 -0
  41. package/build/graphql/mutations/ConvexMutation.js +89 -0
  42. package/build/graphql/mutations/DeviceRunSessionMutation.d.ts +2 -2
  43. package/build/graphql/mutations/DeviceRunSessionMutation.js +4 -4
  44. package/build/graphql/queries/ConvexQuery.d.ts +6 -0
  45. package/build/graphql/queries/ConvexQuery.js +49 -0
  46. package/build/graphql/queries/ObserveQuery.d.ts +21 -1
  47. package/build/graphql/queries/ObserveQuery.js +80 -0
  48. package/build/graphql/types/ConvexTeamConnection.d.ts +11 -0
  49. package/build/graphql/types/ConvexTeamConnection.js +43 -0
  50. package/build/graphql/types/Observe.d.ts +1 -0
  51. package/build/graphql/types/Observe.js +26 -1
  52. package/build/observe/fetchCustomEvents.d.ts +19 -0
  53. package/build/observe/fetchCustomEvents.js +21 -0
  54. package/build/observe/formatCustomEvents.d.ts +70 -0
  55. package/build/observe/formatCustomEvents.js +140 -0
  56. package/build/observe/formatEvents.js +5 -34
  57. package/build/observe/formatMetrics.js +2 -7
  58. package/build/observe/formatUtils.d.ts +27 -0
  59. package/build/observe/formatUtils.js +64 -0
  60. package/build/observe/formatVersions.js +2 -9
  61. package/build/observe/platforms.d.ts +21 -0
  62. package/build/observe/platforms.js +48 -0
  63. package/build/observe/resolveProjectContext.d.ts +22 -0
  64. package/build/observe/resolveProjectContext.js +21 -0
  65. package/build/run/ios/run.d.ts +2 -1
  66. package/build/run/ios/run.js +6 -2
  67. package/build/run/ios/simulator.d.ts +4 -1
  68. package/build/run/ios/simulator.js +14 -2
  69. package/build/run/run.d.ts +2 -1
  70. package/build/run/run.js +2 -2
  71. package/oclif.manifest.json +3984 -3243
  72. package/package.json +4 -4
@@ -51,6 +51,7 @@ class SimulatorStart extends EasCommand_1.default {
51
51
  const platform = flags.platform === 'android' ? generated_1.AppPlatform.Android : generated_1.AppPlatform.Ios;
52
52
  const createSpinner = (0, ora_1.ora)('🚀 Creating device run session').start();
53
53
  let deviceRunSessionId;
54
+ let jobRunUrl;
54
55
  try {
55
56
  const session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.createDeviceRunSessionAsync(graphqlClient, {
56
57
  appId: projectId,
@@ -60,7 +61,7 @@ class SimulatorStart extends EasCommand_1.default {
60
61
  });
61
62
  deviceRunSessionId = session.id;
62
63
  const jobRunId = (0, nullthrows_1.default)(session.turtleJobRun?.id, 'Expected device run session to start');
63
- const jobRunUrl = (0, url_1.getBareJobRunUrl)(session.app.ownerAccount.name, session.app.slug, jobRunId);
64
+ jobRunUrl = (0, url_1.getBareJobRunUrl)(session.app.ownerAccount.name, session.app.slug, jobRunId);
64
65
  createSpinner.succeed(`Device run session created (id: ${deviceRunSessionId}) ${(0, log_1.link)(jobRunUrl)}`);
65
66
  }
66
67
  catch (err) {
@@ -76,13 +77,13 @@ class SimulatorStart extends EasCommand_1.default {
76
77
  const session = await DeviceRunSessionQuery_1.DeviceRunSessionQuery.byIdAsync(graphqlClient, deviceRunSessionId);
77
78
  if (session.status === generated_1.DeviceRunSessionStatus.Errored ||
78
79
  session.status === generated_1.DeviceRunSessionStatus.Stopped) {
79
- throw new Error(`Device run session ${deviceRunSessionId} ${session.status.toLowerCase()} before the ${flags.type} daemon was ready.`);
80
+ throw new Error(`Device run session ${deviceRunSessionId} ${session.status.toLowerCase()} before the ${flags.type} daemon was ready. ${(0, log_1.link)(jobRunUrl)}`);
80
81
  }
81
82
  const jobRunStatus = session.turtleJobRun?.status;
82
83
  if (jobRunStatus === generated_1.JobRunStatus.Errored ||
83
84
  jobRunStatus === generated_1.JobRunStatus.Canceled ||
84
85
  jobRunStatus === generated_1.JobRunStatus.Finished) {
85
- throw new Error(`Turtle job run for device run session ${deviceRunSessionId} ${jobRunStatus.toLowerCase()} before the ${flags.type} daemon was ready.`);
86
+ throw new Error(`Turtle job run for device run session ${deviceRunSessionId} ${jobRunStatus.toLowerCase()} before the ${flags.type} daemon was ready. ${(0, log_1.link)(jobRunUrl)}`);
86
87
  }
87
88
  const logMessages = await fetchLogMessagesAsync(session.turtleJobRun?.logFileUrls ?? []);
88
89
  result = checkReadiness(logMessages);
@@ -95,21 +96,99 @@ class SimulatorStart extends EasCommand_1.default {
95
96
  }
96
97
  catch (err) {
97
98
  pollSpinner.fail(`Failed while polling for ${flags.type} daemon logs`);
99
+ await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
98
100
  throw err;
99
101
  }
100
102
  if (!result.ready) {
101
103
  pollSpinner.fail(`Timed out waiting for ${flags.type} daemon to start`);
102
- throw new Error(`Timed out after ${Math.round(POLL_TIMEOUT_MS / 1000)}s waiting for ${flags.type} daemon to start.`);
104
+ await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
105
+ throw new Error(`Timed out after ${Math.round(POLL_TIMEOUT_MS / 1000)}s waiting for ${flags.type} daemon to start. ${(0, log_1.link)(jobRunUrl)}`);
103
106
  }
104
107
  log_1.default.newLine();
105
108
  log_1.default.log(`🔑 Run the following in your shell to attach to ${flags.type}:`);
106
109
  log_1.default.newLine();
107
110
  log_1.default.log(result.message);
108
111
  log_1.default.newLine();
109
- log_1.default.log(`When you are done, stop the session with: eas simulator:stop --id ${deviceRunSessionId}`);
112
+ if (flags['non-interactive']) {
113
+ log_1.default.log(`When you are done, stop the session with: eas simulator:stop --id ${deviceRunSessionId}`);
114
+ return;
115
+ }
116
+ await waitForSessionEndOrInterruptAsync({
117
+ graphqlClient,
118
+ deviceRunSessionId,
119
+ jobRunUrl,
120
+ });
110
121
  }
111
122
  }
112
123
  exports.default = SimulatorStart;
124
+ async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessionId, jobRunUrl, }) {
125
+ 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();
126
+ const abortController = new AbortController();
127
+ const { signal } = abortController;
128
+ const abortPromise = new Promise(resolve => {
129
+ signal.addEventListener('abort', () => {
130
+ resolve();
131
+ }, { once: true });
132
+ });
133
+ const sigintHandler = () => {
134
+ if (signal.aborted) {
135
+ // Force exit on a second Ctrl+C in case cleanup is hanging. The session may still be
136
+ // running on EAS, so tell the user how to make sure it gets terminated.
137
+ spinner.fail(`Aborted before the device run session could be stopped. Run \`eas simulator:stop --id ${deviceRunSessionId}\` to terminate it and avoid unexpected charges.`);
138
+ process.exit(130);
139
+ }
140
+ abortController.abort();
141
+ };
142
+ process.on('SIGINT', sigintHandler);
143
+ try {
144
+ while (!signal.aborted) {
145
+ let session;
146
+ try {
147
+ session = await DeviceRunSessionQuery_1.DeviceRunSessionQuery.byIdAsync(graphqlClient, deviceRunSessionId);
148
+ }
149
+ catch (err) {
150
+ log_1.default.debug(`Failed to poll device run session: ${err instanceof Error ? err.message : String(err)}`);
151
+ await Promise.race([(0, promise_1.sleepAsync)(POLL_INTERVAL_MS), abortPromise]);
152
+ continue;
153
+ }
154
+ const jobRunStatus = session.turtleJobRun?.status;
155
+ if (session.status === generated_1.DeviceRunSessionStatus.Errored ||
156
+ jobRunStatus === generated_1.JobRunStatus.Errored) {
157
+ spinner.fail(`Device run session errored. ${(0, log_1.link)(jobRunUrl)}`);
158
+ throw new Error(`Device run session ${deviceRunSessionId} errored.`);
159
+ }
160
+ if (session.status === generated_1.DeviceRunSessionStatus.Stopped ||
161
+ jobRunStatus === generated_1.JobRunStatus.Canceled ||
162
+ jobRunStatus === generated_1.JobRunStatus.Finished) {
163
+ spinner.succeed(`Device run session ended. ${(0, log_1.link)(jobRunUrl)}`);
164
+ return;
165
+ }
166
+ await Promise.race([(0, promise_1.sleepAsync)(POLL_INTERVAL_MS), abortPromise]);
167
+ }
168
+ spinner.text = 'Stopping device run session...';
169
+ const stopped = await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
170
+ if (stopped) {
171
+ spinner.succeed('Device run session stopped');
172
+ }
173
+ else {
174
+ spinner.fail(`Could not confirm the device run session was stopped. Run \`eas simulator:stop --id ${deviceRunSessionId}\` to terminate it and avoid unexpected charges.`);
175
+ }
176
+ }
177
+ finally {
178
+ process.removeListener('SIGINT', sigintHandler);
179
+ }
180
+ }
181
+ async function ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId) {
182
+ try {
183
+ await DeviceRunSessionMutation_1.DeviceRunSessionMutation.ensureDeviceRunSessionStoppedAsync(graphqlClient, deviceRunSessionId);
184
+ return true;
185
+ }
186
+ catch (err) {
187
+ // Cleanup is best-effort; surface the failure but don't mask the original error.
188
+ log_1.default.warn(`Failed to stop device run session ${deviceRunSessionId}: ${err instanceof Error ? err.message : String(err)}`);
189
+ return false;
190
+ }
191
+ }
113
192
  function getReadinessCheckerForType(type) {
114
193
  switch (type) {
115
194
  case DEVICE_RUN_SESSION_TYPE_FLAG_VALUES[generated_1.DeviceRunSessionType.AgentDevice]:
@@ -26,7 +26,7 @@ class SimulatorStop extends EasCommand_1.default {
26
26
  });
27
27
  const stopSpinner = (0, ora_1.ora)(`🛑 Stopping device run session ${flags.id}`).start();
28
28
  try {
29
- const session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.stopDeviceRunSessionAsync(graphqlClient, flags.id);
29
+ const session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.ensureDeviceRunSessionStoppedAsync(graphqlClient, flags.id);
30
30
  stopSpinner.succeed(`🎉 Device run session ${session.id} is ${session.status.toLowerCase()}`);
31
31
  }
32
32
  catch (err) {
@@ -69,6 +69,7 @@ async function createFingerprintWithoutLoggingAsync(projectDir, fingerprintPath,
69
69
  if (options.debug) {
70
70
  fingerprintOptions.debug = true;
71
71
  }
72
+ fingerprintOptions.silent = true;
72
73
  return await withTemporaryEnvAsync(options.env ?? {}, () => Fingerprint.createFingerprintAsync(projectDir, fingerprintOptions));
73
74
  }
74
75
  async function withTemporaryEnvAsync(envVars, fn) {