stim 1.17.2 → 1.19.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 (162) hide show
  1. package/README.md +50 -1
  2. package/dist/{activity-IRvJY_ru.mjs → activity-BSw0x84R.mjs} +4 -4
  3. package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-Bjxu_VmX.mjs} +8 -8
  4. package/dist/{android-B9yaNKwa.mjs → android-BXc6BmEi.mjs} +658 -404
  5. package/dist/{android-B_YNZy61.mjs → android-BdQC6zs1.mjs} +9 -9
  6. package/dist/android-BvV1gGdN.mjs +4 -0
  7. package/dist/{android-DE4v59Mb.mjs → android-DE5g_5gv.mjs} +2 -2
  8. package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-C87geAg-.mjs} +5 -5
  9. package/dist/android-cas-compiler.mjs +2 -2
  10. package/dist/api-DIf_s86s.mjs +150 -0
  11. package/dist/api-run.d.mts +1 -0
  12. package/dist/api-run.mjs +145 -0
  13. package/dist/api.d.mts +218 -0
  14. package/dist/api.mjs +2 -0
  15. package/dist/{app-install-VPkI5WEg.mjs → app-install-CFI4ptUL.mjs} +33 -13
  16. package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
  17. package/dist/{web-Dl0KSRHf.mjs → browser-web-CDhqQxhv.mjs} +138 -176
  18. package/dist/{budget-DfcgFdQB.mjs → budget-Dz-kyXLv.mjs} +1391 -58
  19. package/dist/build-facts-B87vo-Im.d.mts +142 -0
  20. package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-DiwKSyv9.mjs} +3 -3
  21. package/dist/cache-manifest.mjs +1 -1
  22. package/dist/{chrome-BrcVWQyD.mjs → chrome-BTUOo8p2.mjs} +1 -1
  23. package/dist/cli-C9Zvgbdc.mjs +68 -0
  24. package/dist/cli.mjs +1 -1
  25. package/dist/{client-SvBdgObD.mjs → client-CuUo8Nvg.mjs} +381 -49
  26. package/dist/collector-run.d.mts +6 -0
  27. package/dist/collector-run.mjs +9 -9
  28. package/dist/{command-output-CDAIFD-8.mjs → command-output-Bt8ygHE7.mjs} +2 -2
  29. package/dist/{config-D0DbpZtM.mjs → config-DC5jEtd1.mjs} +9 -5
  30. package/dist/{created-devices-CpvzBL25.mjs → created-devices-D0s26Hx5.mjs} +2 -2
  31. package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-DKaS1OM9.mjs} +2 -2
  32. package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-pB_IPfV4.mjs} +1 -1
  33. package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CDY_zAmD.mjs} +2 -2
  34. package/dist/{device-C8IOoJPv.mjs → device-BHw0WlHl.mjs} +8 -8
  35. package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-BoN1jQkO.mjs} +25 -14
  36. package/dist/device-host-worker.mjs +19 -19
  37. package/dist/{gradle-dJpre3Yn.mjs → device-ios-B2ruohE-.mjs} +661 -107
  38. package/dist/{device-lease-DpoS36_u.mjs → device-lease-Cu7PtQpZ.mjs} +9 -9
  39. package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-D38SnZtt.mjs} +4 -4
  40. package/dist/{device-pool-3o7s5lo4.mjs → device-pool-CP4rAsHx.mjs} +3 -3
  41. package/dist/{device-remote-hFSkpfQu.mjs → device-remote-B5GiUifk.mjs} +15 -16
  42. package/dist/{doctor-CFv1fCrF.mjs → doctor-PRJH94v6.mjs} +61 -34
  43. package/dist/doctor-watchman-wZhyTdp4.mjs +259 -0
  44. package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics--v5HGOU0.mjs} +11 -11
  45. package/dist/{exec-CaErVv7t.mjs → exec-i8GDPwq9.mjs} +93 -6
  46. package/dist/{gc-DEmkg3Xh.mjs → gc-CyHMcKcn.mjs} +25 -471
  47. package/dist/{guide-CSmhMTf8.mjs → guide-dRaQJJk9.mjs} +681 -507
  48. package/dist/{hosted-android-E1Heoqnv.mjs → hosted-android-Bs7fytj7.mjs} +3 -3
  49. package/dist/{hosted-client-Bp4knSwu.mjs → hosted-client-C5G4h_OJ.mjs} +2 -2
  50. package/dist/{hosted-ios-BnbeYOUD.mjs → hosted-ios-Bik035eL.mjs} +1 -1
  51. package/dist/{hosted-logs-OcgIBvCT.mjs → hosted-logs-CI9Az9a4.mjs} +9 -9
  52. package/dist/{hosted-macos-DcxKjtfH.mjs → hosted-macos-DABm3ScO.mjs} +5 -5
  53. package/dist/{hosted-native-1Ebbig4w.mjs → hosted-native-DbV2aUVw.mjs} +13 -9
  54. package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-DYiMBn34.mjs} +7 -8
  55. package/dist/{ios-DSfnPfse.mjs → ios-BWbHFZVz.mjs} +19 -7
  56. package/dist/{ios-Cr1h5gnO.mjs → ios-DCrkgXEM.mjs} +1 -1
  57. package/dist/ios-DYUr9LGc.mjs +8 -0
  58. package/dist/ios-ITYno9rO.mjs +2241 -0
  59. package/dist/{ios-device-pAN4t54-.mjs → ios-device-CJ_ZOGky.mjs} +3 -3
  60. package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-CrW8aMb5.mjs} +1 -1
  61. package/dist/{ios-state-8L3lgum6.mjs → ios-state-By0XuLTQ.mjs} +1 -1
  62. package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-DCveSE6d.mjs} +5 -5
  63. package/dist/{logs-BAaS7gS2.mjs → logs-Bao1HOMy.mjs} +13 -13
  64. package/dist/{macos-BIRQS90f.mjs → macos-ciyBdTY5.mjs} +194 -132
  65. package/dist/macos-nIOATJmR.mjs +2 -0
  66. package/dist/macos-run.mjs +1 -1
  67. package/dist/maintenance-run.mjs +349 -47
  68. package/dist/manifest-CX0d1IkZ.mjs +148 -0
  69. package/dist/{metro-g8NqiJp-.mjs → metro-DI2AjPF_.mjs} +196 -43
  70. package/dist/{metro-gateway-C-ijGI2F.mjs → metro-gateway-BxpJLIwh.mjs} +4 -4
  71. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
  72. package/dist/{build-plan-BDsSi09V.mjs → miss-reason-CjQCOKqq.mjs} +207 -249
  73. package/dist/{named-ports-C8m8EQs2.mjs → named-ports-B4iWH4E-.mjs} +70 -8
  74. package/dist/{native-runtime-BEK171xQ.mjs → native-runtime-BGxsbfny.mjs} +6 -6
  75. package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
  76. package/dist/offload-worker.d.mts +3 -45
  77. package/dist/offload-worker.mjs +11 -11
  78. package/dist/{ownership-DyRQru0q.mjs → ownership-EB0ySdGK.mjs} +3 -3
  79. package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
  80. package/dist/placement-log-Dxe34rJm.mjs +88 -0
  81. package/dist/plan-placement-BVY59qa8.mjs +290 -0
  82. package/dist/{ports-4nYqxPWC.mjs → ports-ByPc0_97.mjs} +3 -3
  83. package/dist/{prebuild-D7RUMkwN.mjs → prebuild-BKJ3wJI1.mjs} +5 -5
  84. package/dist/preview-BbSottmO.mjs +986 -0
  85. package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
  86. package/dist/project-B5eQ1USs.mjs +612 -0
  87. package/dist/{pull-request-pxnDCQzn.mjs → pull-request-IHNdMnG8.mjs} +1 -1
  88. package/dist/pull-requests.mjs +1 -1
  89. package/dist/react-native-ios-BMh2i25I.mjs +2 -0
  90. package/dist/react-native-ios-CSpIxexZ.mjs +2083 -0
  91. package/dist/{recordings-BNgsD1Tr.mjs → recordings-Bv8McZN4.mjs} +1 -1
  92. package/dist/{reload-DcRxj3qG.mjs → reload-rAPdqeRR.mjs} +14 -15
  93. package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-N3hAEaAL.mjs} +5 -5
  94. package/dist/{run-CkE9y0nc.mjs → run-lAkle1-c.mjs} +5 -5
  95. package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-CkIjnegq.mjs} +5 -6
  96. package/dist/server-expo-DGsdUGX2.mjs +2 -0
  97. package/dist/{server-expo-CHTyXD0G.mjs → server-expo-n3tUi3Em.mjs} +5 -6
  98. package/dist/{settings-SR9OmW9h.mjs → settings-2am7TSxp.mjs} +5 -5
  99. package/dist/{settings-Cfd-0r2d.mjs → settings-CxgngUZk.mjs} +116 -103
  100. package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
  101. package/dist/settings.schema.json +198 -9
  102. package/dist/{simslim-BWNrc3M3.mjs → simslim-D9LkJxIO.mjs} +7 -7
  103. package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-HV_KtPIw.mjs} +6 -6
  104. package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
  105. package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
  106. package/dist/{stage-CzaiwFsI.mjs → stage-BgaZJVZY.mjs} +14 -3
  107. package/dist/{start-AlfCPRVU.mjs → start-D8NbxNBb.mjs} +1 -1
  108. package/dist/{start-DUz3mYdb.mjs → start-DtxthMfI.mjs} +34 -27
  109. package/dist/{state-PeQiJuIu.mjs → state-D2KtnALG.mjs} +4 -4
  110. package/dist/{state-DyaNTgfR.mjs → state-Tp4uImDS.mjs} +5 -3
  111. package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
  112. package/dist/{stats-Bq4fJOdB.mjs → stats-8EJ5ivFT.mjs} +6 -6
  113. package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
  114. package/dist/{status-BA2pehwf.mjs → status-BOyp0CUl.mjs} +31 -34
  115. package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-C65jk_q2.mjs} +1 -1
  116. package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-DbmUjsLv.mjs} +1 -1
  117. package/dist/stop-D0UIpZuh.mjs +72 -0
  118. package/dist/{stop-zF1y04f2.mjs → stop-DRipAg-e.mjs} +2 -2
  119. package/dist/{stop-DQIasgKp.mjs → stop-zDlknFoz.mjs} +79 -33
  120. package/dist/supervisor-run.d.mts +24 -22
  121. package/dist/supervisor-run.mjs +21 -22
  122. package/dist/{support-CsJNVfOc.mjs → support-CNqYkV0y.mjs} +7 -7
  123. package/dist/{support-WvD__tSV.mjs → support-DsNaFg6D.mjs} +5 -5
  124. package/dist/swiftpm-macos-aJzPAj1t.mjs +119 -0
  125. package/dist/{toolchain-DNyR3NPq.mjs → toolchain-C3xSoyUl.mjs} +475 -269
  126. package/dist/{trigger-Bemu8ytw.mjs → trigger-CAhmG-4s.mjs} +6 -6
  127. package/dist/trigger-CoalsMTV.mjs +2 -0
  128. package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-pFLB2e6P.mjs} +135 -38
  129. package/dist/{status-CzS4bPO9.mjs → watchman-BL1IdOtq.mjs} +75 -7
  130. package/dist/web-DFpFeZu5.mjs +2 -0
  131. package/dist/web-DbbrPUn0.mjs +127 -0
  132. package/dist/web-run.mjs +10 -10
  133. package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-CR9X86xJ.mjs} +3 -3
  134. package/dist/{ownership-BMLqyBgJ.mjs → workspaces-Djb2gfi9.mjs} +661 -23
  135. package/dist/{worktree-Be_7NLKy.mjs → worktree-BL4M1rhB.mjs} +252 -50
  136. package/dist/{worktree-j3IcFAI5.mjs → worktree-DV-A0LNL.mjs} +1 -1
  137. package/package.json +10 -5
  138. package/shim/bundle-response.cjs +2 -1
  139. package/shim/bundle-response.d.cts +1 -1
  140. package/shim/expo-metro-config.cjs +1 -1
  141. package/dist/build-progress-D0WkjCPd.mjs +0 -1036
  142. package/dist/build-slots-41v_d2x9.mjs +0 -142
  143. package/dist/cli-CYXb29oE.mjs +0 -51
  144. package/dist/deps-iKw0eD4P.mjs +0 -438
  145. package/dist/device-ios-C2Um-XUR.mjs +0 -412
  146. package/dist/errors-B6W7FkxQ.mjs +0 -15
  147. package/dist/guide-status-Du-ZQ3FD.mjs +0 -125
  148. package/dist/idle-BXG__1BA.mjs +0 -176
  149. package/dist/in-use-CvGOUn-x.mjs +0 -130
  150. package/dist/ios-BJ4XUSe1.mjs +0 -3867
  151. package/dist/native-run-D1Q56rAD.mjs +0 -158
  152. package/dist/preview-B-g9d7kV.mjs +0 -318
  153. package/dist/process-identity-BmRm1qV0.mjs +0 -12
  154. package/dist/project-D47Rh0ly.mjs +0 -276
  155. package/dist/server-expo-CeakhI8P.mjs +0 -2
  156. package/dist/state-BKa8wNN5.mjs +0 -148
  157. package/dist/stop-BIiM49EY.mjs +0 -48
  158. package/dist/stop-cause-rhmGfwvt.mjs +0 -97
  159. package/dist/trigger-CXts6qt1.mjs +0 -2
  160. package/dist/watchman-APYjXSQa.mjs +0 -48
  161. package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
  162. package/dist/workspaces-Bcib97-6.mjs +0 -306
