stim 1.3.1 → 1.5.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 (67) hide show
  1. package/README.md +16 -3
  2. package/dist/{android-hMj83VpN.mjs → android-DJWiTBkz.mjs} +65 -72
  3. package/dist/{android-cas-DXorPCi_.mjs → android-cas-CSS8gjR7.mjs} +15 -12
  4. package/dist/{android-CT-PkXUh.mjs → android-sobvO9ia.mjs} +197 -114
  5. package/dist/{app-install-DIp9riNx.mjs → app-install--PAocWvq.mjs} +2 -2
  6. package/dist/{build-slots-C4AouyNz.mjs → build-slots-CuPszJDu.mjs} +7 -8
  7. package/dist/{cache-manifest-CXR-Y8pH.mjs → cache-manifest-uoOKhgUD.mjs} +1 -1
  8. package/dist/cache-manifest.mjs +1 -1
  9. package/dist/cli.mjs +14 -13
  10. package/dist/collector-run.mjs +4 -4
  11. package/dist/{command-output-8wHMEde0.mjs → command-output-BuIb_ZOI.mjs} +1 -4
  12. package/dist/{config-CcefMY2Q.mjs → config-DMP7L7H6.mjs} +52 -52
  13. package/dist/dependency-state-CTL8A9p3.mjs +76 -0
  14. package/dist/{deps-0Yt0Inly.mjs → deps-BAGOnrc7.mjs} +1 -1
  15. package/dist/{dev-client-VfSZNLT4.mjs → dev-client-BoifdqkU.mjs} +12 -12
  16. package/dist/{device-Bbv45Z9Z.mjs → device-BUw4JBuH.mjs} +7 -7
  17. package/dist/{xcode-xnxyvJkD.mjs → device-DvKJ454A.mjs} +208 -967
  18. package/dist/{device-lease-CJwK7OZr.mjs → device-lease-r7yEQ1R8.mjs} +3 -3
  19. package/dist/{device-pool-CcBNTvM7.mjs → device-pool-DU4FQKp5.mjs} +4 -4
  20. package/dist/{device-remote-DAA0Zm6Z.mjs → device-remote-BTUqbl0i.mjs} +7 -6
  21. package/dist/{doctor-B4EITgQB.mjs → doctor-Di_B26uh.mjs} +396 -117
  22. package/dist/{error-diagnostics-DwhNBFDU.mjs → error-diagnostics-CTePWAzW.mjs} +7 -6
  23. package/dist/{gc-BRu62v4o.mjs → gc-C20dWw_8.mjs} +61 -27
  24. package/dist/{guide-D95E0u3i.mjs → guide-sAnoty2y.mjs} +151 -20
  25. package/dist/guide-status-BF6RsJJp.mjs +271 -0
  26. package/dist/{ios-DAju3TVy.mjs → ios-BdpkncL6.mjs} +2667 -2607
  27. package/dist/{ios-device-CWOM_S44.mjs → ios-device-DJOi9FR5.mjs} +1 -1
  28. package/dist/{logs-CAdwW6c2.mjs → logs-BfQdIhiF.mjs} +5 -5
  29. package/dist/metro-ato6ef3J.mjs +99 -0
  30. package/dist/named-ports-DH9SXd0f.mjs +137 -0
  31. package/dist/{ownership-8O073GIC.mjs → ownership-CD_XnpDu.mjs} +5 -5
  32. package/dist/{ownership-DXcol4oU.mjs → ownership-CVY9cqCr.mjs} +6 -6
  33. package/dist/ports-A6uTJSPj.mjs +39 -0
  34. package/dist/ports-BZprTJRa.mjs +90 -0
  35. package/dist/{project-fKmuIaMI.mjs → project-B_SDt4ip.mjs} +2 -2
  36. package/dist/{reclaim-CUmwucWG.mjs → reclaim-C_rB25f4.mjs} +20 -13
  37. package/dist/{reload-CWpXoOgN.mjs → reload-CYBJbJfI.mjs} +8 -8
  38. package/dist/rolldown-runtime-BsOwBuO_.mjs +30 -0
  39. package/dist/{server-bare-DEzPZLvf.mjs → server-bare-H3pAhbMs.mjs} +11 -35
  40. package/dist/server-expo-CSwUVYOa.mjs +2 -0
  41. package/dist/{server-expo-DizT4OMk.mjs → server-expo-Ds8_mfng.mjs} +6 -14
  42. package/dist/{settings-DEJf6_kJ.mjs → settings-kO2ZqIKy.mjs} +2 -2
  43. package/dist/{slot-launch-CZklomj6.mjs → slot-launch-BpHiHH6A.mjs} +4 -4
  44. package/dist/{start-B5G4NjiA.mjs → start-DiEfqEWk.mjs} +20 -22
  45. package/dist/state-CnWzvCWP.mjs +131 -0
  46. package/dist/{stats-CaaQr4yN.mjs → stats-DHrO0frH.mjs} +4 -4
  47. package/dist/{stats-BR-kwj0B.mjs → stats-Fo9ACIv7.mjs} +1 -1
  48. package/dist/{status-CI9clRSb.mjs → status-J5LAXpag.mjs} +15 -12
  49. package/dist/{stop-CjlgDk0a.mjs → stop-0jJNahwN.mjs} +15 -14
  50. package/dist/{stop-CQhffpmX.mjs → stop-CM1fejKW.mjs} +2 -2
  51. package/dist/supervisor-run.d.mts +7 -28
  52. package/dist/supervisor-run.mjs +20 -11
  53. package/dist/{teardown--gESs9ck.mjs → teardown-CAVcWeRD.mjs} +148 -109
  54. package/dist/{workspace-process-lock-IBj1Towr.mjs → workspace-process-lock-Bz19_R9R.mjs} +4 -4
  55. package/dist/workspace-state-WVwWw_hB.mjs +146 -0
  56. package/dist/{worktree-DtQGO9-s.mjs → worktree-CeUKDAQw.mjs} +38 -34
  57. package/dist/{worktree-Eq6TLH8N.mjs → worktree-DJnBbQZ9.mjs} +79 -22
  58. package/dist/xcode-BqDeRkpT.mjs +860 -0
  59. package/package.json +7 -7
  60. package/shim/expo-metro-config.cjs +11 -17
  61. package/dist/doctor-BueDfLYG.mjs +0 -530
  62. package/dist/metro-CORqXyTL.mjs +0 -184
  63. package/dist/ownership-claim-BtPtJVmq.mjs +0 -584
  64. package/dist/server-expo-44em5Pmg.mjs +0 -2
  65. package/dist/state-B3XA2wp2.mjs +0 -268
  66. package/shim/metro-cache-generation.cjs +0 -20
  67. package/shim/metro-cache-generation.d.cts +0 -5
@@ -1,21 +1,20 @@
1
1
  import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { C as withConfigLock, E as inspectProcessIdentity, L as deviceSlotPlatforms, _ as releaseAndroidConsolePort, b as setDevice, g as loadConfig, i as clearDevice, t as allConsolePortsAndSerials, u as getConfigDir, w as captureProcessIdentity, y as saveConfig, z as projectDeviceSlots } from "./config-CcefMY2Q.mjs";
3
- import { r as register } from "./cache-manifest-CXR-Y8pH.mjs";
4
- import { E as workspaceId, S as sharedCompilationCache, w as workspaceDerivedData } from "./state-B3XA2wp2.mjs";
5
- import { s as ownedDeviceLabel } from "./project-fKmuIaMI.mjs";
2
+ import { C as withConfigLock, E as ownership_claim_exports, F as deviceSlotPlatforms, L as projectDeviceSlots, T as acquireAvdClaim, _ as releaseAndroidConsolePort, b as setDevice, g as loadConfig, i as clearDevice, t as allConsolePortsAndSerials, u as getConfigDir, w as process_identity_exports, y as saveConfig } from "./config-DMP7L7H6.mjs";
3
+ import { g as workspaceId } from "./workspace-state-WVwWw_hB.mjs";
4
+ import { s as ownedDeviceLabel } from "./project-B_SDt4ip.mjs";
6
5
  import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-C7IlIzd7.mjs";
7
- import { r as pidExists } from "./metro-CORqXyTL.mjs";
8
- import { a as formatElapsed, l as phaseLine } from "./command-output-8wHMEde0.mjs";
9
- import { c as markClaimChildPending, d as releaseClaim, f as setClaimChild, i as claimRemoveCommand, l as processGroupAlive, m as tryAcquireClaim, t as ClaimRefusedError } from "./ownership-claim-BtPtJVmq.mjs";
10
- import { A as pickDefaultSystemImage, D as ownedAvdName, E as ownedAvdMatchesConfiguration, L as waitForBoot, N as resolveOwnedAvdSerial, O as ownedAvdSystemImage, b as listAvds, c as avdPoolConfiguration, d as configureNewOwnedAvd, f as createOwnedAvd, o as assertOwnedAvdStopped, u as bootAndroidEmulator, v as getAvdNameForSerial, w as nextConsolePort, x as listInstalledSystemImages, y as listAdbDevices } from "./android-hMj83VpN.mjs";
11
- import { U as resolveOptimizations, l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting, v as iosSimSlimProfileSetting } from "./settings-DEJf6_kJ.mjs";
12
- import { t as cleanLine } from "./server-expo-DizT4OMk.mjs";
13
- import { c as parkedMaxSetting, i as teardownParkedIosSim, l as readParked, o as adoptParked, r as teardownParkedAvd, s as dropParked, t as teardownOwnedAvd, u as selectParked } from "./teardown--gESs9ck.mjs";
14
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-IBj1Towr.mjs";
6
+ import { r as pidExists } from "./metro-ato6ef3J.mjs";
7
+ import { l as phaseLine } from "./command-output-BuIb_ZOI.mjs";
8
+ import { A as pickDefaultSystemImage, D as ownedAvdName, E as ownedAvdMatchesConfiguration, L as waitForBoot, N as resolveOwnedAvdSerial, O as ownedAvdSystemImage, b as listAvds, c as avdPoolConfiguration, d as configureNewOwnedAvd, f as createOwnedAvd, o as assertOwnedAvdStopped, u as bootAndroidEmulator, v as getAvdNameForSerial, w as nextConsolePort, x as listInstalledSystemImages, y as listAdbDevices } from "./android-DJWiTBkz.mjs";
9
+ import { l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting, v as iosSimSlimProfileSetting } from "./settings-kO2ZqIKy.mjs";
10
+ import { c as isLegacyDeletionClaim, d as selectParked, i as teardownParkedIosSim, l as parkedMaxSetting, o as adoptParked, r as teardownParkedAvd, s as dropParked, t as teardownOwnedAvd, u as readParked } from "./teardown-CAVcWeRD.mjs";
11
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-Bz19_R9R.mjs";
15
12
  import { E as readHostMemoryPressure, T as hostMemoryPressureAdvice, _ as renameIosSim, b as resolveIosCreation, c as iosRuntimeMatches, d as listIosDeviceTypes, g as parseRuntimeVersion, i as createOwnedIosSim, l as iosSimulatorFailureAdvice, m as ownedSimName, n as bootIosSim, u as listAllIosSims, v as resetIosKeychain, x as resolveOwnedIosSim, y as resetIosPrivacy } from "./ios-Deh5js5V.mjs";
16
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
17
- import { createHash, randomUUID } from "node:crypto";
18
- import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
13
+ import { randomUUID } from "node:crypto";
14
+ import { join } from "node:path";
15
+ import { claimRemoveCommand, clearClaimChild, markClaimChildPending, processGroupAlive, releaseClaim, setClaimChild } from "@stim-cli/core/ownership-claim";
16
+ import { captureProcessIdentity } from "@stim-cli/core/process-identity";
17
+ import { isDeepStrictEqual } from "node:util";
19
18
  import chalk from "chalk";
20
19
  //#region src/engine/simslim.ts