@@ -1,14 +1,25 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
1
+ import { n as ownership_claim_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
2
3
  import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
3
- import { l as phaseLine } from "./command-output-CDAIFD-8.mjs";
4
- import { a as spawnDeclared } from "./spawn-claims-D0XfSe8u.mjs";
5
- import { o as androidHome } from "./android-B_YNZy61.mjs";
6
- import { O as readCcacheActivity, T as CCACHE_UNAVAILABLE, u as HEARTBEAT_INTERVAL_MS, y as startBuildHeartbeat } from "./toolchain-DNyR3NPq.mjs";
7
- import { createHash } from "node:crypto";
4
+ import { l as phaseLine } from "./command-output-Bt8ygHE7.mjs";
5
+ import { C as saveConfig, D as withConfigLock, H as projectDeviceSlots, _ as loadConfig, a as clearDevice, d as getConfigDir, w as setDevice, z as deviceSlotPlatforms } from "./config-DC5jEtd1.mjs";
6
+ import { p as sharedCompilationCache, y as workspaceId } from "./workspace-state-CR9X86xJ.mjs";
7
+ import { v as resolvePackageJson } from "./project-B5eQ1USs.mjs";
8
+ import { Ot as resolveOptimizations, v as iosSimSlimProfileSetting } from "./settings-CxgngUZk.mjs";
9
+ import { r as register } from "./cache-manifest-DiwKSyv9.mjs";
10
+ import { j as teardownParkedIosSim } from "./workspaces-Djb2gfi9.mjs";
11
+ import { a as spawnDeclared } from "./spawn-claims-Kqiifsk2.mjs";
12
+ import { ct as dropParked, ft as parkedMaxSetting, ht as selectParked, o as androidHome, pt as readParked, st as adoptParked } from "./android-BdQC6zs1.mjs";
13
+ import { C as resetIosPrivacy, M as readHostMemoryPressure, S as resetIosKeychain, T as resolveOwnedIosSim, a as createOwnedIosSim, d as listAllIosSims, g as ownedSimName, j as hostMemoryPressureAdvice, k as configuredIosSimulatorViewer, m as listIosRuntimes, n as bootIosSim, p as listIosDeviceTypes, u as iosSimulatorFailureAdvice, v as parseRuntimeVersion, w as resolveIosCreation, x as renameIosSim } from "./ios-BWbHFZVz.mjs";
14
+ import { a as withWorkspaceProcessLock } from "./named-ports-B4iWH4E-.mjs";
15
+ import { a as deviceTypeMismatch, c as runtimeMismatch, n as DeviceAdmissionRefusal, p as withDeviceBootAdmission } from "./device-capacity-BoN1jQkO.mjs";
16
+ import { o as readCcacheActivity, r as CCACHE_UNAVAILABLE } from "./manifest-CX0d1IkZ.mjs";
17
+ import { D as swiftPrefixMappingSupported, E as startBuildHeartbeat, S as pickXcodeProject, _ as buildXcode, b as detectSwiftVersion, g as SWIFT_PREFIX_MAPPING_MIN_SWIFT, h as HEARTBEAT_INTERVAL_MS, x as detectXcodeMajor, y as compilationCacheSettings } from "./toolchain-C3xSoyUl.mjs";
8
18
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
9
19
  import { dirname, isAbsolute, join, relative, sep } from "node:path";
10
20
  import { fileURLToPath } from "node:url";
11
21
  import chalk from "chalk";
22
+ import { createHash } from "node:crypto";
12
23
  /** The ABI name a build for every ABI has to fit; `stim android` narrows a build to the device's. */
13
24
  const LONGEST_ANDROID_ABI = "armeabi-v7a";
14
25
  function androidPathRoom(root, { abi = null, variant = null, platform = process.platform } = {}) {
@@ -290,44 +301,12 @@ function assembleTaskFor(variant) {
290
301
  }
291
302
  const LAST_LINES = 20;
292
303
  const TRANSCRIPT_LINES = 2e3;
293
- function androidDir(root) {
294
- return join(root, "android");
295
- }
296
- function gradlewPath(root) {
297
- return join(androidDir(root), process.platform === "win32" ? "gradlew.bat" : "gradlew");
298
- }
299
- function apkOutputsDir(root) {
300
- return join(androidDir(root), "app", "build", "outputs", "apk");
301
- }
302
- function debugApkDir(root) {
303
- return join(apkOutputsDir(root), "debug");
304
- }
305
- function discoverAndroidProject(root) {
306
- const dir = androidDir(root);
307
- if (!existsSync(dir)) return {
308
- failed: true,
309
- code: BUILD_ERROR,
310
- reason: `No android/ directory in ${root}.`,
311
- remedy: "Generate it (`npx expo prebuild -p android`, which `stim android` runs itself on an Expo project) or check out the native sources."
312
- };
313
- const gradlew = gradlewPath(root);
314
- if (!existsSync(gradlew)) return {
315
- failed: true,
316
- code: BUILD_ERROR,
317
- reason: `${gradlew} does not exist, so there is no gradle wrapper to build with.`,
318
- remedy: "Restore the wrapper (`gradle wrapper` in android/, or regenerate the project with `npx expo prebuild -p android --clean`)."
319
- };
320
- return {
321
- androidDir: dir,
322
- gradlew
323
- };
324
- }
325
- function androidSdkRefusal({ sdkPath, sdkExists, hasLocalProperties }) {
304
+ function androidSdkRefusal({ sdkPath, sdkExists, hasLocalProperties, localPropertiesPath }) {
326
305
  if (sdkExists || hasLocalProperties) return null;
327
306
  return {
328
307
  code: BUILD_ERROR,
329
308
  reason: `No Android SDK at ${sdkPath}.`,
330
- remedy: "Set ANDROID_HOME to the Android SDK (Android Studio installs it at ~/Library/Android/sdk), or write sdk.dir into android/local.properties. JAVA_HOME must point at a JDK 17 install as well."
309
+ remedy: `Set ANDROID_HOME to the Android SDK (Android Studio installs it at ~/Library/Android/sdk), or write sdk.dir into ${localPropertiesPath}. JAVA_HOME must point at a JDK 17 install as well.`
331
310
  };
332
311
  }
333
312
  function pickDebugApk(files) {
@@ -363,7 +342,7 @@ function variantNameOf(segments) {
363
342
  return (Array.isArray(segments) ? segments : []).filter((s) => typeof s === "string" && s.trim()).map((s, i) => i === 0 ? s : `${s[0].toUpperCase()}${s.slice(1)}`).join("");
364
343
  }
365
344
  function apkInDir(dir) {
366
- const metadata = readOrNull(join(dir, "output-metadata.json"));
345
+ const metadata = readOrNull$1(join(dir, "output-metadata.json"));
367
346
  if (metadata) {
368
347
  const named = parseOutputMetadata(metadata);
369
348
  if (named) {
@@ -392,8 +371,7 @@ function listApkSubdirs(base, prefix = [], depth = 0) {
392
371
  }
393
372
  return dirs;
394
373
  }
395
- function findVariantApkDir(root, variant) {
396
- const base = apkOutputsDir(root);
374
+ function findVariantApkDir(base, variant) {
397
375
  const wanted = variant.trim().toLowerCase();
398
376
  for (const segments of listApkSubdirs(base)) if (variantNameOf(segments).toLowerCase() === wanted) return join(base, ...segments);
399
377
  return null;
@@ -410,25 +388,25 @@ function findDebugApksUnder(base) {
410
388
  }
411
389
  return found.toSorted();
412
390
  }
413
- function locateApk(root, transcript = "", variant = null) {
391
+ function locateApk(project, transcript = "", variant = null) {
414
392
  const fromTranscript = parseApkFromTranscript(transcript);
415
393
  if (fromTranscript) {
416
- const abs = isAbsolute(fromTranscript) ? fromTranscript : join(androidDir(root), fromTranscript);
394
+ const abs = isAbsolute(fromTranscript) ? fromTranscript : join(project.directory, fromTranscript);
417
395
  if (existsSync(abs)) return { apkPath: abs };
418
396
  }
419
397
  if (variant) {
420
- const dir = findVariantApkDir(root, variant);
398
+ const dir = findVariantApkDir(project.outputsDir, variant);
421
399
  return { apkPath: dir ? apkInDir(dir) : null };
422
400
  }
423
- const direct = apkInDir(debugApkDir(root));
401
+ const direct = apkInDir(join(project.outputsDir, "debug"));
424
402
  if (direct) return { apkPath: direct };
425
- const found = findDebugApksUnder(apkOutputsDir(root));
403
+ const found = findDebugApksUnder(project.outputsDir);
426
404
  if (found.length === 1) {
427
405
  const apk = found[0];
428
- const suggested = variantNameOf(relative(apkOutputsDir(root), apk).split(sep).slice(0, -1));
406
+ const suggested = variantNameOf(relative(project.outputsDir, apk).split(sep).slice(0, -1));
429
407
  return {
430
408
  apkPath: apk,
431
- note: `no APK in ${relative(androidDir(root), debugApkDir(root))}; using ${relative(androidDir(root), apk)}${suggested ? ` -- set the android.variant setting to "${suggested}" to build this variant explicitly` : ""}`
409
+ note: `no APK in ${relative(project.directory, join(project.outputsDir, "debug"))}; using ${relative(project.directory, apk)}${suggested ? ` -- set the android.variant setting to "${suggested}" to build this variant explicitly` : ""}`
432
410
  };
433
411
  }
434
412
  if (found.length > 1) return {
@@ -585,48 +563,21 @@ function parseProductFlavors(source) {
585
563
  dimensions
586
564
  };
587
565
  }
588
- function readProductFlavors(root) {
589
- return parseProductFlavors(readOrNull(join(androidDir(root), "app", "build.gradle")));
590
- }
591
- function productFlavorRefusal({ flavors, variant }) {
592
- if (variant) return null;
593
- if (!flavors.known || flavors.dimensions.length === 0) return null;
594
- let combinations = [[]];
595
- for (const group of flavors.dimensions) combinations = combinations.flatMap((combination) => group.map((flavor) => combination.concat(flavor)));
596
- if (combinations.length < 2) return null;
597
- const count = flavors.dimensions.reduce((total, group) => total + group.length, 0);
598
- const variants = combinations.map((combination) => variantNameOf(combination.concat("debug")));
599
- return {
600
- code: "STIM_BAD_ARG",
601
- reason: `android/app/build.gradle declares ${count} product flavors, so \`./gradlew ${ASSEMBLE_TASK}\` builds an APK for each of them and nothing says which flavor to install.`,
602
- remedy: `Pass \`--variant ${variants[0]}\` or set the android.variant setting -- e.g. {"android": {"variant": "${variants[0]}"}} in .stim.json. The debug variants are: ${variants.join(", ")}.`
603
- };
604
- }
605
- function gradleArgs(task, { buildCache = true, abi = null } = {}) {
566
+ function gradleArgs(task, { buildCache = true, projectArgs = [] } = {}) {
606
567
  return [
607
568
  task,
608
569
  ...buildCache ? ["--build-cache"] : ["--no-build-cache"],
609
- ...abi ? [`-PreactNativeArchitectures=${abi}`] : []
570
+ ...projectArgs
610
571
  ];
611
572
  }
612
- async function buildAndroid({ root, logWriter, variant = null, abi = null }, { spawnFn = null, now = Date.now, env = process.env, buildCache = true, ccache = null, cas = null, pch = "auto", compilerCacheDisabled = false, heartbeatMs = HEARTBEAT_INTERVAL_MS, estimateMs = null, onHeartbeat = (line) => console.error(line), onNote = (line) => console.error(line), platform = process.platform } = {}) {
613
- const project = discoverAndroidProject(root);
614
- if (project.failed) return {
615
- ok: false,
616
- code: project.code,
617
- reason: project.reason,
618
- remedy: project.remedy,
619
- diagnostics: [],
620
- truncated: 0,
621
- lastLines: [],
622
- durationMs: 0
623
- };
573
+ async function buildGradle({ root, logWriter, variant = null, project, task, projectArgs = [], preflightFailure = null }, { spawnFn = null, now = Date.now, env = process.env, buildCache = true, ccache = null, cas = null, pch = "auto", compilerCacheDisabled = false, heartbeatMs = HEARTBEAT_INTERVAL_MS, estimateMs = null, onHeartbeat = (line) => console.error(line), onNote = (line) => console.error(line) } = {}) {
624
574
  const sdk = androidHome(env);
625
575
  const sdkExists = existsSync(sdk);
626
576
  const refusal = androidSdkRefusal({
627
577
  sdkPath: sdk,
628
578
  sdkExists,
629
- hasLocalProperties: existsSync(join(project.androidDir, "local.properties"))
579
+ hasLocalProperties: existsSync(join(project.directory, "local.properties")),
580
+ localPropertiesPath: relative(root, join(project.directory, "local.properties")).split(sep).join("/")
630
581
  });
631
582
  if (refusal) return {
632
583
  ok: false,
@@ -636,26 +587,21 @@ async function buildAndroid({ root, logWriter, variant = null, abi = null }, { s
636
587
  lastLines: [],
637
588
  durationMs: 0
638
589
  };
639
- const room = androidPathRoom(root, {
640
- abi,
641
- variant,
642
- platform
643
- });
644
- if (room) return {
590
+ if (preflightFailure) return {
645
591
  ok: false,
646
- code: "STIM_PATH_TOO_LONG",
647
- reason: androidPathRoomMessage(room),
648
- remedy: androidPathRoomRemedy(room),
592
+ ...preflightFailure,
649
593
  diagnostics: [],
650
594
  truncated: 0,
651
595
  lastLines: [],
652
596
  durationMs: 0
653
597
  };
598
+ const wrapper = `./${relative(project.directory, project.gradlew).split(sep).join("/")}`;
599
+ const moduleTask = task.includes(":") ? task : `${project.module === ":" ? "" : project.module}:${task}`;
600
+ const tasksTask = `${project.module === ":" ? "" : project.module}:tasks`;
654
601
  const spawn = spawnFn || ((cmd, args, opts) => getExecutor().spawn(cmd, args, opts));
655
- const task = assembleTaskFor(variant);
656
602
  const args = gradleArgs(task, {
657
603
  buildCache,
658
- abi
604
+ projectArgs
659
605
  });
660
606
  if (cas) {
661
607
  args.push("--init-script", cas.initScript);
@@ -709,7 +655,7 @@ async function buildAndroid({ root, logWriter, variant = null, abi = null }, { s
709
655
  let child;
710
656
  try {
711
657
  child = spawnDeclared(() => spawn(project.gradlew, args, {
712
- cwd: project.androidDir,
658
+ cwd: project.directory,
713
659
  stdio: [
714
660
  "ignore",
715
661
  "pipe",
@@ -726,7 +672,7 @@ async function buildAndroid({ root, logWriter, variant = null, abi = null }, { s
726
672
  }
727
673
  }));
728
674
  } catch (err) {
729
- return spawnFailure(err, project, now() - startedAt);
675
+ return spawnFailure(err, root, project, now() - startedAt);
730
676
  }
731
677
  const outReader = createLineReader(push);
732
678
  const errReader = createLineReader(push);
@@ -748,7 +694,7 @@ async function buildAndroid({ root, logWriter, variant = null, abi = null }, { s
748
694
  const transcript = window.join("\n");
749
695
  const ccacheActivity = ccache ? readCcacheActivity(ccache.statsLog) : CCACHE_UNAVAILABLE;
750
696
  if (result.error) return {
751
- ...spawnFailure(result.error, project, durationMs),
697
+ ...spawnFailure(result.error, root, project, durationMs),
752
698
  lastLines: tail.slice(),
753
699
  ccache: ccacheActivity
754
700
  };
@@ -758,7 +704,7 @@ async function buildAndroid({ root, logWriter, variant = null, abi = null }, { s
758
704
  return {
759
705
  ok: false,
760
706
  code: BUILD_ERROR,
761
- reason: `\`./gradlew ${task}\` failed (${how}).`,
707
+ reason: `\`${wrapper} ${task}\` failed (${how}).`,
762
708
  diagnostics: shown,
763
709
  truncated,
764
710
  lastLines: tail.slice(),
@@ -766,23 +712,23 @@ async function buildAndroid({ root, logWriter, variant = null, abi = null }, { s
766
712
  ccache: ccacheActivity
767
713
  };
768
714
  }
769
- const located = locateApk(root, transcript, variant);
715
+ const located = locateApk(project, transcript, variant);
770
716
  if (!located.apkPath && located.candidates?.length) return {
771
717
  ok: false,
772
718
  code: BUILD_ERROR,
773
- reason: `\`./gradlew ${task}\` left ${located.candidates.length} debug APKs under ${apkOutputsDir(root)}, and nothing says which flavor to install.`,
774
- remedy: `Set the android.variant setting to the variant to install -- e.g. {"android": {"variant": "${variantNameOf(relative(apkOutputsDir(root), located.candidates[0]).split(sep).slice(0, -1))}"}} in .stim.json.`,
719
+ reason: `\`${wrapper} ${task}\` left ${located.candidates.length} debug APKs under ${project.outputsDir}, and nothing says which flavor to install.`,
720
+ remedy: `Set the android.variant setting to the variant to install -- e.g. {"android": {"variant": "${variantNameOf(relative(project.outputsDir, located.candidates[0]).split(sep).slice(0, -1))}"}} in .stim.json.`,
775
721
  diagnostics: [],
776
722
  truncated: 0,
777
- lastLines: located.candidates.map((c) => relative(androidDir(root), c)),
723
+ lastLines: located.candidates.map((c) => relative(project.directory, c)),
778
724
  durationMs,
779
725
  ccache: ccacheActivity
780
726
  };
781
727
  if (!located.apkPath) return {
782
728
  ok: false,
783
729
  code: BUILD_ERROR,
784
- reason: variant ? `\`./gradlew ${task}\` succeeded but produced no APK under ${apkOutputsDir(root)} for variant "${variant}".` : `\`./gradlew ${task}\` succeeded but produced no APK in ${debugApkDir(root)}.`,
785
- remedy: variant ? `Check that the android.variant setting ("${variant}") names a real variant (\`./gradlew :app:tasks\` lists the assemble tasks).` : `Check that ${task} builds the app module (\`./gradlew :app:${task}\` in android/) and that no flavour redirects the output.`,
730
+ reason: variant ? `\`${wrapper} ${task}\` succeeded but produced no APK under ${project.outputsDir} for variant "${variant}".` : `\`${wrapper} ${task}\` succeeded but produced no APK in ${join(project.outputsDir, "debug")}.`,
731
+ remedy: variant ? `Check that the android.variant setting ("${variant}") names a real variant (\`${wrapper} ${tasksTask}\` lists the assemble tasks).` : `Check that ${task} builds the ${project.module.replace(/^:/, "") || "root"} module (\`${wrapper} ${moduleTask}\` in ${relative(root, project.directory).split(sep).join("/") || "."}/) and that no flavour redirects the output.`,
786
732
  diagnostics: [],
787
733
  truncated: 0,
788
734
  lastLines: tail.slice(),
@@ -804,7 +750,7 @@ function resetStatsLog(statsLog) {
804
750
  rmSync(statsLog, { force: true });
805
751
  } catch {}
806
752
  }
807
- function spawnFailure(err, project, durationMs) {
753
+ function spawnFailure(err, root, project, durationMs) {
808
754
  const nodeErr = err;
809
755
  const message = String(nodeErr?.message || err || "");
810
756
  const permissionDenied = nodeErr?.code === "EACCES" || /EACCES|permission denied/i.test(message);
@@ -812,7 +758,7 @@ function spawnFailure(err, project, durationMs) {
812
758
  ok: false,
813
759
  code: BUILD_ERROR,
814
760
  reason: `Could not run ${project.gradlew}: ${message}`,
815
- remedy: permissionDenied ? `Make the wrapper executable: \`chmod +x ${project.gradlew}\`.` : "Check that the gradle wrapper is intact (android/gradlew and android/gradle/wrapper/) and that JAVA_HOME points at a JDK 17 install.",
761
+ remedy: permissionDenied ? `Make the wrapper executable: \`chmod +x ${project.gradlew}\`.` : `Check that the gradle wrapper is intact (${relative(root, project.gradlew)} and ${relative(root, join(project.directory, "gradle", "wrapper"))}/) and that JAVA_HOME points at a JDK 17 install.`,
816
762
  diagnostics: [],
817
763
  truncated: 0,
818
764
  lastLines: [],
@@ -830,7 +776,7 @@ function safeList(dir) {
830
776
  return [];
831
777
  }
832
778
  }
833
- function readOrNull(file) {
779
+ function readOrNull$1(file) {
834
780
  try {
835
781
  return readFileSync(file, "utf-8");
836
782
  } catch {
@@ -838,4 +784,612 @@ function readOrNull(file) {
838
784
  }
839
785
  }
840
786
  //#endregion
841
- export { gradlewPath as a, formatDiagnostic as c, androidPathRoomRemedy as d, buildAndroid as i, androidPathRoom as l, apkOutputsDir as n, productFlavorRefusal as o, assembleTaskFor as r, readProductFlavors as s, androidSdkRefusal as t, androidPathRoomMessage as u };
787
+ //#region src/integrations/react-native-build.ts
788
+ function androidDir(root) {
789
+ return join(root, "android");
790
+ }
791
+ function gradlewPath(root) {
792
+ return join(androidDir(root), process.platform === "win32" ? "gradlew.bat" : "gradlew");
793
+ }
794
+ function apkOutputsDir(root) {
795
+ return join(androidDir(root), "app", "build", "outputs", "apk");
796
+ }
797
+ function discoverAndroidProject(root) {
798
+ const dir = androidDir(root);
799
+ if (!existsSync(dir)) return {
800
+ failed: true,
801
+ code: BUILD_ERROR,
802
+ reason: `No android/ directory in ${root}.`,
803
+ remedy: "Generate it (`npx expo prebuild -p android`, which `stim android` runs itself on an Expo project) or check out the native sources."
804
+ };
805
+ const gradlew = gradlewPath(root);
806
+ if (!existsSync(gradlew)) return {
807
+ failed: true,
808
+ code: BUILD_ERROR,
809
+ reason: `${gradlew} does not exist, so there is no gradle wrapper to build with.`,
810
+ remedy: "Restore the wrapper (`gradle wrapper` in android/, or regenerate the project with `npx expo prebuild -p android --clean`)."
811
+ };
812
+ return {
813
+ androidDir: dir,
814
+ gradlew
815
+ };
816
+ }
817
+ function readProductFlavors(root) {
818
+ return parseProductFlavors(readOrNull(join(androidDir(root), "app", "build.gradle")));
819
+ }
820
+ function productFlavorRefusal({ flavors, variant }) {
821
+ if (variant) return null;
822
+ if (!flavors.known || flavors.dimensions.length === 0) return null;
823
+ let combinations = [[]];
824
+ for (const group of flavors.dimensions) combinations = combinations.flatMap((combination) => group.map((flavor) => combination.concat(flavor)));
825
+ if (combinations.length < 2) return null;
826
+ const count = flavors.dimensions.reduce((total, group) => total + group.length, 0);
827
+ const variants = combinations.map((combination) => variantNameOf(combination.concat("debug")));
828
+ return {
829
+ code: "STIM_BAD_ARG",
830
+ reason: `android/app/build.gradle declares ${count} product flavors, so \`./gradlew ${ASSEMBLE_TASK}\` builds an APK for each of them and nothing says which flavor to install.`,
831
+ remedy: `Pass \`--variant ${variants[0]}\` or set the android.variant setting -- e.g. {"android": {"variant": "${variants[0]}"}} in .stim.json. The debug variants are: ${variants.join(", ")}.`
832
+ };
833
+ }
834
+ function readOrNull(path) {
835
+ try {
836
+ return readFileSync(path, "utf-8");
837
+ } catch {
838
+ return null;
839
+ }
840
+ }
841
+ async function buildAndroid({ root, logWriter, variant = null, abi = null }, options = {}) {
842
+ const project = discoverAndroidProject(root);
843
+ if (project.failed) return {
844
+ ok: false,
845
+ code: project.code,
846
+ reason: project.reason,
847
+ remedy: project.remedy,
848
+ diagnostics: [],
849
+ truncated: 0,
850
+ lastLines: [],
851
+ durationMs: 0
852
+ };
853
+ const room = androidPathRoom(root, {
854
+ abi,
855
+ variant,
856
+ platform: options.platform ?? process.platform
857
+ });
858
+ return buildGradle({
859
+ root,
860
+ logWriter,
861
+ variant,
862
+ project: {
863
+ directory: project.androidDir,
864
+ gradlew: project.gradlew,
865
+ module: ":app",
866
+ outputsDir: apkOutputsDir(root)
867
+ },
868
+ task: assembleTaskFor(variant),
869
+ projectArgs: abi ? [`-PreactNativeArchitectures=${abi}`] : [],
870
+ preflightFailure: room ? {
871
+ code: "STIM_PATH_TOO_LONG",
872
+ reason: androidPathRoomMessage(room),
873
+ remedy: androidPathRoomRemedy(room)
874
+ } : null
875
+ }, options);
876
+ }
877
+ const IOS_DIR = "ios";
878
+ 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.";
879
+ function buildFailure(message, remedy) {
880
+ return { error: {
881
+ code: "STIM_BUILD_FAILED",
882
+ message,
883
+ remedy
884
+ } };
885
+ }
886
+ function discoverXcodeProject(root) {
887
+ const dir = join(root, IOS_DIR);
888
+ let entries;
889
+ try {
890
+ entries = readdirSync(dir);
891
+ } catch {
892
+ return buildFailure(`No ${IOS_DIR}/ directory in ${root}.`, PREBUILD_REMEDY);
893
+ }
894
+ const picked = pickXcodeProject(entries);
895
+ if (!picked) return buildFailure(`${dir} contains no .xcworkspace and no .xcodeproj.`, PREBUILD_REMEDY);
896
+ return {
897
+ ...picked,
898
+ dir,
899
+ path: join(dir, picked.file)
900
+ };
901
+ }
902
+ function ccacheEnabled(podfileProperties) {
903
+ if (!podfileProperties || typeof podfileProperties !== "object") return false;
904
+ return podfileProperties["apple.ccacheEnabled"] === "true";
905
+ }
906
+ function readPodfileProperties(root) {
907
+ try {
908
+ const parsed = JSON.parse(readFileSync(join(root, IOS_DIR, "Podfile.properties.json"), "utf-8"));
909
+ return parsed && typeof parsed === "object" ? parsed : null;
910
+ } catch {
911
+ return null;
912
+ }
913
+ }
914
+ const SWIFT_CACHE_MIN_REACT_NATIVE = {
915
+ major: 0,
916
+ minor: 87
917
+ };
918
+ function reactNativeSupportsSwiftCache(version) {
919
+ if (!version) return false;
920
+ const min = SWIFT_CACHE_MIN_REACT_NATIVE;
921
+ return version.major > min.major || version.major === min.major && version.minor >= min.minor;
922
+ }
923
+ function parseReactNativeVersion(packageJson) {
924
+ if (!packageJson || typeof packageJson !== "object") return null;
925
+ const raw = packageJson.version;
926
+ const m = typeof raw === "string" ? /^(\d+)\.(\d+)\./.exec(raw) : null;
927
+ if (!m || m[1] === void 0 || m[2] === void 0) return null;
928
+ return {
929
+ major: parseInt(m[1], 10),
930
+ minor: parseInt(m[2], 10)
931
+ };
932
+ }
933
+ function detectReactNativeVersion(root) {
934
+ const file = resolvePackageJson(root, "react-native");
935
+ if (!file) return null;
936
+ try {
937
+ return parseReactNativeVersion(JSON.parse(readFileSync(file, "utf-8")));
938
+ } catch {
939
+ return null;
940
+ }
941
+ }
942
+ function reactNativeCompilationCacheSettings({ reactNativeVersion = null, ...options }) {
943
+ return compilationCacheSettings({
944
+ ...options,
945
+ swiftCacheCompatible: reactNativeSupportsSwiftCache(reactNativeVersion)
946
+ });
947
+ }
948
+ function resolveCompilationCacheSettings({ root, derivedDataPath, exec = null, casPath = sharedCompilationCache(), optimizations = resolveOptimizations({}, {}).ios, onNote = (line) => console.error(line) }) {
949
+ const xcodeMajor = detectXcodeMajor(exec);
950
+ const ccache = ccacheEnabled(readPodfileProperties(root));
951
+ const probe = xcodeMajor !== null && xcodeMajor >= 26 && !ccache && optimizations.compilationCache && optimizations.swiftCompilationCache !== false;
952
+ const swiftVersion = probe ? detectSwiftVersion(exec) : null;
953
+ const reactNativeVersion = probe ? detectReactNativeVersion(root) : null;
954
+ const settings = reactNativeCompilationCacheSettings({
955
+ workspaceRoot: root,
956
+ derivedDataPath,
957
+ casPath,
958
+ xcodeMajor,
959
+ swiftVersion,
960
+ reactNativeVersion,
961
+ ccache,
962
+ optimizations
963
+ });
964
+ if (settings.length > 0 && optimizations.compilationCache) {
965
+ register({
966
+ dir: casPath,
967
+ name: "Xcode compilation cache",
968
+ prune: "atomic",
969
+ note: "shared Xcode compilation cache"
970
+ });
971
+ const swift = settings.includes("SWIFT_ENABLE_COMPILE_CACHE=YES") ? settings.includes("SWIFT_ENABLE_PREFIX_MAPPING=YES") ? "Swift on" : "Swift on, unmapped" : swiftVersion && !swiftPrefixMappingSupported(swiftVersion) ? `Swift off, ${swiftVersion.major}.${swiftVersion.minor} < ${SWIFT_PREFIX_MAPPING_MIN_SWIFT.major}.${SWIFT_PREFIX_MAPPING_MIN_SWIFT.minor}` : swiftVersion && reactNativeVersion && !reactNativeSupportsSwiftCache(reactNativeVersion) ? `Swift off, react-native ${reactNativeVersion.major}.${reactNativeVersion.minor} < ${SWIFT_CACHE_MIN_REACT_NATIVE.major}.${SWIFT_CACHE_MIN_REACT_NATIVE.minor}` : "Swift off";
972
+ onNote(chalk.dim(phaseLine("cache", `compilation cache on (CAS at ${casPath}, ${swift})`)));
973
+ }
974
+ return settings;
975
+ }
976
+ async function buildIos(inputs) {
977
+ const { root, logWriter, udid, destination, now = () => Date.now() } = inputs;
978
+ if (!root || typeof root !== "string") throw new TypeError("buildIos requires {root}");
979
+ if (!logWriter || typeof logWriter.write !== "function") throw new TypeError("buildIos requires {logWriter} with a write() method");
980
+ if (!udid && !destination) throw new TypeError("buildIos requires {udid} (or an explicit {destination})");
981
+ const startedAt = now();
982
+ return buildXcode({
983
+ ...inputs,
984
+ startedAt,
985
+ project: inputs.project ?? discoverXcodeProject(root),
986
+ compilationCache: inputs.compilationCache === void 0 ? ({ derivedDataPath, exec }) => resolveCompilationCacheSettings({
987
+ root,
988
+ derivedDataPath,
989
+ exec,
990
+ optimizations: inputs.optimizations,
991
+ onNote: inputs.onNote
992
+ }) : inputs.compilationCache
993
+ });
994
+ }
995
+ //#endregion
996
+ //#region src/engine/device-ios.ts
997
+ function startIosBoot(udid, configure, label, out, simulatorApp, root, deviceSlotWait) {
998
+ const done = withDeviceBootAdmission({
999
+ platform: "ios",
1000
+ key: udid
1001
+ }, async () => {
1002
+ out(chalk.dim(phaseLine("device", `booting ${label} (${udid})`)));
1003
+ await bootIosSim(udid, {
1004
+ label,
1005
+ out,
1006
+ simulatorApp
1007
+ });
1008
+ await configure();
1009
+ }, {
1010
+ ...deviceSlotWait,
1011
+ root,
1012
+ out
1013
+ });
1014
+ done.catch(() => {});
1015
+ return {
1016
+ udid,
1017
+ done
1018
+ };
1019
+ }
1020
+ var IosDeviceMismatchError = class extends Error {
1021
+ remedy = "Run `stim worktree remove` (or `stim gc --delete`) to reap the current sim, then `stim ios` again to create the requested one, or pass `--slot <name>` to create it beside the current one.";
1022
+ };
1023
+ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, label, settings, flags, note, out, reconcileIosSimulator, simSnapshot }) {
1024
+ const memoryAdvice = hostMemoryPressureAdvice(readHostMemoryPressure());
1025
+ if (memoryAdvice) out(chalk.yellow(phaseLine("memory", memoryAdvice)));
1026
+ const simslimProfile = iosSimSlimProfileSetting(settings, settingsRoot);
1027
+ if (record?.deviceUdid) {
1028
+ if (record.owned) {
1029
+ const listed = simSnapshot ? simSnapshot.read() : listAllIosSims({ includeUnavailable: true });
1030
+ const resolved = resolveOwnedIosSim(record.deviceUdid, listed);
1031
+ if (resolved.notOwned) note(chalk.yellow(`Note: recorded sim is now named "${resolved.notOwned}", not Stim-owned -- creating a fresh owned sim instead of booting it.`));
1032
+ else if (resolved.missing) {} else {
1033
+ const sim = resolved.sim;
1034
+ const wantedType = flags.deviceType || settings?.ios?.deviceType;
1035
+ const deviceTypes = listIosDeviceTypes();
1036
+ const mismatch = deviceTypeMismatch(sim.deviceTypeIdentifier, wantedType, deviceTypes);
1037
+ if (mismatch) throw new IosDeviceMismatchError(`${mismatch}. Stim will not silently boot a different model.`);
1038
+ const versionMismatch = flags.runtimeFlag ? runtimeMismatch(sim.runtime, flags.runtimeFlag, listIosRuntimes()) : null;
1039
+ if (versionMismatch) throw new IosDeviceMismatchError(`${versionMismatch}. Stim will not silently boot a different iOS version.`);
1040
+ const model = {
1041
+ model: deviceTypes.find((d) => d.identifier === sim.deviceTypeIdentifier)?.name ?? null,
1042
+ runtime: parseRuntimeVersion(sim.runtime)
1043
+ };
1044
+ const name = ownedIosName(sim, label, model, projectPath, listed) === sim.name ? sim.name : await withIosDeviceNameLock(() => renameToOwnedName(sim, label, model, projectPath));
1045
+ const updated = {
1046
+ deviceUdid: sim.udid,
1047
+ owned: true,
1048
+ deviceName: name,
1049
+ ...record.simslimManaged ? { simslimManaged: true } : {},
1050
+ ...record.adopted ? { adopted: true } : {},
1051
+ ...record.adoptionPending ? { adoptionPending: true } : {},
1052
+ ...record.parkedCacheKey ? { parkedCacheKey: record.parkedCacheKey } : {},
1053
+ ...record.schemeApprovals ? { schemeApprovals: record.schemeApprovals } : {}
1054
+ };
1055
+ const configure = async () => {
1056
+ if (record.adoptionPending) resetAdoptedSim(sim.udid, out);
1057
+ return configureOwnedIosSim({
1058
+ record: updated,
1059
+ projectPath,
1060
+ slot,
1061
+ profile: simslimProfile,
1062
+ out,
1063
+ reconcileIosSimulator
1064
+ });
1065
+ };
1066
+ const facts = {
1067
+ deviceType: model.model,
1068
+ runtime: model.runtime
1069
+ };
1070
+ if (sim.state !== "Booted") return {
1071
+ ...updated,
1072
+ booting: startIosBoot(sim.udid, configure, name, out, flags.simulatorApp, projectPath, flags.deviceSlotWait),
1073
+ ...facts
1074
+ };
1075
+ return {
1076
+ ...await configure(),
1077
+ ...facts,
1078
+ booted: sim.udid
1079
+ };
1080
+ }
1081
+ } else {
1082
+ const sim = listAllIosSims().find((s) => s.udid === record.deviceUdid);
1083
+ if (sim) {
1084
+ if (sim.state !== "Booted") {
1085
+ note(chalk.yellow(`Note: assigned sim ${sim.name} (${sim.udid}) is shut down and is not owned by Stim, so it will not be booted automatically.`));
1086
+ note(chalk.dim("Boot it yourself, or run `stim gc --delete` to clear the assignment so Stim can create an owned sim."));
1087
+ }
1088
+ return record;
1089
+ }
1090
+ }
1091
+ withConfigLock(() => {
1092
+ const current = deviceSlotPlatforms(loadConfig()?.projects?.[projectPath], slot)?.ios;
1093
+ if (!current) return;
1094
+ if (current.deviceUdid !== record.deviceUdid || current.owned !== record.owned) throw new Error("The simulator assignment changed during recovery. Run `stim ios` again.");
1095
+ clearDevice(projectPath, "ios", slot);
1096
+ });
1097
+ }
1098
+ const choice = resolveIosCreation({
1099
+ deviceType: flags.deviceType || settings.ios?.deviceType,
1100
+ runtime: flags.runtime || settings.ios?.runtime
1101
+ });
1102
+ const adopted = parkedMaxSetting("ios").max > 0 ? await withIosDeviceNameLock(() => takeParkedIosSim({
1103
+ projectPath,
1104
+ slot,
1105
+ label,
1106
+ choice,
1107
+ out
1108
+ })) : null;
1109
+ if (adopted) {
1110
+ const booting = startIosBoot(adopted.deviceUdid, async () => {
1111
+ resetAdoptedSim(adopted.deviceUdid, out);
1112
+ await configureOwnedIosSim({
1113
+ record: adopted,
1114
+ projectPath,
1115
+ slot,
1116
+ profile: simslimProfile,
1117
+ out,
1118
+ reconcileIosSimulator
1119
+ });
1120
+ }, adopted.deviceName, out, flags.simulatorApp, projectPath, flags.deviceSlotWait);
1121
+ return {
1122
+ ...adopted,
1123
+ booting,
1124
+ deviceType: choice.deviceType,
1125
+ runtime: choice.runtime
1126
+ };
1127
+ }
1128
+ const created = await withIosDeviceNameLock(() => {
1129
+ const suffix = ownedIosNameSuffix(label, {
1130
+ model: choice.deviceType,
1131
+ runtime: choice.runtime
1132
+ }, projectPath);
1133
+ const result = createOwnedIosSim(label, { suffix }, choice);
1134
+ setDevice(projectPath, "ios", {
1135
+ deviceUdid: result.udid,
1136
+ owned: true,
1137
+ deviceName: result.name
1138
+ }, slot);
1139
+ return result;
1140
+ });
1141
+ const newRecord = {
1142
+ deviceUdid: created.udid,
1143
+ owned: true,
1144
+ deviceName: created.name
1145
+ };
1146
+ const booting = startIosBoot(created.udid, () => configureOwnedIosSim({
1147
+ record: newRecord,
1148
+ projectPath,
1149
+ slot,
1150
+ profile: simslimProfile,
1151
+ out,
1152
+ reconcileIosSimulator
1153
+ }), created.name, out, flags.simulatorApp, projectPath, flags.deviceSlotWait);
1154
+ return {
1155
+ ...newRecord,
1156
+ created: true,
1157
+ booting,
1158
+ deviceType: created.deviceType,
1159
+ runtime: created.runtime
1160
+ };
1161
+ }
1162
+ function withIosDeviceNameLock(fn) {
1163
+ return withWorkspaceProcessLock(getConfigDir(), "ios-device-names", async () => fn(), { external: true });
1164
+ }
1165
+ function ownedIosNameSuffix(label, model, projectPath, udid, listed = listAllIosSims({ includeUnavailable: true })) {
1166
+ const names = new Set(listed.filter((sim) => sim.udid !== udid).map((sim) => sim.name));
1167
+ for (const project of Object.values(loadConfig()?.projects ?? {})) for (const { platforms } of projectDeviceSlots(project)) {
1168
+ const record = platforms.ios;
1169
+ if (record?.deviceUdid !== udid && record?.deviceName) names.add(record.deviceName);
1170
+ }
1171
+ for (const parked of readParked("ios")) if (parked.udid !== udid) names.add(parked.name);
1172
+ let suffix = "";
1173
+ for (let attempt = 0; names.has(ownedSimName(label, model, suffix)); attempt++) suffix = ` ${workspaceId(projectPath)}${attempt ? `-${attempt}` : ""}`;
1174
+ return suffix;
1175
+ }
1176
+ function ownedIosName(sim, label, model, projectPath, listed) {
1177
+ return ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, sim.udid, listed));
1178
+ }
1179
+ function renameToOwnedName(sim, label, model, projectPath) {
1180
+ const wanted = ownedIosName(sim, label, model, projectPath);
1181
+ if (sim.name === wanted) return wanted;
1182
+ try {
1183
+ renameIosSim(sim.udid, wanted);
1184
+ return wanted;
1185
+ } catch {
1186
+ return sim.name;
1187
+ }
1188
+ }
1189
+ function resetAdoptedSim(udid, out) {
1190
+ out(chalk.dim(phaseLine("device", `resetting privacy grants and the keychain on ${udid}`)));
1191
+ resetIosPrivacy(udid);
1192
+ resetIosKeychain(udid);
1193
+ }
1194
+ function takeParkedIosSim({ projectPath, slot, label, choice, out }) {
1195
+ const candidates = selectParked(readParked("ios"), {
1196
+ deviceTypeIdentifier: choice.deviceTypeId,
1197
+ runtimeIdentifier: choice.runtimeId
1198
+ });
1199
+ if (candidates.length === 0) return null;
1200
+ const listed = new Map(listAllIosSims({ includeUnavailable: true }).map((sim) => [sim.udid, sim]));
1201
+ for (const parked of candidates) {
1202
+ const sim = listed.get(parked.udid);
1203
+ if (!sim || !sim.available) {
1204
+ const result = sim ? teardownParkedIosSim(parked.udid, { label: parked.name }) : null;
1205
+ if (sim ? result?.status === "torn-down" : dropParked("ios", parked.udid)) out(chalk.dim(phaseLine("device", `deleted parked ${parked.name} (${parked.udid}): ${sim ? "unavailable" : "gone"}`)));
1206
+ else if (result?.status === "failed") out(chalk.yellow(phaseLine("device", `could not delete unavailable parked ${parked.name} (${parked.udid}): ${result.reason}`)));
1207
+ continue;
1208
+ }
1209
+ if (!sim.name.startsWith("stim-")) {
1210
+ out(chalk.yellow(phaseLine("device", `kept parked record for ${parked.udid}: simulator is now named ${JSON.stringify(sim.name)} and is not Stim-owned`)));
1211
+ continue;
1212
+ }
1213
+ const model = {
1214
+ model: choice.deviceType,
1215
+ runtime: choice.runtime
1216
+ };
1217
+ const name = ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, parked.udid));
1218
+ const device = {
1219
+ deviceUdid: parked.udid,
1220
+ owned: true,
1221
+ deviceName: name,
1222
+ adopted: true,
1223
+ adoptionPending: true,
1224
+ ...parked.simslimManaged ? { simslimManaged: true } : {},
1225
+ ...parked.cacheKey ? { parkedCacheKey: parked.cacheKey } : {}
1226
+ };
1227
+ const adopted = adoptParked({
1228
+ platform: "ios",
1229
+ projectPath,
1230
+ slot,
1231
+ udid: parked.udid,
1232
+ device
1233
+ });
1234
+ if (!adopted) continue;
1235
+ try {
1236
+ renameIosSim(parked.udid, name);
1237
+ } catch {}
1238
+ return adopted.schemeApprovals && adopted.schemeApprovals.length > 0 ? {
1239
+ ...device,
1240
+ schemeApprovals: adopted.schemeApprovals
1241
+ } : device;
1242
+ }
1243
+ return null;
1244
+ }
1245
+ function clearIosAdoptionPending(projectPath, slot = "default") {
1246
+ withConfigLock(() => {
1247
+ const cfg = loadConfig();
1248
+ const ios = deviceSlotPlatforms(cfg?.projects?.[projectPath], slot)?.ios;
1249
+ if (!cfg || !ios?.adoptionPending) return;
1250
+ delete ios.adopted;
1251
+ delete ios.adoptionPending;
1252
+ delete ios.parkedCacheKey;
1253
+ saveConfig(cfg);
1254
+ });
1255
+ }
1256
+ async function configureOwnedIosSim({ record, projectPath, slot, profile, out, reconcileIosSimulator }) {
1257
+ if (profile) out(chalk.dim(phaseLine("device", `applying the SimSlim profile to ${record.deviceUdid}`)));
1258
+ else if (record.simslimManaged) out(chalk.dim(phaseLine("device", `restoring stock simulator services on ${record.deviceUdid}`)));
1259
+ const previouslyManaged = Boolean(record.simslimManaged);
1260
+ if (profile && !record.simslimManaged) {
1261
+ const pending = {
1262
+ ...record,
1263
+ simslimManaged: true
1264
+ };
1265
+ setDevice(projectPath, "ios", pending, slot);
1266
+ record = pending;
1267
+ }
1268
+ const result = await reconcileIosSimulator({
1269
+ udid: record.deviceUdid,
1270
+ profile,
1271
+ previouslyManaged,
1272
+ out
1273
+ });
1274
+ const updated = { ...record };
1275
+ if (result.managed) updated.simslimManaged = true;
1276
+ else delete updated.simslimManaged;
1277
+ setDevice(projectPath, "ios", updated, slot);
1278
+ return updated;
1279
+ }
1280
+ async function ensureIosBooted({ device, projectPath, deviceSlotWait, simulatorApp, timeoutMs, pollMs, out }) {
1281
+ const udid = device?.deviceUdid;
1282
+ if (!udid) return {
1283
+ failed: true,
1284
+ reason: "No iOS simulator is recorded for this project."
1285
+ };
1286
+ const ready = () => {
1287
+ try {
1288
+ getExecutor().runFile("xcrun", [
1289
+ "simctl",
1290
+ "spawn",
1291
+ udid,
1292
+ "launchctl",
1293
+ "list"
1294
+ ], { timeoutMs: 3e4 });
1295
+ return {
1296
+ ok: true,
1297
+ udid
1298
+ };
1299
+ } catch (error) {
1300
+ return {
1301
+ failed: true,
1302
+ reason: `Simulator ${udid} failed its process-spawn readiness check: ${error?.message || error}. ${iosSimulatorFailureAdvice()}`
1303
+ };
1304
+ }
1305
+ };
1306
+ const booting = device?.booting;
1307
+ if (booting?.udid === udid) {
1308
+ try {
1309
+ await booting.done;
1310
+ } catch (e) {
1311
+ return bootFailure(udid, e);
1312
+ }
1313
+ return ready();
1314
+ }
1315
+ const readiness = device?.booted === udid ? ready() : null;
1316
+ if (readiness?.ok) {
1317
+ if (simulatorApp !== void 0) configuredIosSimulatorViewer(simulatorApp).open(udid);
1318
+ return readiness;
1319
+ }
1320
+ let resolved;
1321
+ try {
1322
+ resolved = resolveOwnedIosSim(udid);
1323
+ } catch (e) {
1324
+ return {
1325
+ failed: true,
1326
+ reason: `Could not list simulators: ${e?.message || e}`
1327
+ };
1328
+ }
1329
+ if (resolved.missing) return {
1330
+ failed: true,
1331
+ reason: `Simulator ${udid} no longer exists. Run \`stim ios\` again to create a fresh owned sim.`
1332
+ };
1333
+ if (resolved.notOwned) return {
1334
+ failed: true,
1335
+ reason: `Simulator ${udid} is now named "${resolved.notOwned}" and is not Stim-owned; refusing to boot it.`
1336
+ };
1337
+ const sim = resolved.sim;
1338
+ if (sim.state === "Booted") {
1339
+ const result = readiness ?? ready();
1340
+ if (result.ok && simulatorApp !== void 0) configuredIosSimulatorViewer(simulatorApp).open(udid);
1341
+ return result;
1342
+ }
1343
+ const bootDeadline = Date.now() + timeoutMs;
1344
+ try {
1345
+ await withDeviceBootAdmission({
1346
+ platform: "ios",
1347
+ key: udid
1348
+ }, () => {
1349
+ out(chalk.dim(phaseLine("device", `booting ${sim.name} (${udid})`)));
1350
+ return bootIosSim(udid, {
1351
+ timeoutMs,
1352
+ label: sim.name,
1353
+ out,
1354
+ simulatorApp
1355
+ });
1356
+ }, {
1357
+ ...deviceSlotWait,
1358
+ root: projectPath,
1359
+ out
1360
+ });
1361
+ } catch (e) {
1362
+ return bootFailure(udid, e);
1363
+ }
1364
+ const deadline = Math.max(bootDeadline, Date.now() + 2 * pollMs);
1365
+ while (Date.now() < deadline) {
1366
+ await sleep(pollMs);
1367
+ let state = null;
1368
+ try {
1369
+ state = listAllIosSims({ timeoutMs: 3e4 }).find((s) => s.udid === udid)?.state ?? null;
1370
+ } catch {}
1371
+ if (state === "Booted") return ready();
1372
+ }
1373
+ return {
1374
+ failed: true,
1375
+ reason: `Simulator ${udid} did not reach the Booted state within ${Math.round(timeoutMs / 1e3)}s.`
1376
+ };
1377
+ }
1378
+ function bootFailure(udid, error) {
1379
+ const refusal = error instanceof DeviceAdmissionRefusal ? error : (0, ownership_claim_exports.claimFailure)(error, "stim ios");
1380
+ if (refusal) return {
1381
+ failed: true,
1382
+ code: refusal.code,
1383
+ reason: refusal.message,
1384
+ remedy: refusal.remedy
1385
+ };
1386
+ return {
1387
+ failed: true,
1388
+ reason: `Could not boot simulator ${udid}: ${error?.message || error}`
1389
+ };
1390
+ }
1391
+ function sleep(ms) {
1392
+ return new Promise((r) => setTimeout(r, ms));
1393
+ }
1394
+ //#endregion
1395
+ export { androidPathRoomMessage as _, apkOutputsDir as a, ccacheEnabled as c, productFlavorRefusal as d, readProductFlavors as f, androidPathRoom as g, formatDiagnostic as h, ensureOwnedIosDevice as i, discoverXcodeProject as l, assembleTaskFor as m, clearIosAdoptionPending as n, buildAndroid as o, androidSdkRefusal as p, ensureIosBooted as r, buildIos as s, IosDeviceMismatchError as t, gradlewPath as u, androidPathRoomRemedy as v };