21
20
  async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out = () => {}, spawn, timeoutMs = 72e4, cleanupMs = 1e4 }) {
@@ -42,14 +41,14 @@ async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out
42
41
  const stderr = createLineReader(onLine);
43
42
  const root = join(getConfigDir(), "simslim-locks", `${encodeURIComponent(udid.toLowerCase())}.lock`);
44
43
  const label = `SimSlim for ${udid}`;
45
- const attempt = tryAcquireClaim({
44
+ const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
46
45
  root,
47
46
  mode: "exclusive",
48
47
  label
49
48
  });
50
- if (attempt.pending) releaseClaim(attempt.pending);
49
+ if (attempt.pending) (0, ownership_claim_exports.releaseClaim)(attempt.pending);
51
50
  const claim = attempt.acquired;
52
- if (!claim) throw new ClaimRefusedError({
51
+ if (!claim) throw new ownership_claim_exports.ClaimRefusedError({
53
52
  root,
54
53
  claimPath: attempt.held?.path ?? root,
55
54
  label,
@@ -60,7 +59,7 @@ async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out
60
59
  let identity;
61
60
  let cancellation;
62
61
  const stopGroup = () => {
63
- if (child?.pid !== void 0 && identity && inspectProcessIdentity(identity) === "same") try {
62
+ if (child?.pid !== void 0 && identity && (0, process_identity_exports.inspectProcessIdentity)(identity) === "same") try {
64
63
  process.kill(-child.pid, "SIGKILL");
65
64
  } catch {}
66
65
  };
@@ -70,7 +69,7 @@ async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out
70
69
  };
71
70
  const onInterrupt = () => cancel("SIGINT");
72
71
  const onTerminate = () => cancel("SIGTERM");
73
- const groupAlive = () => child?.pid !== void 0 && processGroupAlive(child.pid);
72
+ const groupAlive = () => child?.pid !== void 0 && (0, ownership_claim_exports.processGroupAlive)(child.pid);
74
73
  const settled = () => result !== void 0 && !groupAlive();
75
74
  const waitUntil = async (deadline, cancellable = false) => {
76
75
  while (!settled() && Date.now() < deadline) {
@@ -83,7 +82,7 @@ async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out
83
82
  process.on("SIGTERM", onTerminate);
84
83
  process.on("exit", stopGroup);
85
84
  try {
86
- markClaimChildPending(claim);
85
+ (0, ownership_claim_exports.markClaimChildPending)(claim);
87
86
  try {
88
87
  child = (spawn ?? ((cmd, childArgs, opts) => getExecutor().spawn(cmd, childArgs, opts)))("simslim", args, {
89
88
  stdio: [
@@ -102,14 +101,14 @@ async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out
102
101
  result = value;
103
102
  });
104
103
  if (child.pid !== void 0) {
105
- const captured = captureProcessIdentity(child.pid);
104
+ const captured = (0, process_identity_exports.captureProcessIdentity)(child.pid);
106
105
  if (captured.ok) {
107
106
  identity = {
108
107
  pid: child.pid,
109
108
  processToken: captured.token
110
109
  };
111
110
  try {
112
- setClaimChild(claim, identity);
111
+ (0, ownership_claim_exports.setClaimChild)(claim, identity);
113
112
  } catch {}
114
113
  }
115
114
  }
@@ -118,13 +117,13 @@ async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out
118
117
  stopGroup();
119
118
  const stopped = await waitUntil(Date.now() + cleanupMs);
120
119
  const detail = lines.length ? ` ${lines.join(" | ")}` : "";
121
- const recovery = stopped ? "Its process group has stopped. Retry stim ios to reconcile the retained simulator settings." : `Its process group could not be confirmed stopped. The claim remains at ${claim.path}. Inspect the SimSlim process group ${child.pid ?? "unknown"} before retrying. Only when nothing is using it, remove the claim: ${claimRemoveCommand(claim.path)}`;
120
+ const recovery = stopped ? "Its process group has stopped. Retry stim ios to reconcile the retained simulator settings." : `Its process group could not be confirmed stopped. The claim remains at ${claim.path}. Inspect the SimSlim process group ${child.pid ?? "unknown"} before retrying. Only when nothing is using it, remove the claim: ${(0, ownership_claim_exports.claimRemoveCommand)(claim.path)}`;
122
121
  const message = `SimSlim ${action} ${cancellation ? `interrupted by ${cancellation}` : `exceeded ${Math.round(timeoutMs / 1e3)}s`}. ${recovery}${detail}`;
123
122
  if (cancellation) out(message);
124
123
  throw Object.assign(new Error(message), { code: cancellation ? "EINTR" : "ETIMEDOUT" });
125
124
  }
126
125
  } finally {
127
- if (!child || settled()) releaseClaim(claim);
126
+ if (!child || settled()) (0, ownership_claim_exports.releaseClaim)(claim);
128
127
  else {
129
128
  child.stdout?.destroy?.();
130
129
  child.stderr?.destroy?.();
@@ -160,6 +159,157 @@ function simslimIsOnPath() {
160
159
  }
161
160
  }
162
161
  //#endregion
162
+ //#region src/engine/android-avd-setup.ts
163
+ var AvdBootError = class extends Error {
164
+ remedy;
165
+ constructor(message, remedy, cause) {
166
+ super(message, { cause });
167
+ this.remedy = remedy;
168
+ }
169
+ };
170
+ var AvdRecoveryError = class extends AvdBootError {};
171
+ function findOtherOwner(avdName, projectPath, selectedSlot = "default") {
172
+ for (const [path, project] of Object.entries(loadConfig()?.projects ?? {})) if (projectDeviceSlots(project).some(({ slot, platforms }) => (path !== projectPath || slot !== selectedSlot) && platforms.android?.avdName?.toLowerCase() === avdName.toLowerCase())) return path;
173
+ return null;
174
+ }
175
+ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, systemImage, configuration, configure, teardown = teardownOwnedAvd }) {
176
+ const { claim, reservation, previous, avdName } = withConfigLock(() => {
177
+ const previous = deviceSlotPlatforms(loadConfig()?.projects?.[projectPath], slot)?.android;
178
+ if (previous?.avdName && previous.avdName !== previousAvdName) throw new Error(`Another Stim run assigned AVD ${previous.avdName} to this workspace. Retry to use it.`);
179
+ if (readParked("android").some((entry) => entry.name === ownedAvdName(label)) || findOtherOwner(ownedAvdName(label), projectPath, slot)) label = `${label}-${randomUUID().slice(0, 8)}`;
180
+ const avdName = ownedAvdName(label);
181
+ const claim = acquireAvdClaim(avdName);
182
+ const reservation = {
183
+ avdName,
184
+ owned: true,
185
+ deviceName: avdName,
186
+ setupIncomplete: true,
187
+ poolConfiguration: configuration
188
+ };
189
+ try {
190
+ setDevice(projectPath, "android", reservation, slot);
191
+ return {
192
+ claim,
193
+ reservation,
194
+ previous,
195
+ avdName
196
+ };
197
+ } catch (error) {
198
+ releaseClaim(claim);
199
+ throw error;
200
+ }
201
+ });
202
+ const currentRecord = () => deviceSlotPlatforms(loadConfig()?.projects?.[projectPath], slot)?.android;
203
+ const assertReservation = () => {
204
+ if (readParked("android").some((entry) => entry.name === avdName)) throw new Error(`AVD ${avdName} was parked by another Stim run. Retry to adopt it safely.`);
205
+ const owner = findOtherOwner(avdName, projectPath, slot);
206
+ if (owner) throw new Error(`AVD ${avdName} already exists and is owned by another project (${owner}). Retry to allocate a distinct owned emulator.`);
207
+ const current = currentRecord();
208
+ if (!isDeepStrictEqual(current, reservation)) {
209
+ const state = current?.setupIncomplete ? "has incomplete setup" : "was registered or removed";
210
+ throw new Error(`AVD ${current?.avdName ?? avdName} ${state} by another concurrent Stim run. Retry after that run finishes so the recorded device is resolved safely.`);
211
+ }
212
+ };
213
+ let prepared;
214
+ let child;
215
+ const creationRunning = () => child?.pid !== void 0 && processGroupAlive(child.pid);
216
+ let configurationFailure;
217
+ try {
218
+ try {
219
+ const creation = await createOwnedAvd(label, {
220
+ systemImage,
221
+ spawn: (...args) => {
222
+ markClaimChildPending(claim);
223
+ child = getExecutor().spawn(...args);
224
+ if (child.pid !== void 0) {
225
+ const captured = captureProcessIdentity(child.pid);
226
+ if (captured.ok) try {
227
+ setClaimChild(claim, {
228
+ pid: child.pid,
229
+ processToken: captured.token
230
+ });
231
+ } catch {}
232
+ }
233
+ return child;
234
+ }
235
+ }).then((result) => ({ result }), (error) => ({ error }));
236
+ if (creationRunning()) throw new AvdRecoveryError(`AVD ${avdName} creation left its process group running (pid ${child.pid}); its reservation remains incomplete.`, `Wait for process group ${child.pid} to finish before retrying stim android. If its identity could not be recorded, inspect the group and remove the claim only after it stops: ${claimRemoveCommand(claim.path)}`);
237
+ clearClaimChild(claim);
238
+ if ("error" in creation) throw creation.error;
239
+ prepared = {
240
+ ...creation.result,
241
+ created: true
242
+ };
243
+ } catch (error) {
244
+ const message = String(error?.message || error);
245
+ if (!message.includes("already exists")) throw error;
246
+ try {
247
+ if (!listAvds().includes(avdName)) throw new AvdRecoveryError(`AVD ${avdName} already exists on disk but is not listed by the emulator. ${message}`, "Run `npx stim gc` to inspect orphaned owned AVDs, then `npx stim gc --delete` to reclaim those safe to delete. Retry `stim android` after cleanup; keep any AVD that GC cannot verify.", error);
248
+ withConfigLock(assertReservation);
249
+ const resolved = resolveOwnedAvdSerial(avdName);
250
+ if (resolved.missing || resolved.notOwned) throw new Error(`AVD ${avdName} could not be verified for recovery. Retry after checking its registration.`, { cause: error });
251
+ if (!resolved.serial) assertOwnedAvdStopped(avdName);
252
+ prepared = {
253
+ avdName,
254
+ created: false,
255
+ systemImage: ownedAvdSystemImage(avdName),
256
+ ...resolved.serial ? {
257
+ serial: resolved.serial,
258
+ consolePort: Number(resolved.serial.replace(/^emulator-/, ""))
259
+ } : {}
260
+ };
261
+ } catch (recoveryError) {
262
+ withConfigLock(() => {
263
+ if (!isDeepStrictEqual(currentRecord(), reservation)) return;
264
+ if (previous) setDevice(projectPath, "android", previous, slot);
265
+ else clearDevice(projectPath, "android", slot, avdName);
266
+ });
267
+ if (recoveryError instanceof AvdRecoveryError) throw recoveryError;
268
+ throw new AvdRecoveryError(`Could not recover owned AVD ${avdName}: ${String(recoveryError?.message || recoveryError)}`, "Inspect `npx stim status` and `adb devices`. Wait for any other Stim run using this AVD to finish, then retry `stim android`. Keep the AVD and its process locks while its state is unverified.", recoveryError);
269
+ }
270
+ }
271
+ if (prepared.created) try {
272
+ configure(avdName);
273
+ } catch (error) {
274
+ configurationFailure = { error };
275
+ }
276
+ if (!configurationFailure) withConfigLock(() => {
277
+ assertReservation();
278
+ setDevice(projectPath, "android", {
279
+ avdName,
280
+ owned: true,
281
+ deviceName: avdName,
282
+ ...prepared.created ? { poolConfiguration: configuration } : {},
283
+ ...prepared.consolePort ? { consolePort: prepared.consolePort } : {}
284
+ }, slot);
285
+ });
286
+ } finally {
287
+ if (!creationRunning()) releaseClaim(claim);
288
+ else {
289
+ child?.stdout?.destroy();
290
+ child?.stderr?.destroy();
291
+ child?.unref();
292
+ }
293
+ }
294
+ if (configurationFailure) {
295
+ const { error } = configurationFailure;
296
+ const cleanup = teardown(avdName, {
297
+ del: true,
298
+ owner: {
299
+ projectPath,
300
+ slot,
301
+ expectedRecord: reservation
302
+ },
303
+ onRemoved: () => {
304
+ clearDevice(projectPath, "android", slot, avdName);
305
+ }
306
+ });
307
+ const orphan = cleanup.status === "failed" || cleanup.status === "skipped" ? ` The owned AVD remains tracked for cleanup (${cleanup.reason || cleanup.status}); fix the cause, then retry or run \`stim gc --delete\`.` : "";
308
+ throw new Error(`Created owned AVD ${avdName}, but could not configure its AVD settings: ${String(error?.message || error)}${orphan}`, { cause: error });
309
+ }
310
+ return prepared;
311
+ }
312
+ //#endregion
163
313
  //#region src/engine/device.ts
164
314
  function startIosBoot(udid, configure, label, out) {
165
315
  const done = (async () => {
@@ -455,29 +605,23 @@ async function configureOwnedIosSim({ record, projectPath, slot, profile, out, r
455
605
  setDevice(projectPath, "ios", updated, slot);
456
606
  return updated;
457
607
  }
458
- function findOtherProjectOwningAvd(avdName, projectPath, selectedSlot = "default") {
459
- const cfg = loadConfig();
460
- for (const [path, proj] of Object.entries(cfg?.projects || {})) if (projectDeviceSlots(proj).some(({ slot, platforms }) => (path !== projectPath || slot !== selectedSlot) && platforms.android?.avdName?.toLowerCase() === avdName.toLowerCase())) return path;
461
- return null;
462
- }
463
- var AvdBootError = class extends Error {
464
- remedy;
465
- constructor(message, remedy, cause) {
466
- super(message, { cause });
467
- this.remedy = remedy;
468
- }
469
- };
470
- var AvdRecoveryError = class extends AvdBootError {};
471
608
  async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoot, label, settings, flags, note, out, logFile, alive, configureAvd, teardownAvd }) {
472
609
  const avdConfig = androidAvdConfigSetting(settings, settingsRoot);
473
610
  const configuration = avdPoolConfiguration(androidDataPartitionSizeGbSetting(settings), avdConfig);
474
611
  if (record?.setupIncomplete && record.avdName) {
475
- const cleanup = teardownAvd(record.avdName, {
612
+ const avdName = record.avdName;
613
+ const cleanup = teardownAvd(avdName, {
476
614
  del: true,
477
- owner: { projectPath }
615
+ owner: {
616
+ projectPath,
617
+ slot,
618
+ expectedRecord: record
619
+ },
620
+ onRemoved: () => {
621
+ clearDevice(projectPath, "android", slot, avdName);
622
+ }
478
623
  });
479
- if (cleanup.status === "failed" || cleanup.status === "skipped") throw new Error(`Owned AVD ${record.avdName} has incomplete setup and could not be deleted (${cleanup.reason || cleanup.status}). Fix the cause, then retry; Stim kept the device record for cleanup.`);
480
- clearDevice(projectPath, "android", slot);
624
+ if (cleanup.status === "failed" || cleanup.status === "skipped") throw new AvdRecoveryError(`Owned AVD ${record.avdName} has incomplete setup and could not be deleted (${cleanup.reason || cleanup.status}). Fix the cause, then retry; Stim kept the device record for cleanup.`, "Wait for any active AVD operation to finish. If the refusal names an unresolved claim, inspect its creator and native processes before removing only that claim. Retry `stim android` to reconcile the incomplete device.");
481
625
  record = null;
482
626
  }
483
627
  if (record?.avdName) {
@@ -530,7 +674,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
530
674
  const systemImage = pickDefaultSystemImage(listInstalledSystemImages(), { systemImage: flags.systemImage || settings.android?.systemImage })?.pkg;
531
675
  const candidates = readParked("android").filter((entry) => entry.systemImage === systemImage && entry.configuration === configuration).toSorted((a, b) => a.parkedAt.localeCompare(b.parkedAt));
532
676
  for (const parked of candidates) {
533
- if (parked.deletionClaim !== void 0) continue;
677
+ if (isLegacyDeletionClaim(parked.deletionClaim)) continue;
534
678
  const resolved = resolveOwnedAvdSerial(parked.name);
535
679
  if (resolved.missing) {
536
680
  const result = teardownParkedAvd(parked.name);
@@ -578,87 +722,31 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
578
722
  };
579
723
  }
580
724
  }
581
- let created;
582
- let fresh = false;
583
- try {
584
- created = withConfigLock(() => {
585
- const current = deviceSlotPlatforms(loadConfig()?.projects?.[projectPath], slot)?.android;
586
- if (current?.avdName && current.avdName !== record?.avdName) throw new Error(`Another Stim run assigned AVD ${current.avdName} to this workspace. Retry to use it.`);
587
- if (readParked("android").some((entry) => entry.name === ownedAvdName(label)) || findOtherProjectOwningAvd(ownedAvdName(label), projectPath, slot)) label = `${label}-${randomUUID().slice(0, 8)}`;
588
- const result = createOwnedAvd(label, { systemImage: flags.systemImage || settings.android?.systemImage });
589
- setDevice(projectPath, "android", {
590
- avdName: result.avdName,
591
- owned: true,
592
- deviceName: result.avdName,
593
- setupIncomplete: true,
594
- poolConfiguration: configuration
595
- }, slot);
596
- return result;
597
- });
598
- fresh = true;
599
- } catch (e) {
600
- const message = String(e?.message || e);
601
- const avdName = ownedAvdName(label);
602
- if (message.includes("already exists")) {
603
- try {
604
- if (!listAvds().includes(avdName)) throw new AvdRecoveryError(`AVD ${avdName} already exists on disk but is not listed by the emulator. ${message}`, "Run `npx stim gc` to inspect orphaned owned AVDs, then `npx stim gc --delete` to reclaim those safe to delete. Retry `stim android` after cleanup; keep any AVD that GC cannot verify.", e);
605
- created = withConfigLock(() => {
606
- if (readParked("android").some((entry) => entry.name === avdName)) throw new Error(`AVD ${avdName} was parked by another Stim run. Retry to adopt it safely.`, { cause: e });
607
- const owner = findOtherProjectOwningAvd(avdName, projectPath, slot);
608
- if (owner) throw new Error(`AVD ${avdName} already exists and is owned by another project (${owner}). Retry to allocate a distinct owned emulator.`, { cause: e });
609
- const current = deviceSlotPlatforms(loadConfig()?.projects?.[projectPath], slot)?.android;
610
- if (current?.avdName) {
611
- const state = current.setupIncomplete ? "has incomplete setup" : "was registered";
612
- throw new Error(`AVD ${current.avdName} ${state} by another concurrent Stim run. Retry after that run finishes so the recorded device is resolved safely.`, { cause: e });
613
- }
614
- const resolved = resolveOwnedAvdSerial(avdName);
615
- if (resolved.missing || resolved.notOwned) throw new Error(`AVD ${avdName} could not be verified for recovery. Retry after checking its registration.`, { cause: e });
616
- if (!resolved.serial) assertOwnedAvdStopped(avdName);
617
- const recovered = {
618
- avdName,
619
- owned: true,
620
- deviceName: avdName,
621
- ...resolved.serial ? { consolePort: Number(resolved.serial.replace(/^emulator-/, "")) } : {}
622
- };
623
- setDevice(projectPath, "android", recovered, slot);
624
- return {
625
- ...recovered,
626
- systemImage: ownedAvdSystemImage(avdName),
627
- serial: resolved.serial
628
- };
629
- });
630
- } catch (error) {
631
- if (error instanceof AvdRecoveryError) throw error;
632
- throw new AvdRecoveryError(`Could not recover owned AVD ${avdName}: ${String(error?.message || error)}`, "Inspect `npx stim status` and `adb devices`. Wait for any other Stim run using this AVD to finish, then retry `stim android`. Keep the AVD and its process locks while its state is unverified.", error);
633
- }
634
- out(chalk.dim(phaseLine("device", `recovered ${avdName} (unrecorded from a prior run)`)));
635
- if (created.serial) return {
636
- ...created,
637
- owned: true,
638
- deviceName: avdName,
639
- created: false
640
- };
641
- } else throw e;
642
- }
643
- if (fresh) try {
644
- configureAvd(created.avdName, {
725
+ const created = await prepareOwnedAvd({
726
+ projectPath,
727
+ slot,
728
+ label,
729
+ previousAvdName: record?.avdName,
730
+ systemImage: flags.systemImage || settings.android?.systemImage || void 0,
731
+ configuration,
732
+ configure: (avdName) => configureAvd(avdName, {
645
733
  dataPartitionSizeGb: androidDataPartitionSizeGbSetting(settings),
646
734
  avdConfig
647
- });
648
- } catch (error) {
649
- const cleanup = teardownAvd(created.avdName, {
650
- del: true,
651
- owner: { projectPath }
652
- });
653
- const kept = cleanup.status === "failed" || cleanup.status === "skipped";
654
- if (!kept) clearDevice(projectPath, "android", slot);
655
- const orphan = kept ? ` The owned AVD remains tracked for cleanup (${cleanup.reason || cleanup.status}); fix the cause, then retry or run \`stim gc --delete\`.` : "";
656
- throw new Error(`Created owned AVD ${created.avdName}, but could not configure its AVD settings: ${String(error?.message || error)}${orphan}`, { cause: error });
735
+ }),
736
+ teardown: teardownAvd
737
+ });
738
+ if (!created.created) {
739
+ out(chalk.dim(phaseLine("device", `recovered ${created.avdName} (unrecorded from a prior run)`)));
740
+ if (created.serial) return {
741
+ ...created,
742
+ owned: true,
743
+ deviceName: created.avdName
744
+ };
657
745
  }
658
746
  return {
659
747
  ...await bootOwnedAvdOnFreshPort({
660
748
  avdName: created.avdName,
661
- metadata: fresh ? { poolConfiguration: configuration } : void 0,
749
+ metadata: created.created ? { poolConfiguration: configuration } : void 0,
662
750
  projectPath,
663
751
  slot,
664
752
  deviceName: created.avdName,
@@ -666,7 +754,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
666
754
  logFile,
667
755
  alive
668
756
  }),
669
- created: fresh,
757
+ created: created.created,
670
758
  systemImage: created.systemImage
671
759
  };
672
760
  }
@@ -1045,851 +1133,4 @@ function sleep(ms) {
1045
1133
  return new Promise((r) => setTimeout(r, ms));
1046
1134
  }
1047
1135
  //#endregion
1048
- //#region src/engine/bundler.ts
1049
- const COCOAPODS_SPEC = /^ {4}cocoapods \(/m;
1050
- function bundlerPin(root) {
1051
- const gemfile = join(root, "Gemfile");
1052
- const lockfile = join(root, "Gemfile.lock");
1053
- if (!existsSync(gemfile)) return null;
1054
- let lock;
1055
- try {
1056
- lock = readFileSync(lockfile, "utf-8");
1057
- } catch {
1058
- return null;
1059
- }
1060
- if (!COCOAPODS_SPEC.test(lock)) return null;
1061
- return {
1062
- gemfile,
1063
- lockfile
1064
- };
1065
- }
1066
- function podInstallCommand(root, ...args) {
1067
- const pod = [
1068
- "pod",
1069
- "install",
1070
- ...args
1071
- ].join(" ");
1072
- return bundlerPin(root) ? `bundle exec ${pod}` : pod;
1073
- }
1074
- const BUILD_FAILED = "** BUILD FAILED **";
1075
- const POSITIONED = /^(\S[^\t]*?):(\d+):(?:(\d+):)?\s*(?:fatal\s+)?error:\s+(.+)$/;
1076
- const UNPOSITIONED = /^(\S[^\t]*?)?\berror:\s+(.+)$/;
1077
- const LD_ERROR = /^ld:\s+(?!warning:)(.+)$/;
1078
- const UNDEFINED_HEADER = /^(?:ld:\s+)?Undefined symbols?(?:\s+for architecture\s+\S+)?:\s*$/;
1079
- const UNDEFINED_SYMBOL = /^\s+"?([^",]+)"?,\s+referenced from:\s*$/;
1080
- function undefinedSymbolMessage(symbol) {
1081
- return `Undefined symbol: ${symbol}`;
1082
- }
1083
- const PODS_OUT_OF_SYNC = /The sandbox is not in sync with the Podfile\.lock/;
1084
- const SIGNING = [
1085
- /No profiles for '.*' were found/,
1086
- /requires a development team/i,
1087
- /[Cc]ode ?[Ss]igning [Ee]rror/,
1088
- /No signing certificate/i,
1089
- /code signing is required/i,
1090
- /errSecInternalComponent/,
1091
- /Provisioning profile .* doesn't (?:include|match)/i
1092
- ];
1093
- const NO_SUCH_SCHEME = /does not contain a scheme named/;
1094
- const CAS_CORRUPT = /not a IncludeTreeRoot node kind/;
1095
- const DEVICE_SIGNING_REMEDY = "Set a team and a Development profile for the target's configuration in Xcode > Signing & Capabilities, then build once from Xcode to install the profile. Stim never passes -allowProvisioningUpdates, because registering a device or minting a profile changes your Apple Developer account.";
1096
- const SIMULATOR_SIGNING_REMEDY = "Stim builds for the simulator here, which needs no signing. Check CODE_SIGNING_REQUIRED / DEVELOPMENT_TEAM in the target's configuration, or build for a phone with `stim ios --device`.";
1097
- function remedyFor(message, root, sdk) {
1098
- if (CAS_CORRUPT.test(message)) return "The compilation cache holds a damaged object. Run `stim gc --delete --cache \"compilation cache\"` to empty that cache, then build again. The next build is a cold one.";
1099
- if (PODS_OUT_OF_SYNC.test(message)) return `Run \`${root ? podInstallCommand(root) : "pod install"}\` in ios/ (stim ios does this when Podfile.lock and Pods/Manifest.lock disagree), then build again.`;
1100
- if (NO_SUCH_SCHEME.test(message)) return "Run `xcodebuild -list` in ios/ to see the schemes this project defines, and share the app scheme so it is visible to the build.";
1101
- for (const pattern of SIGNING) if (pattern.test(message)) return sdk.startsWith("iphoneos") ? DEVICE_SIGNING_REMEDY : SIMULATOR_SIGNING_REMEDY;
1102
- return null;
1103
- }
1104
- function fileFromPrefix(prefix) {
1105
- const trimmed = String(prefix).trim().replace(/:$/, "");
1106
- if (!trimmed) return null;
1107
- const head = (trimmed.split(": ")[0] ?? "").trim();
1108
- if (!head.includes("/")) return null;
1109
- return head;
1110
- }
1111
- function makeDiagnostic({ file = null, line = null, column = null, message }, root, sdk) {
1112
- const text = String(message).trim();
1113
- const out = { message: text };
1114
- if (file) out.file = file;
1115
- if (line !== null && line !== void 0) out.line = line;
1116
- if (column !== null && column !== void 0) out.column = column;
1117
- out.message = text;
1118
- const remedy = remedyFor(text, root, sdk);
1119
- if (remedy) out.remedy = remedy;
1120
- return out;
1121
- }
1122
- function dedupeKey(d) {
1123
- return `${d.file || ""}|${d.line || ""}|${d.column || ""}|${d.message}`;
1124
- }
1125
- function extractXcodeDiagnostics(transcript, root = null, sdk = "iphonesimulator") {
1126
- if (typeof transcript !== "string" || transcript === "") return [];
1127
- const lines = transcript.split("\n");
1128
- const out = [];
1129
- const seen = /* @__PURE__ */ new Set();
1130
- const push = (d) => {
1131
- const key = dedupeKey(d);
1132
- if (seen.has(key)) return;
1133
- seen.add(key);
1134
- out.push(d);
1135
- };
1136
- for (let i = 0; i < lines.length; i += 1) {
1137
- const rawLine = lines[i];
1138
- if (rawLine === void 0) continue;
1139
- const raw = rawLine.replace(/\r$/, "");
1140
- if (raw.includes(BUILD_FAILED)) break;
1141
- if (UNDEFINED_HEADER.test(raw)) {
1142
- for (let j = i + 1; j < lines.length; j += 1) {
1143
- const symLine = lines[j];
1144
- if (symLine === void 0) continue;
1145
- const sym = UNDEFINED_SYMBOL.exec(symLine.replace(/\r$/, ""));
1146
- if (sym) {
1147
- const symbol = sym[1];
1148
- if (symbol !== void 0) push(makeDiagnostic({ message: undefinedSymbolMessage(symbol) }, root, sdk));
1149
- continue;
1150
- }
1151
- if (/^\s+\S/.test(symLine) && !/^\S/.test(symLine)) continue;
1152
- i = j - 1;
1153
- break;
1154
- }
1155
- continue;
1156
- }
1157
- const ld = LD_ERROR.exec(raw);
1158
- if (ld) {
1159
- push(makeDiagnostic({ message: `ld: ${ld[1]}` }, root, sdk));
1160
- continue;
1161
- }
1162
- const positioned = POSITIONED.exec(raw);
1163
- if (positioned) {
1164
- const posMsg = positioned[4];
1165
- if (posMsg === void 0) continue;
1166
- push(makeDiagnostic({
1167
- file: positioned[1],
1168
- line: Number(positioned[2]),
1169
- column: positioned[3] === void 0 ? null : Number(positioned[3]),
1170
- message: posMsg
1171
- }, root, sdk));
1172
- continue;
1173
- }
1174
- const plain = UNPOSITIONED.exec(raw);
1175
- if (plain) {
1176
- const plainMsg = plain[2];
1177
- if (plainMsg === void 0) continue;
1178
- push(makeDiagnostic({
1179
- file: fileFromPrefix(plain[1] || ""),
1180
- message: plainMsg
1181
- }, root, sdk));
1182
- }
1183
- }
1184
- return out;
1185
- }
1186
- function capDiagnostics(diagnostics, max = 10) {
1187
- const list = Array.isArray(diagnostics) ? diagnostics : [];
1188
- if (list.length <= max) return {
1189
- diagnostics: list.slice(),
1190
- truncated: 0
1191
- };
1192
- return {
1193
- diagnostics: list.slice(0, max),
1194
- truncated: list.length - max
1195
- };
1196
- }
1197
- function describeDiagnostic(diagnostic) {
1198
- if (!diagnostic || typeof diagnostic !== "object") return "";
1199
- const { file, line, column, message } = diagnostic;
1200
- if (!file) return String(message ?? "");
1201
- return `${file}${line ? column ? `:${line}:${column}` : `:${line}` : ""}: ${message}`;
1202
- }
1203
- //#endregion
1204
- //#region src/engine/xcode.ts
1205
- const IOS_DIR = "ios";
1206
- const PREBUILD_REMEDY = "Generate it with `npx expo prebuild -p ios` (stim ios does this automatically for an Expo project with no ios/ directory), or commit the native project.";
1207
- function buildFailure(message, remedy) {
1208
- return { error: {
1209
- code: "STIM_BUILD_FAILED",
1210
- message,
1211
- remedy
1212
- } };
1213
- }
1214
- function pickXcodeProject(entries) {
1215
- const names = (Array.isArray(entries) ? entries : []).filter((e) => typeof e === "string");
1216
- const workspaces = names.filter((e) => e.endsWith(".xcworkspace")).toSorted();
1217
- const projects = names.filter((e) => e.endsWith(".xcodeproj")).toSorted();
1218
- if (workspaces.length > 0) {
1219
- const projectNames = new Set(projects.map((p) => basename(p, ".xcodeproj")));
1220
- const file = workspaces.find((w) => projectNames.has(basename(w, ".xcworkspace"))) || workspaces[0];
1221
- if (file === void 0) return null;
1222
- return {
1223
- kind: "workspace",
1224
- flag: "-workspace",
1225
- file,
1226
- name: basename(file, ".xcworkspace")
1227
- };
1228
- }
1229
- if (projects.length > 0) {
1230
- const file = projects[0];
1231
- if (file === void 0) return null;
1232
- return {
1233
- kind: "project",
1234
- flag: "-project",
1235
- file,
1236
- name: basename(file, ".xcodeproj")
1237
- };
1238
- }
1239
- return null;
1240
- }
1241
- function discoverXcodeProject(root) {
1242
- const dir = join(root, IOS_DIR);
1243
- let entries;
1244
- try {
1245
- entries = readdirSync(dir);
1246
- } catch {
1247
- return buildFailure(`No ${IOS_DIR}/ directory in ${root}.`, PREBUILD_REMEDY);
1248
- }
1249
- const picked = pickXcodeProject(entries);
1250
- if (!picked) return buildFailure(`${dir} contains no .xcworkspace and no .xcodeproj.`, PREBUILD_REMEDY);
1251
- return {
1252
- ...picked,
1253
- dir,
1254
- path: join(dir, picked.file)
1255
- };
1256
- }
1257
- function parseSchemeList(text) {
1258
- const empty = {
1259
- name: null,
1260
- schemes: []
1261
- };
1262
- if (typeof text !== "string") return empty;
1263
- const start = text.indexOf("{");
1264
- const end = text.lastIndexOf("}");
1265
- if (start < 0 || end <= start) return empty;
1266
- let data;
1267
- try {
1268
- data = JSON.parse(text.slice(start, end + 1));
1269
- } catch {
1270
- return empty;
1271
- }
1272
- if (!data || typeof data !== "object") return empty;
1273
- const record = data;
1274
- const container = record.workspace || record.project;
1275
- if (!container || typeof container !== "object") return empty;
1276
- const info = container;
1277
- const schemes = Array.isArray(info.schemes) ? info.schemes.filter((s) => typeof s === "string" && s.trim() !== "") : [];
1278
- return {
1279
- name: typeof info.name === "string" ? info.name : null,
1280
- schemes
1281
- };
1282
- }
1283
- const TEST_SCHEME = /(?:UI)?Tests$/;
1284
- function pickScheme(schemes, containerName) {
1285
- const list = (Array.isArray(schemes) ? schemes : []).filter((s) => typeof s === "string" && s.trim() !== "");
1286
- if (list.length === 0) return null;
1287
- const name = typeof containerName === "string" ? containerName.trim() : "";
1288
- if (name) {
1289
- const exact = list.find((s) => s === name);
1290
- if (exact) return exact;
1291
- const insensitive = list.find((s) => s.toLowerCase() === name.toLowerCase());
1292
- if (insensitive) return insensitive;
1293
- }
1294
- const app = list.filter((s) => !TEST_SCHEME.test(s));
1295
- return app.length === 1 ? app[0] : null;
1296
- }
1297
- const LIST_TIMEOUT_MS = 18e4;
1298
- function listSchemes(project, { exec = null } = {}) {
1299
- const executor = exec || getExecutor();
1300
- try {
1301
- return parseSchemeList(executor.runFile("xcodebuild", [
1302
- project.flag,
1303
- project.path,
1304
- "-list",
1305
- "-json"
1306
- ], { timeoutMs: LIST_TIMEOUT_MS }));
1307
- } catch {
1308
- return null;
1309
- }
1310
- }
1311
- function resolveScheme(project, { exec = null, scheme: requested = null } = {}) {
1312
- const listing = listSchemes(project, { exec });
1313
- if (listing === null) return { error: {
1314
- code: "STIM_NO_SCHEME",
1315
- message: `Could not list schemes for ${project.path}.`,
1316
- remedy: `Run \`xcodebuild ${project.flag} ${project.path} -list\` to see what it reports.`
1317
- } };
1318
- if (requested !== null) {
1319
- if (listing.schemes.includes(requested)) return {
1320
- scheme: requested,
1321
- schemes: listing.schemes
1322
- };
1323
- return { error: {
1324
- code: "STIM_NO_SCHEME",
1325
- message: `No shared Xcode scheme named ${JSON.stringify(requested)} in ${project.path}. Available schemes: ${listing.schemes.join(", ") || "none"}.`,
1326
- remedy: "Pass an exact available name with --scheme, or share the intended app scheme in Xcode."
1327
- } };
1328
- }
1329
- let scheme = pickScheme(listing.schemes, listing.name || project.name);
1330
- if (!scheme && project.dir) try {
1331
- const app = JSON.parse(readFileSync(join(project.dir, "..", "app.json"), "utf8"));
1332
- scheme = pickScheme(listing.schemes, app?.name);
1333
- } catch {}
1334
- if (!scheme) {
1335
- const found = listing.schemes.length ? listing.schemes.join(", ") : "none";
1336
- return { error: {
1337
- code: "STIM_NO_SCHEME",
1338
- message: `Could not select an app scheme in ${project.path} (schemes: ${found}).`,
1339
- remedy: listing.schemes.length > 0 ? "Pass --scheme <name> to select the intended shared app scheme. Stim does not guess between unmatched schemes." : "Share the app scheme in Xcode (Product > Scheme > Manage Schemes, tick Shared) so xcodebuild can see it."
1340
- } };
1341
- }
1342
- return {
1343
- scheme,
1344
- schemes: listing.schemes
1345
- };
1346
- }
1347
- const PREFIX_MAP_TARGET = "/^src";
1348
- function prefixMapping(workspaceRoot) {
1349
- return `${String(workspaceRoot).replace(/\/+$/, "")}=${PREFIX_MAP_TARGET}`;
1350
- }
1351
- function compilationPrefixMappings(workspaceRoot, derivedDataPath) {
1352
- const source = resolve(workspaceRoot);
1353
- const derived = resolve(derivedDataPath);
1354
- const mappings = [prefixMapping(source)];
1355
- const rel = relative(source, derived);
1356
- if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) mappings.push(`${derived}=/^derived-data`);
1357
- return mappings.join(" ");
1358
- }
1359
- function ccacheEnabled(podfileProperties) {
1360
- if (!podfileProperties || typeof podfileProperties !== "object") return false;
1361
- return podfileProperties["apple.ccacheEnabled"] === "true";
1362
- }
1363
- function readPodfileProperties(root) {
1364
- try {
1365
- const parsed = JSON.parse(readFileSync(join(root, IOS_DIR, "Podfile.properties.json"), "utf-8"));
1366
- return parsed && typeof parsed === "object" ? parsed : null;
1367
- } catch {
1368
- return null;
1369
- }
1370
- }
1371
- function compilationCacheSettings({ workspaceRoot, derivedDataPath, casPath, xcodeMajor, ccache = false, optimizations = resolveOptimizations({}, {}).ios }) {
1372
- if (xcodeMajor === null || xcodeMajor === void 0) return [];
1373
- if (xcodeMajor < 26) return [];
1374
- if (ccache) return [];
1375
- return [
1376
- `COMPILATION_CACHE_ENABLE_CACHING=${optimizations.compilationCache ? "YES" : "NO"}`,
1377
- `COMPILATION_CACHE_CAS_PATH=${casPath}`,
1378
- `SWIFT_ENABLE_COMPILE_CACHE=${optimizations.compilationCache && optimizations.swiftCompilationCache ? "YES" : "NO"}`,
1379
- `CLANG_ENABLE_PREFIX_MAPPING=${optimizations.prefixMapping ? "YES" : "NO"}`,
1380
- `CLANG_OTHER_PREFIX_MAPPINGS=${optimizations.prefixMapping ? compilationPrefixMappings(workspaceRoot, derivedDataPath) : ""}`
1381
- ];
1382
- }
1383
- function parseXcodeMajor(output) {
1384
- const m = /^Xcode\s+(\d+)/m.exec(String(output || ""));
1385
- if (!m) return null;
1386
- const digits = m[1];
1387
- if (digits === void 0) return null;
1388
- const major = parseInt(digits, 10);
1389
- return Number.isFinite(major) ? major : null;
1390
- }
1391
- function detectXcodeMajor(exec = null) {
1392
- return parseXcodeMajor((exec || getExecutor()).runQuiet("xcodebuild -version", { timeoutMs: 1e4 }));
1393
- }
1394
- function resolveCompilationCacheSettings({ root, derivedDataPath, exec = null, casPath = sharedCompilationCache(), optimizations = resolveOptimizations({}, {}).ios, onNote = (line) => console.error(line) }) {
1395
- const settings = compilationCacheSettings({
1396
- workspaceRoot: root,
1397
- derivedDataPath,
1398
- casPath,
1399
- xcodeMajor: detectXcodeMajor(exec),
1400
- ccache: ccacheEnabled(readPodfileProperties(root)),
1401
- optimizations
1402
- });
1403
- if (settings.length > 0 && optimizations.compilationCache) {
1404
- register({
1405
- dir: casPath,
1406
- name: "Xcode compilation cache",
1407
- prune: "atomic",
1408
- note: "shared Xcode compilation cache"
1409
- });
1410
- onNote(chalk.dim(phaseLine("cache", `compilation cache on (CAS at ${casPath})`)));
1411
- }
1412
- return settings;
1413
- }
1414
- function xcodebuildArgs({ project, scheme, udid = null, destination = null, configuration = "Debug", sdk = "iphonesimulator", derivedDataPath, extraArgs = [], buildSettings = [] }) {
1415
- return [
1416
- project.flag,
1417
- project.path,
1418
- "-scheme",
1419
- scheme,
1420
- "-configuration",
1421
- configuration,
1422
- "-sdk",
1423
- sdk,
1424
- "-destination",
1425
- destination || `id=${udid}`,
1426
- "-derivedDataPath",
1427
- derivedDataPath,
1428
- ...extraArgs,
1429
- "build",
1430
- ...buildSettings
1431
- ];
1432
- }
1433
- function productsDir(derivedDataPath, { configuration = "Debug", sdk = "iphonesimulator" } = {}) {
1434
- return join(derivedDataPath, "Build", "Products", `${configuration}-${sdk}`);
1435
- }
1436
- function pickAppBundle(entries, preferredName = null) {
1437
- const apps = (Array.isArray(entries) ? entries : []).filter((e) => typeof e === "string" && e.endsWith(".app")).toSorted();
1438
- if (apps.length === 0) return null;
1439
- if (preferredName) {
1440
- const wanted = `${preferredName}.app`;
1441
- const exact = apps.find((a) => a === wanted);
1442
- if (exact) return exact;
1443
- }
1444
- return apps[0];
1445
- }
1446
- function findAppBundle(dir, preferredName = null) {
1447
- let entries;
1448
- try {
1449
- entries = readdirSync(dir);
1450
- } catch {
1451
- return null;
1452
- }
1453
- const app = pickAppBundle(entries, preferredName);
1454
- return app ? join(dir, app) : null;
1455
- }
1456
- function parseBundleId(plistJson) {
1457
- if (typeof plistJson !== "string") return null;
1458
- let data;
1459
- try {
1460
- data = JSON.parse(plistJson);
1461
- } catch {
1462
- return null;
1463
- }
1464
- if (!data || typeof data !== "object") return null;
1465
- const id = data.CFBundleIdentifier;
1466
- return typeof id === "string" && id.trim() !== "" ? id.trim() : null;
1467
- }
1468
- function readBundleId(appPath, { exec = null } = {}) {
1469
- const executor = exec || getExecutor();
1470
- try {
1471
- const id = parseBundleId(executor.runFile("plutil", [
1472
- "-convert",
1473
- "json",
1474
- "-o",
1475
- "-",
1476
- join(appPath, "Info.plist")
1477
- ]));
1478
- if (id) return id;
1479
- } catch {}
1480
- try {
1481
- const value = executor.runFile("defaults", [
1482
- "read",
1483
- join(appPath, "Info"),
1484
- "CFBundleIdentifier"
1485
- ]);
1486
- const trimmed = String(value).trim();
1487
- return trimmed === "" ? null : trimmed;
1488
- } catch {
1489
- return null;
1490
- }
1491
- }
1492
- function parseBundleExecutable(plistJson) {
1493
- if (typeof plistJson !== "string") return null;
1494
- let data;
1495
- try {
1496
- data = JSON.parse(plistJson);
1497
- } catch {
1498
- return null;
1499
- }
1500
- if (!data || typeof data !== "object") return null;
1501
- const name = data.CFBundleExecutable;
1502
- return typeof name === "string" && name.trim() !== "" ? name.trim() : null;
1503
- }
1504
- function readBundleExecutable(appPath, { exec = null } = {}) {
1505
- const executor = exec || getExecutor();
1506
- try {
1507
- const name = parseBundleExecutable(executor.runFile("plutil", [
1508
- "-convert",
1509
- "json",
1510
- "-o",
1511
- "-",
1512
- join(appPath, "Info.plist")
1513
- ]));
1514
- if (name) return name;
1515
- } catch {}
1516
- try {
1517
- const value = executor.runFile("defaults", [
1518
- "read",
1519
- join(appPath, "Info"),
1520
- "CFBundleExecutable"
1521
- ]);
1522
- const trimmed = String(value).trim();
1523
- return trimmed === "" ? null : trimmed;
1524
- } catch {
1525
- return null;
1526
- }
1527
- }
1528
- function tailLines(lines, count = 5) {
1529
- return (Array.isArray(lines) ? lines : []).filter((l) => typeof l === "string" && l.trim() !== "").slice(-count);
1530
- }
1531
- const HEARTBEAT_INTERVAL_MS = 3e4;
1532
- const HEARTBEAT_ACTIVITY = {
1533
- build: "compiling",
1534
- deps: "installing",
1535
- pods: "installing"
1536
- };
1537
- function heartbeatLine(elapsedMs, label = "build", estimateMs = null) {
1538
- const activity = HEARTBEAT_ACTIVITY[label] ?? "running";
1539
- const elapsed = formatElapsed(elapsedMs);
1540
- const inside = estimateMs && estimateMs > 0 ? elapsedMs > estimateMs ? `${elapsed}, usually ~${formatElapsed(estimateMs)}` : `${elapsed} of ~${formatElapsed(estimateMs)}` : elapsed;
1541
- return phaseLine(label, `still ${activity} (${inside})`);
1542
- }
1543
- const defaultSchedule = (run, delayMs) => {
1544
- const timer = setTimeout(run, delayMs);
1545
- timer.unref?.();
1546
- return () => clearTimeout(timer);
1547
- };
1548
- function startBuildHeartbeat({ intervalMs, elapsed, emit, label = "build", estimateMs = null, schedule = defaultSchedule }) {
1549
- if (!(intervalMs > 0)) return () => {};
1550
- let stopped = false;
1551
- let lastBeat = 0;
1552
- let cancel = null;
1553
- function tick() {
1554
- if (stopped) return;
1555
- const ms = elapsed();
1556
- const beat = Math.floor(ms / intervalMs) * intervalMs;
1557
- if (beat > lastBeat) {
1558
- lastBeat = beat;
1559
- emit(heartbeatLine(beat, label, estimateMs));
1560
- }
1561
- cancel = schedule(tick, intervalMs - ms % intervalMs);
1562
- }
1563
- cancel = schedule(tick, intervalMs);
1564
- return () => {
1565
- stopped = true;
1566
- cancel?.();
1567
- };
1568
- }
1569
- function failedResult({ code, diagnostics, durationMs, exitCode = null, transcriptLines = 0, tail = [], compilationCache = COMPILATION_CACHE_UNAVAILABLE }) {
1570
- const capped = capDiagnostics(diagnostics);
1571
- return {
1572
- failed: true,
1573
- code,
1574
- diagnostics: capped.diagnostics,
1575
- truncated: capped.truncated,
1576
- durationMs,
1577
- exitCode,
1578
- transcriptLines,
1579
- tail,
1580
- compilationCache
1581
- };
1582
- }
1583
- const COMPILATION_CACHE_UNAVAILABLE = {
1584
- status: "unavailable",
1585
- hits: null,
1586
- cacheableTasks: null,
1587
- hitRatePercent: null
1588
- };
1589
- const COMPILATION_CACHE_NOT_RUN = {
1590
- status: "not-run",
1591
- hits: null,
1592
- cacheableTasks: null,
1593
- hitRatePercent: null
1594
- };
1595
- function parseCompilationCacheActivity(line) {
1596
- if (typeof line !== "string") return null;
1597
- const match = line.match(/(?:note:\s*)?(\d+) hits \/ (\d+) cacheable tasks \((\d+(?:\.\d+)?)%\)/);
1598
- if (!match) return null;
1599
- return {
1600
- status: "reported",
1601
- hits: Number(match[1]),
1602
- cacheableTasks: Number(match[2]),
1603
- hitRatePercent: Number(match[3])
1604
- };
1605
- }
1606
- function compilationCacheActivityLine(activity) {
1607
- if (activity.status === "reported") return `${activity.hits}/${activity.cacheableTasks} hits (${activity.hitRatePercent}%)`;
1608
- if (activity.status === "not-run") return "not run; artifact cache supplied the app";
1609
- return "unavailable; Xcode did not report reliable statistics";
1610
- }
1611
- async function buildIos({ root, udid = null, logWriter, project = null, scheme = null, configuration = "Debug", sdk = "iphonesimulator", destination = null, derivedDataPath = null, extraArgs = [], compilationCache = void 0, optimizations = resolveOptimizations({}, {}).ios, now = () => Date.now(), exec = null, heartbeatMs = HEARTBEAT_INTERVAL_MS, estimateMs = null, onHeartbeat = (line) => console.error(line), onNote = (line) => console.error(line) }) {
1612
- if (!root || typeof root !== "string") throw new TypeError("buildIos requires {root}");
1613
- if (!logWriter || typeof logWriter.write !== "function") throw new TypeError("buildIos requires {logWriter} with a write() method");
1614
- if (!udid && !destination) throw new TypeError("buildIos requires {udid} (or an explicit {destination})");
1615
- const executor = exec || getExecutor();
1616
- const dd = derivedDataPath || (scheme ? join(workspaceDerivedData(root), `scheme-${createHash("sha256").update(scheme).digest("hex")}`) : workspaceDerivedData(root));
1617
- const startedAt = now();
1618
- const elapsed = () => now() - startedAt;
1619
- const reportError = (message, remedy) => {
1620
- logWriter.write({
1621
- src: "build",
1622
- level: "error",
1623
- msg: message,
1624
- event: "build_diagnostic",
1625
- ...remedy ? { remedy } : {}
1626
- });
1627
- };
1628
- let target = project;
1629
- if (!target) {
1630
- const discovered = discoverXcodeProject(root);
1631
- if (discovered.error) {
1632
- reportError(discovered.error.message, discovered.error.remedy);
1633
- return failedResult({
1634
- code: discovered.error.code,
1635
- diagnostics: [{
1636
- message: discovered.error.message,
1637
- remedy: discovered.error.remedy ?? void 0
1638
- }],
1639
- durationMs: elapsed()
1640
- });
1641
- }
1642
- target = discovered;
1643
- }
1644
- const resolvedTarget = target;
1645
- const selected = resolveScheme(resolvedTarget, {
1646
- exec: executor,
1647
- scheme
1648
- });
1649
- if (selected.error) {
1650
- reportError(selected.error.message, selected.error.remedy);
1651
- return failedResult({
1652
- code: selected.error.code,
1653
- diagnostics: [{
1654
- message: selected.error.message,
1655
- remedy: selected.error.remedy
1656
- }],
1657
- durationMs: elapsed()
1658
- });
1659
- }
1660
- const buildScheme = selected.scheme;
1661
- const buildSettings = compilationCache === void 0 ? resolveCompilationCacheSettings({
1662
- root,
1663
- derivedDataPath: dd,
1664
- exec: executor,
1665
- onNote,
1666
- optimizations
1667
- }) : compilationCache || [];
1668
- const args = xcodebuildArgs({
1669
- project: resolvedTarget,
1670
- scheme: buildScheme,
1671
- udid,
1672
- destination,
1673
- configuration,
1674
- sdk,
1675
- derivedDataPath: dd,
1676
- extraArgs,
1677
- buildSettings
1678
- });
1679
- logWriter.write({
1680
- src: "build",
1681
- level: "info",
1682
- msg: `xcodebuild ${args.join(" ")}`,
1683
- event: "build_start"
1684
- });
1685
- const transcript = [];
1686
- let compilationCacheActivity = COMPILATION_CACHE_UNAVAILABLE;
1687
- const onLine = (line) => {
1688
- const msg = cleanLine(line);
1689
- transcript.push(msg);
1690
- if (msg.trim() === "") return;
1691
- logWriter.write({
1692
- src: "build",
1693
- level: "debug",
1694
- msg
1695
- });
1696
- const activity = parseCompilationCacheActivity(msg);
1697
- if (activity) {
1698
- compilationCacheActivity = activity;
1699
- logWriter.write({
1700
- src: "build",
1701
- level: "info",
1702
- msg: compilationCacheActivityLine(activity),
1703
- event: "compilation_cache",
1704
- hits: activity.hits,
1705
- cacheableTasks: activity.cacheableTasks,
1706
- hitRatePercent: activity.hitRatePercent
1707
- });
1708
- }
1709
- };
1710
- let child;
1711
- try {
1712
- child = executor.spawn("xcodebuild", args, {
1713
- cwd: resolvedTarget.dir || root,
1714
- stdio: [
1715
- "ignore",
1716
- "pipe",
1717
- "pipe"
1718
- ],
1719
- detached: false,
1720
- env: {
1721
- ...process.env,
1722
- NSUnbufferedIO: "YES",
1723
- FORCE_COLOR: "0"
1724
- }
1725
- });
1726
- } catch (err) {
1727
- const message = `Could not run xcodebuild: ${err.message}`;
1728
- const remedy = "Install Xcode and select it with `sudo xcode-select -s /Applications/Xcode.app`.";
1729
- reportError(message, remedy);
1730
- return failedResult({
1731
- code: "STIM_BUILD_FAILED",
1732
- diagnostics: [{
1733
- message,
1734
- remedy
1735
- }],
1736
- durationMs: elapsed()
1737
- });
1738
- }
1739
- const outReader = createLineReader(onLine);
1740
- const errReader = createLineReader(onLine);
1741
- child.stdout?.setEncoding?.("utf-8");
1742
- child.stderr?.setEncoding?.("utf-8");
1743
- child.stdout?.on("data", (chunk) => outReader.push(chunk));
1744
- child.stderr?.on("data", (chunk) => errReader.push(chunk));
1745
- const stopHeartbeat = startBuildHeartbeat({
1746
- intervalMs: heartbeatMs,
1747
- elapsed,
1748
- emit: onHeartbeat,
1749
- estimateMs
1750
- });
1751
- const outcome = await new Promise((settlePromise) => {
1752
- let settled = false;
1753
- const finish = (value) => {
1754
- if (settled) return;
1755
- settled = true;
1756
- outReader.flush();
1757
- errReader.flush();
1758
- settlePromise(value);
1759
- };
1760
- child.on("close", (code, signal) => finish({
1761
- code,
1762
- signal
1763
- }));
1764
- child.on("error", (error) => finish({
1765
- code: null,
1766
- signal: null,
1767
- error
1768
- }));
1769
- });
1770
- stopHeartbeat();
1771
- const durationMs = elapsed();
1772
- const text = transcript.join("\n");
1773
- if (outcome.error) {
1774
- const message = `Could not run xcodebuild: ${outcome.error.message}`;
1775
- const remedy = "Install Xcode and select it with `sudo xcode-select -s /Applications/Xcode.app`.";
1776
- reportError(message, remedy);
1777
- return failedResult({
1778
- code: "STIM_BUILD_FAILED",
1779
- diagnostics: [{
1780
- message,
1781
- remedy
1782
- }],
1783
- durationMs,
1784
- transcriptLines: transcript.length,
1785
- tail: tailLines(transcript),
1786
- compilationCache: compilationCacheActivity
1787
- });
1788
- }
1789
- if (outcome.code !== 0) {
1790
- const diagnostics = extractXcodeDiagnostics(text, root, sdk);
1791
- const capped = capDiagnostics(diagnostics);
1792
- for (const d of capped.diagnostics) reportError(describeDiagnostic(d), d.remedy);
1793
- if (capped.diagnostics.length === 0) reportError(`xcodebuild exited ${outcome.code ?? `on ${outcome.signal}`} with no recognizable diagnostic; see the transcript above.`, null);
1794
- return failedResult({
1795
- code: "STIM_BUILD_FAILED",
1796
- diagnostics,
1797
- durationMs,
1798
- exitCode: outcome.code,
1799
- transcriptLines: transcript.length,
1800
- tail: tailLines(transcript),
1801
- compilationCache: compilationCacheActivity
1802
- });
1803
- }
1804
- const products = productsDir(dd, {
1805
- configuration,
1806
- sdk
1807
- });
1808
- let appPath = null;
1809
- if (scheme) try {
1810
- const targets = JSON.parse(executor.runFile("xcodebuild", [
1811
- resolvedTarget.flag,
1812
- resolvedTarget.path,
1813
- "-scheme",
1814
- buildScheme,
1815
- "-configuration",
1816
- configuration,
1817
- "-sdk",
1818
- sdk,
1819
- "-destination",
1820
- destination || `id=${udid}`,
1821
- "-derivedDataPath",
1822
- dd,
1823
- ...extraArgs,
1824
- ...buildSettings,
1825
- "-showBuildSettings",
1826
- "-json"
1827
- ], { timeoutMs: 3e4 }));
1828
- const paths = /* @__PURE__ */ new Set();
1829
- for (const item of Array.isArray(targets) ? targets : []) {
1830
- const settings = item?.buildSettings;
1831
- if (settings?.PRODUCT_TYPE !== "com.apple.product-type.application" || settings.PLATFORM_NAME !== sdk) continue;
1832
- const dir = settings.TARGET_BUILD_DIR;
1833
- const name = settings.FULL_PRODUCT_NAME;
1834
- if (typeof dir !== "string" || !isAbsolute(dir) || typeof name !== "string" || basename(name) !== name || !name.endsWith(".app")) continue;
1835
- const path = join(dir, name);
1836
- if (statSync(path).isDirectory()) paths.add(path);
1837
- }
1838
- if (paths.size === 1) appPath = [...paths][0];
1839
- } catch {}
1840
- else appPath = findAppBundle(products, buildScheme);
1841
- if (!appPath) {
1842
- const message = scheme ? `xcodebuild succeeded, but the built app for scheme ${JSON.stringify(buildScheme)} could not be identified unambiguously from its build settings.` : `xcodebuild reported success but no .app is in ${products}.`;
1843
- const remedy = "Check that the scheme builds one application target for this platform, not a library, test bundle, or several apps.";
1844
- reportError(message, remedy);
1845
- return failedResult({
1846
- code: "STIM_BUILD_FAILED",
1847
- diagnostics: [{
1848
- message,
1849
- remedy
1850
- }],
1851
- durationMs,
1852
- exitCode: 0,
1853
- transcriptLines: transcript.length,
1854
- tail: tailLines(transcript),
1855
- compilationCache: compilationCacheActivity
1856
- });
1857
- }
1858
- const bundleId = readBundleId(appPath, { exec: executor });
1859
- if (!bundleId) {
1860
- const message = `No readable CFBundleIdentifier in ${join(appPath, "Info.plist")}.`;
1861
- const remedy = "Check PRODUCT_BUNDLE_IDENTIFIER in the target's Debug configuration.";
1862
- reportError(message, remedy);
1863
- return failedResult({
1864
- code: "STIM_BUILD_FAILED",
1865
- diagnostics: [{
1866
- message,
1867
- remedy
1868
- }],
1869
- durationMs,
1870
- exitCode: 0,
1871
- transcriptLines: transcript.length,
1872
- tail: tailLines(transcript),
1873
- compilationCache: compilationCacheActivity
1874
- });
1875
- }
1876
- logWriter.write({
1877
- src: "build",
1878
- level: "info",
1879
- msg: `BUILD SUCCEEDED ${appPath} (${bundleId}) in ${durationMs}ms`,
1880
- event: "build_done"
1881
- });
1882
- return {
1883
- appPath,
1884
- bundleId,
1885
- durationMs,
1886
- scheme: buildScheme,
1887
- project: resolvedTarget,
1888
- derivedDataPath: dd,
1889
- productsDir: products,
1890
- transcriptLines: transcript.length,
1891
- compilationCache: compilationCacheActivity
1892
- };
1893
- }
1894
- //#endregion
1895
- export { liveOwnedDeviceCount as C, simslimIsOnPath as D, unknownIosRuntimeRefusal as E, ensureOwnedDevice as S, unknownIosDeviceTypeRefusal as T, AvdBootError as _, ccacheEnabled as a, clearIosAdoptionPending as b, discoverXcodeProject as c, resolveScheme as d, startBuildHeartbeat as f, podInstallCommand as g, bundlerPin as h, buildIos as i, readBundleExecutable as l, describeDiagnostic as m, COMPILATION_CACHE_UNAVAILABLE as n, compilationCacheActivityLine as o, tailLines as p, HEARTBEAT_INTERVAL_MS as r, detectXcodeMajor as s, COMPILATION_CACHE_NOT_RUN as t, readBundleId as u, AvdRecoveryError as v, unknownAndroidSystemImageRefusal as w, ensureBooted as x, checkDeviceCapacity as y };
1136
+ export { liveOwnedDeviceCount as a, unknownIosRuntimeRefusal as c, simslimIsOnPath as d, ensureOwnedDevice as i, AvdBootError as l, clearIosAdoptionPending as n, unknownAndroidSystemImageRefusal as o, ensureBooted as r, unknownIosDeviceTypeRefusal as s, checkDeviceCapacity as t, AvdRecoveryError as u };