stim 1.14.0 → 1.16.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 (152) hide show
  1. package/README.md +2 -0
  2. package/dist/activity-BxwdD81S.mjs +609 -0
  3. package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
  4. package/dist/{android-d6rGq1pA.mjs → android-B_NumVyQ.mjs} +169 -81
  5. package/dist/{android-LbPFRWVL.mjs → android-Ift4kVvt.mjs} +517 -413
  6. package/dist/android-cas-8U4DPqeR.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{android-XpvCrV7q.mjs → android-r2rBL8kV.mjs} +2 -2
  9. package/dist/{app-install-BpgSgkJj.mjs → app-install-CKWdbXIX.mjs} +128 -151
  10. package/dist/attempt-Bk2ouQVL.mjs +39 -0
  11. package/dist/budget-DjrURfQn.mjs +2700 -0
  12. package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-DjeSb2Xl.mjs} +192 -653
  13. package/dist/build-slots-rUk77xqh.mjs +132 -0
  14. package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  15. package/dist/cache-manifest.mjs +1 -1
  16. package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
  17. package/dist/cli-BkYZ_6hy.mjs +51 -0
  18. package/dist/cli.mjs +6 -42
  19. package/dist/client-BTY686R6.mjs +1368 -0
  20. package/dist/collector-run.mjs +9 -195
  21. package/dist/{command-output-DVcpw6eE.mjs → command-output-DnJMdEY-.mjs} +1 -1
  22. package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
  23. package/dist/{created-devices-CllH_8eb.mjs → created-devices-D5owjN4C.mjs} +2 -2
  24. package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-B6cMXZab.mjs} +2 -2
  25. package/dist/{deps-D9IcKqXS.mjs → deps-urbmfXig.mjs} +3 -3
  26. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  27. package/dist/dev-client-CroAIjjj.mjs +43 -0
  28. package/dist/{device-CjuuAJjQ.mjs → device-DZQjF7H5.mjs} +8 -7
  29. package/dist/device-capacity-BHwxNBlu.mjs +136 -0
  30. package/dist/device-host-worker.d.mts +1 -0
  31. package/dist/device-host-worker.mjs +964 -0
  32. package/dist/device-ios-BadmeL2V.mjs +381 -0
  33. package/dist/{device-lease-5c3WrVRm.mjs → device-lease-D-hLqx1b.mjs} +43 -36
  34. package/dist/{device-pool-BiyDyPP-.mjs → device-lease-run-CqjShqWW.mjs} +6 -126
  35. package/dist/device-pool-CUaEWMMm.mjs +125 -0
  36. package/dist/{device-remote-D87zEzaq.mjs → device-remote-D5WVWqOq.mjs} +69 -163
  37. package/dist/{doctor-x9pnU9fq.mjs → doctor-Cimf-gGB.mjs} +241 -103
  38. package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-B9L4Ae0x.mjs} +15 -18
  39. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  40. package/dist/{gc-ko6mUAYD.mjs → gc-Bqy4bjEu.mjs} +599 -108
  41. package/dist/{android-cas-iZK3DOCr.mjs → gradle-BsO1MdsH.mjs} +8 -294
  42. package/dist/{guide-6R0VNFqI.mjs → guide-B_uyKTxW.mjs} +2511 -239
  43. package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CjEZMAD4.mjs} +3 -3
  44. package/dist/hosted-client-a-WmqE2L.mjs +197 -0
  45. package/dist/hosted-ios-CIWelIqf.mjs +220 -0
  46. package/dist/hosted-logs-D5g041Q3.mjs +187 -0
  47. package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
  48. package/dist/idle-DYVymG92.mjs +1099 -0
  49. package/dist/idle-shutdown-CTsDnAde.mjs +122 -0
  50. package/dist/in-use-DvBw0Kxo.mjs +130 -0
  51. package/dist/{ios-BwDPrnYQ.mjs → ios-BaInn3sW.mjs} +201 -10
  52. package/dist/ios-Cr1h5gnO.mjs +189 -0
  53. package/dist/{ios-P1f5nm2n.mjs → ios-aq3QoGI3.mjs} +1159 -633
  54. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  55. package/dist/{ios-device-DGglU3E_.mjs → ios-device-FfCN_C8p.mjs} +12 -7
  56. package/dist/ios-state-DS5UpYTH.mjs +48 -0
  57. package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-2DBRizjo.mjs} +5 -5
  58. package/dist/logs-CHEVK8zD.mjs +371 -0
  59. package/dist/machines-BIBUdhpj.mjs +265 -0
  60. package/dist/macos-CSYEAFmF.mjs +473 -0
  61. package/dist/macos-run.d.mts +5 -0
  62. package/dist/macos-run.mjs +2 -0
  63. package/dist/maintenance-run.d.mts +4 -0
  64. package/dist/maintenance-run.mjs +254 -0
  65. package/dist/{metro-CcEo8Fl8.mjs → metro-BeK1ZPz1.mjs} +20 -120
  66. package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
  67. package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Bk_YXEmE.mjs} +6 -59
  68. package/dist/native-run-NaBrRFtQ.mjs +147 -0
  69. package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-DBzRnEKS.mjs} +9 -5
  70. package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
  71. package/dist/offload-worker.d.mts +122 -0
  72. package/dist/offload-worker.mjs +606 -0
  73. package/dist/{ownership-DyYDF5ww.mjs → ownership-DiA16AsN.mjs} +3 -2
  74. package/dist/ownership-hCkPzGmr.mjs +571 -0
  75. package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
  76. package/dist/{ports-BguS83ex.mjs → ports-R2k4Knz3.mjs} +3 -3
  77. package/dist/prebuild-D2fsca_Z.mjs +213 -0
  78. package/dist/preview-DhVbvPAZ.mjs +318 -0
  79. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  80. package/dist/{project-CWlvEaeN.mjs → project-Du91Ilvc.mjs} +74 -19
  81. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  82. package/dist/pull-requests.d.mts +120 -0
  83. package/dist/pull-requests.mjs +2 -0
  84. package/dist/recordings-BIXOzVnr.mjs +37 -0
  85. package/dist/{reload-BiniwsSZ.mjs → reload-rM9jV1fz.mjs} +109 -24
  86. package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-C8-V13XN.mjs} +5 -5
  87. package/dist/run-f4vI6EnK.mjs +119 -0
  88. package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-CpPGmnmO.mjs} +33 -15
  89. package/dist/server-expo-CVqO2c35.mjs +2 -0
  90. package/dist/{server-expo-BVuwQ65v.mjs → server-expo-MC0rHlOC.mjs} +8 -12
  91. package/dist/settings-BBpMcDbD.mjs +25 -0
  92. package/dist/{settings-B-fsC-Om.mjs → settings-ZkDzkzvB.mjs} +22 -10
  93. package/dist/settings-oTSkkNrM.mjs +1439 -0
  94. package/dist/settings.schema.json +845 -97
  95. package/dist/{simslim-DrDN4WuV.mjs → simslim-B8-7AoTI.mjs} +8 -132
  96. package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-B8VrHxn1.mjs} +11 -8
  97. package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  98. package/dist/spawn-entry-jw19m8Nl.mjs +17 -0
  99. package/dist/stage-CzaiwFsI.mjs +143 -0
  100. package/dist/start-CX8Ig-U2.mjs +2 -0
  101. package/dist/{start-s4RXp1wy.mjs → start-ClDfwXa4.mjs} +116 -65
  102. package/dist/state-3DwSQIBK.mjs +41 -0
  103. package/dist/state-B4PQFXMW.d.mts +12 -0
  104. package/dist/{state-Dk9RVXI7.mjs → state-C1jB7Q8q.mjs} +70 -8
  105. package/dist/{state-DHqvsbqC.mjs → state-DcW6vOd_.mjs} +7 -4
  106. package/dist/stats-CEZ_i5je.mjs +77 -0
  107. package/dist/{status-BgAuxESg.mjs → status-BuvBFeII.mjs} +118 -20
  108. package/dist/{status-BsPAnfOI.mjs → status-Cs7VLg9D.mjs} +542 -283
  109. package/dist/status-DlKvVJBE.mjs +49 -0
  110. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  111. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  112. package/dist/stim-footprint.swift +32 -0
  113. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  114. package/dist/stop-B0Yxtmiy.mjs +48 -0
  115. package/dist/{stop-BzreOWJp.mjs → stop-Bffozsac.mjs} +83 -16
  116. package/dist/{stop-BWIF_6BK.mjs → stop-Dt_dM16A.mjs} +2 -2
  117. package/dist/stop-cause-CidxIqup.mjs +97 -0
  118. package/dist/supervisor-run.d.mts +43 -11
  119. package/dist/supervisor-run.mjs +363 -24
  120. package/dist/support-CAP8qDUF.mjs +316 -0
  121. package/dist/support-D4aYJniO.mjs +201 -0
  122. package/dist/teardown-CL6vDs2Y.mjs +727 -0
  123. package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DkUflduf.mjs} +364 -14
  124. package/dist/trigger-Bemu8ytw.mjs +65 -0
  125. package/dist/trigger-CXts6qt1.mjs +2 -0
  126. package/dist/warm-progress-CpGv3Wb6.mjs +732 -0
  127. package/dist/{web-BsQs1WlO.mjs → web-DWUaMp8P.mjs} +37 -21
  128. package/dist/web-run.mjs +310 -13
  129. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  130. package/dist/workspace-state-B8-aJ1_5.mjs +371 -0
  131. package/dist/workspaces-BV4pdHFz.mjs +306 -0
  132. package/dist/{worktree-UrQWXSeb.mjs → worktree-K-xj9Db0.mjs} +625 -256
  133. package/dist/worktree-jnG8Iph5.mjs +2 -0
  134. package/package.json +10 -5
  135. package/shim/bundle-response.cjs +28 -12
  136. package/dist/activity-CYVDcrt3.mjs +0 -356
  137. package/dist/budget-CGDkhvv_.mjs +0 -1745
  138. package/dist/build-progress-DxKpwnaS.mjs +0 -247
  139. package/dist/idle-DXtx3RbX.mjs +0 -191
  140. package/dist/in-use-DlqBDKqY.mjs +0 -249
  141. package/dist/logs-Civ4gbBH.mjs +0 -486
  142. package/dist/merge-state-ytFQCeh4.mjs +0 -262
  143. package/dist/server-expo-DIHDhvGg.mjs +0 -2
  144. package/dist/settings-D61iNOsB.mjs +0 -629
  145. package/dist/start-Cetfvy8l.mjs +0 -2
  146. package/dist/stats-CFpFGlJV.mjs +0 -62
  147. package/dist/stats-CX1Z8Tca.mjs +0 -368
  148. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  149. package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
  150. package/dist/worktree-BVrHsYZG.mjs +0 -752
  151. package/dist/worktree-CHWWvm5v.mjs +0 -2
  152. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -1,29 +1,30 @@
1
- import { B as deviceSlotPlatforms, C as setDevice, E as withConfigLock$1, S as saveConfig, U as projectDeviceSlots, _ as loadConfig$1, a as clearDevice, d as getConfigDir, i as clearAndroidBootPending, k as acquireAvdClaim, t as allConsolePortsAndSerials, y as releaseAndroidConsolePort } from "./config-lvc7d3C2.mjs";
2
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
- import { A as workspaceId, C as sharedBuildCache, _ as updateWorkspaceState, a as pidExists, h as readWorkspaceState$1 } from "./metro-CcEo8Fl8.mjs";
4
- import { D as makeTemporaryDirectory, O as removeTemporaryEntry, c as gitCommonDirOnDisk } from "./worktree-BVrHsYZG.mjs";
5
- import { c as ownedDeviceLabel, r as detectIsExpo, s as isPackageResolvable } from "./project-CWlvEaeN.mjs";
6
- import { a as spawnDeclared } from "./spawn-claims-Drw_o-Fb.mjs";
7
- import { a as withWorkspaceProcessLock } from "./named-ports-Blg8IxOh.mjs";
8
- import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
9
- import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-DVcpw6eE.mjs";
10
- import { C as diffFingerprintSources, I as storedSources, w as entryDir, y as artifactIn } from "./device-lease-5c3WrVRm.mjs";
11
- import { $ as waitForBoot, A as listInstalledSystemImages, D as listAdbDevices, I as ownedAvdMatchesConfiguration, L as ownedAvdName, N as nextConsolePort, P as ownedAvdDeviceProfile, T as getAvdNameForSerial, U as pickDefaultSystemImage, _ as bootAndroidEmulator, at as dropParked, ct as parkedMaxSetting, d as assertOwnedAvdStopped, dt as selectParked, h as avdPoolConfiguration, it as adoptParked, k as listAvds, lt as readParked, n as OWNED_AVD_CONFIG_DEFAULTS, ot as isLegacyDeletionClaim, q as resolveOwnedAvdSerial, t as DEFAULT_AVD_DEVICE_PROFILE, v as configureNewOwnedAvd, y as createOwnedAvd, z as ownedAvdSystemImage } from "./android-d6rGq1pA.mjs";
12
- import { l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting, v as iosSimSlimProfileSetting } from "./settings-D61iNOsB.mjs";
13
- import { T as teardownParkedIosSim, w as teardownParkedAvd, x as teardownOwnedAvd } from "./stop-cause-hjGBOZTG.mjs";
14
- import { E as configuredIosSimulatorViewer, O as hostMemoryPressureAdvice, S as resolveOwnedIosSim, b as resetIosPrivacy, c as iosSimulatorFailureAdvice, d as listIosDeviceTypes, f as listIosRuntimes, g as parseRuntimeVersion, k as readHostMemoryPressure, l as listAllIosSims, m as ownedSimName, n as bootIosSim, r as createOwnedIosSim, v as renameIosSim, x as resolveIosCreation, y as resetIosKeychain } from "./ios-BwDPrnYQ.mjs";
15
- import { o as estimateBuild } from "./build-progress-DxKpwnaS.mjs";
16
- import { i as expoBinRefusal, r as expoBinPath, t as cleanLine } from "./server-expo-BVuwQ65v.mjs";
17
- import { a as easAuthNote } from "./remote-cache-CcR6o6lI.mjs";
18
- import { a as liveOwnedDeviceCount, i as deviceTypeMismatch, o as runtimeMismatch, t as reconcileSimSlim } from "./simslim-DrDN4WuV.mjs";
19
- import { r as readStats } from "./stats-CX1Z8Tca.mjs";
20
- import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-Cp5KvK9x.mjs";
21
- import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync } from "node:fs";
22
- import { LAST_BUILD_KEYS, isJsonObject, loadConfig, readJsonObject, readWorkspaceState } from "@stim-cli/core/state";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { D as withConfigLock$1, H as projectDeviceSlots, _ as loadConfig$1, a as clearDevice, b as releaseAndroidConsolePort, i as clearAndroidBootPending, k as acquireAvdClaim, t as allConsolePortsAndSerials, w as setDevice, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
+ import { r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
4
+ import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-DnJMdEY-.mjs";
5
+ import { Tt as removeTemporaryEntry, at as gitCommonDirOnDisk, l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting, wt as makeTemporaryDirectory } from "./settings-oTSkkNrM.mjs";
6
+ import { l as ownedDeviceLabel } from "./project-Du91Ilvc.mjs";
7
+ import { o as pidExists } from "./metro-BeK1ZPz1.mjs";
8
+ import { d as sharedBuildCache } from "./workspace-state-B8-aJ1_5.mjs";
9
+ import { C as diffFingerprintSources, I as storedSources, w as entryDir, y as artifactIn } from "./device-lease-D-hLqx1b.mjs";
10
+ import { b as readStats$1, p as estimateBuild } from "./idle-DYVymG92.mjs";
11
+ import { A as listAvds, B as ownedAvdSystemImage, E as getAvdNameForSerial, F as ownedAvdDeviceProfile, J as resolveOwnedAvdSerial, L as ownedAvdMatchesConfiguration, O as listAdbDevices, P as nextConsolePort, R as ownedAvdName, W as pickDefaultSystemImage, b as createOwnedAvd, f as assertOwnedAvdStopped, ft as parkedMaxSetting, g as avdPoolConfiguration, j as listInstalledSystemImages, n as OWNED_AVD_CONFIG_DEFAULTS, pt as readParked, st as adoptParked, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, ut as isLegacyDeletionClaim, v as bootAndroidEmulator, y as configureNewOwnedAvd } from "./android-B_NumVyQ.mjs";
12
+ import { j as readHostMemoryPressure, u as listAllIosSims } from "./ios-BaInn3sW.mjs";
13
+ import { i as teardownOwnedAvd, s as teardownParkedAvd } from "./teardown-CL6vDs2Y.mjs";
14
+ import { b as tailLines, u as HEARTBEAT_INTERVAL_MS, y as startBuildHeartbeat } from "./toolchain-DkUflduf.mjs";
15
+ import { t as cleanLine } from "./server-expo-MC0rHlOC.mjs";
16
+ import { t as clearDeviceIdleShutdown } from "./idle-shutdown-CTsDnAde.mjs";
17
+ import { i as liveOwnedDeviceCount } from "./device-capacity-BHwxNBlu.mjs";
18
+ import { i as staleNativeDirRefusal } from "./prebuild-D2fsca_Z.mjs";
19
+ import { i as ensureOwnedIosDevice, r as ensureIosBooted } from "./device-ios-BadmeL2V.mjs";
20
+ import { a as easAuthNote } from "./remote-cache-C8-V13XN.mjs";
21
+ import { t as reconcileSimSlim } from "./simslim-B8-7AoTI.mjs";
22
+ import { randomUUID } from "node:crypto";
23
+ import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
23
24
  import { basename, dirname, join, relative } from "node:path";
25
+ import { LAST_BUILD_KEYS, loadConfig, readWorkspaceState } from "@stim-cli/core/state";
24
26
  import { claimRemoveCommand, clearClaimChild, markClaimChildPending, processGroupAlive, releaseClaim, setClaimChild } from "@stim-cli/core/ownership-claim";
25
27
  import { captureProcessIdentity } from "@stim-cli/core/process-identity";
26
- import { randomUUID } from "node:crypto";
27
28
  import chalk from "chalk";
28
29
  import { isDeepStrictEqual } from "node:util";
29
30
  import * as expoFingerprint from "@expo/fingerprint";
@@ -91,370 +92,6 @@ async function warmMetro({ port, platform, isExpo, appId, bundleUrl }) {
91
92
  } catch {}
92
93
  }
93
94
  //#endregion
94
- //#region src/engine/device-ios.ts
95
- function startIosBoot(udid, configure, label, out, simulatorApp) {
96
- const done = (async () => {
97
- await bootIosSim(udid, {
98
- label,
99
- out,
100
- simulatorApp
101
- });
102
- await configure();
103
- })();
104
- done.catch(() => {});
105
- return {
106
- udid,
107
- done
108
- };
109
- }
110
- var IosDeviceMismatchError = class extends Error {
111
- 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.";
112
- };
113
- async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, label, settings, flags, note, out, reconcileIosSimulator }) {
114
- const memoryAdvice = hostMemoryPressureAdvice(readHostMemoryPressure());
115
- if (memoryAdvice) out(chalk.yellow(phaseLine("memory", memoryAdvice)));
116
- const simslimProfile = iosSimSlimProfileSetting(settings, settingsRoot);
117
- if (record?.deviceUdid) {
118
- if (record.owned) {
119
- const resolved = resolveOwnedIosSim(record.deviceUdid);
120
- 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.`));
121
- else if (resolved.missing) {} else {
122
- const sim = resolved.sim;
123
- const wantedType = flags.deviceType || settings?.ios?.deviceType;
124
- const deviceTypes = listIosDeviceTypes();
125
- const mismatch = deviceTypeMismatch(sim.deviceTypeIdentifier, wantedType, deviceTypes);
126
- if (mismatch) throw new IosDeviceMismatchError(`${mismatch}. Stim will not silently boot a different model.`);
127
- const versionMismatch = flags.runtimeFlag ? runtimeMismatch(sim.runtime, flags.runtimeFlag, listIosRuntimes()) : null;
128
- if (versionMismatch) throw new IosDeviceMismatchError(`${versionMismatch}. Stim will not silently boot a different iOS version.`);
129
- const model = {
130
- model: deviceTypes.find((d) => d.identifier === sim.deviceTypeIdentifier)?.name ?? null,
131
- runtime: parseRuntimeVersion(sim.runtime)
132
- };
133
- const name = await withIosDeviceNameLock(() => renameToOwnedName(sim, label, model, projectPath));
134
- const updated = {
135
- deviceUdid: sim.udid,
136
- owned: true,
137
- deviceName: name,
138
- ...record.simslimManaged ? { simslimManaged: true } : {},
139
- ...record.adopted ? { adopted: true } : {},
140
- ...record.adoptionPending ? { adoptionPending: true } : {},
141
- ...record.parkedCacheKey ? { parkedCacheKey: record.parkedCacheKey } : {}
142
- };
143
- const configure = async () => {
144
- if (record.adoptionPending) resetAdoptedSim(sim.udid, out);
145
- return configureOwnedIosSim({
146
- record: updated,
147
- projectPath,
148
- slot,
149
- profile: simslimProfile,
150
- out,
151
- reconcileIosSimulator
152
- });
153
- };
154
- const facts = {
155
- deviceType: model.model,
156
- runtime: model.runtime
157
- };
158
- if (sim.state !== "Booted") {
159
- out(chalk.dim(phaseLine("device", `booting ${name} (${sim.udid})`)));
160
- return {
161
- ...updated,
162
- booting: startIosBoot(sim.udid, configure, name, out, flags.simulatorApp),
163
- ...facts
164
- };
165
- }
166
- return {
167
- ...await configure(),
168
- ...facts
169
- };
170
- }
171
- } else {
172
- const sim = listAllIosSims().find((s) => s.udid === record.deviceUdid);
173
- if (sim) {
174
- if (sim.state !== "Booted") {
175
- 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.`));
176
- note(chalk.dim("Boot it yourself, or run `stim gc --delete` to clear the assignment so Stim can create an owned sim."));
177
- }
178
- return record;
179
- }
180
- }
181
- withConfigLock$1(() => {
182
- const current = deviceSlotPlatforms(loadConfig$1()?.projects?.[projectPath], slot)?.ios;
183
- if (!current) return;
184
- if (current.deviceUdid !== record.deviceUdid || current.owned !== record.owned) throw new Error("The simulator assignment changed during recovery. Run `stim ios` again.");
185
- clearDevice(projectPath, "ios", slot);
186
- });
187
- }
188
- const choice = resolveIosCreation({
189
- deviceType: flags.deviceType || settings.ios?.deviceType,
190
- runtime: flags.runtime || settings.ios?.runtime
191
- });
192
- const adopted = parkedMaxSetting("ios").max > 0 ? await withIosDeviceNameLock(() => takeParkedIosSim({
193
- projectPath,
194
- slot,
195
- label,
196
- choice,
197
- out
198
- })) : null;
199
- if (adopted) {
200
- out(chalk.dim(phaseLine("device", `booting ${adopted.deviceName} (${adopted.deviceUdid})`)));
201
- const booting = startIosBoot(adopted.deviceUdid, async () => {
202
- resetAdoptedSim(adopted.deviceUdid, out);
203
- await configureOwnedIosSim({
204
- record: adopted,
205
- projectPath,
206
- slot,
207
- profile: simslimProfile,
208
- out,
209
- reconcileIosSimulator
210
- });
211
- }, adopted.deviceName, out, flags.simulatorApp);
212
- return {
213
- ...adopted,
214
- booting,
215
- deviceType: choice.deviceType,
216
- runtime: choice.runtime
217
- };
218
- }
219
- const created = await withIosDeviceNameLock(() => {
220
- const suffix = ownedIosNameSuffix(label, {
221
- model: choice.deviceType,
222
- runtime: choice.runtime
223
- }, projectPath);
224
- const result = createOwnedIosSim(label, { suffix }, choice);
225
- setDevice(projectPath, "ios", {
226
- deviceUdid: result.udid,
227
- owned: true,
228
- deviceName: result.name
229
- }, slot);
230
- return result;
231
- });
232
- const newRecord = {
233
- deviceUdid: created.udid,
234
- owned: true,
235
- deviceName: created.name
236
- };
237
- const booting = startIosBoot(created.udid, () => configureOwnedIosSim({
238
- record: newRecord,
239
- projectPath,
240
- slot,
241
- profile: simslimProfile,
242
- out,
243
- reconcileIosSimulator
244
- }), created.name, out, flags.simulatorApp);
245
- return {
246
- ...newRecord,
247
- created: true,
248
- booting,
249
- deviceType: created.deviceType,
250
- runtime: created.runtime
251
- };
252
- }
253
- function withIosDeviceNameLock(fn) {
254
- return withWorkspaceProcessLock(getConfigDir(), "ios-device-names", async () => fn(), { external: true });
255
- }
256
- function ownedIosNameSuffix(label, model, projectPath, udid) {
257
- const names = new Set(listAllIosSims({ includeUnavailable: true }).filter((sim) => sim.udid !== udid).map((sim) => sim.name));
258
- for (const project of Object.values(loadConfig$1()?.projects ?? {})) for (const { platforms } of projectDeviceSlots(project)) {
259
- const record = platforms.ios;
260
- if (record?.deviceUdid !== udid && record?.deviceName) names.add(record.deviceName);
261
- }
262
- for (const parked of readParked("ios")) if (parked.udid !== udid) names.add(parked.name);
263
- let suffix = "";
264
- for (let attempt = 0; names.has(ownedSimName(label, model, suffix)); attempt++) suffix = ` ${workspaceId(projectPath)}${attempt ? `-${attempt}` : ""}`;
265
- return suffix;
266
- }
267
- function renameToOwnedName(sim, label, model, projectPath) {
268
- const wanted = ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, sim.udid));
269
- if (sim.name === wanted) return wanted;
270
- try {
271
- renameIosSim(sim.udid, wanted);
272
- return wanted;
273
- } catch {
274
- return sim.name;
275
- }
276
- }
277
- function resetAdoptedSim(udid, out) {
278
- out(chalk.dim(phaseLine("device", `resetting privacy grants and the keychain on ${udid}`)));
279
- resetIosPrivacy(udid);
280
- resetIosKeychain(udid);
281
- }
282
- function takeParkedIosSim({ projectPath, slot, label, choice, out }) {
283
- const candidates = selectParked(readParked("ios"), {
284
- deviceTypeIdentifier: choice.deviceTypeId,
285
- runtimeIdentifier: choice.runtimeId
286
- });
287
- if (candidates.length === 0) return null;
288
- const listed = new Map(listAllIosSims({ includeUnavailable: true }).map((sim) => [sim.udid, sim]));
289
- for (const parked of candidates) {
290
- const sim = listed.get(parked.udid);
291
- if (!sim || !sim.available) {
292
- const result = sim ? teardownParkedIosSim(parked.udid, { label: parked.name }) : null;
293
- if (sim ? result?.status === "torn-down" : dropParked("ios", parked.udid)) out(chalk.dim(phaseLine("device", `deleted parked ${parked.name} (${parked.udid}): ${sim ? "unavailable" : "gone"}`)));
294
- else if (result?.status === "failed") out(chalk.yellow(phaseLine("device", `could not delete unavailable parked ${parked.name} (${parked.udid}): ${result.reason}`)));
295
- continue;
296
- }
297
- if (!sim.name.startsWith("stim-")) {
298
- out(chalk.yellow(phaseLine("device", `kept parked record for ${parked.udid}: simulator is now named ${JSON.stringify(sim.name)} and is not Stim-owned`)));
299
- continue;
300
- }
301
- const model = {
302
- model: choice.deviceType,
303
- runtime: choice.runtime
304
- };
305
- const name = ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, parked.udid));
306
- const device = {
307
- deviceUdid: parked.udid,
308
- owned: true,
309
- deviceName: name,
310
- adopted: true,
311
- adoptionPending: true,
312
- ...parked.simslimManaged ? { simslimManaged: true } : {},
313
- ...parked.cacheKey ? { parkedCacheKey: parked.cacheKey } : {}
314
- };
315
- if (!adoptParked({
316
- platform: "ios",
317
- projectPath,
318
- slot,
319
- udid: parked.udid,
320
- device
321
- })) continue;
322
- try {
323
- renameIosSim(parked.udid, name);
324
- } catch {}
325
- return device;
326
- }
327
- return null;
328
- }
329
- function clearIosAdoptionPending(projectPath, slot = "default") {
330
- withConfigLock$1(() => {
331
- const cfg = loadConfig$1();
332
- const ios = deviceSlotPlatforms(cfg?.projects?.[projectPath], slot)?.ios;
333
- if (!cfg || !ios?.adoptionPending) return;
334
- delete ios.adopted;
335
- delete ios.adoptionPending;
336
- delete ios.parkedCacheKey;
337
- saveConfig(cfg);
338
- });
339
- }
340
- async function configureOwnedIosSim({ record, projectPath, slot, profile, out, reconcileIosSimulator }) {
341
- if (profile) out(chalk.dim(phaseLine("device", `applying the SimSlim profile to ${record.deviceUdid}`)));
342
- else if (record.simslimManaged) out(chalk.dim(phaseLine("device", `restoring stock simulator services on ${record.deviceUdid}`)));
343
- const previouslyManaged = Boolean(record.simslimManaged);
344
- if (profile && !record.simslimManaged) {
345
- const pending = {
346
- ...record,
347
- simslimManaged: true
348
- };
349
- setDevice(projectPath, "ios", pending, slot);
350
- record = pending;
351
- }
352
- const result = await reconcileIosSimulator({
353
- udid: record.deviceUdid,
354
- profile,
355
- previouslyManaged,
356
- out
357
- });
358
- const updated = { ...record };
359
- if (result.managed) updated.simslimManaged = true;
360
- else delete updated.simslimManaged;
361
- setDevice(projectPath, "ios", updated, slot);
362
- return updated;
363
- }
364
- async function ensureIosBooted({ device, simulatorApp, timeoutMs, pollMs, out }) {
365
- const udid = device?.deviceUdid;
366
- if (!udid) return {
367
- failed: true,
368
- reason: "No iOS simulator is recorded for this project."
369
- };
370
- const ready = () => {
371
- try {
372
- getExecutor().runFile("xcrun", [
373
- "simctl",
374
- "spawn",
375
- udid,
376
- "launchctl",
377
- "list"
378
- ], { timeoutMs: 3e4 });
379
- return {
380
- ok: true,
381
- udid
382
- };
383
- } catch (error) {
384
- return {
385
- failed: true,
386
- reason: `Simulator ${udid} failed its process-spawn readiness check: ${error?.message || error}. ${iosSimulatorFailureAdvice()}`
387
- };
388
- }
389
- };
390
- const booting = device?.booting;
391
- if (booting?.udid === udid) {
392
- try {
393
- await booting.done;
394
- } catch (e) {
395
- return {
396
- failed: true,
397
- reason: `Could not boot simulator ${udid}: ${e?.message || e}`
398
- };
399
- }
400
- return ready();
401
- }
402
- let resolved;
403
- try {
404
- resolved = resolveOwnedIosSim(udid);
405
- } catch (e) {
406
- return {
407
- failed: true,
408
- reason: `Could not list simulators: ${e?.message || e}`
409
- };
410
- }
411
- if (resolved.missing) return {
412
- failed: true,
413
- reason: `Simulator ${udid} no longer exists. Run \`stim ios\` again to create a fresh owned sim.`
414
- };
415
- if (resolved.notOwned) return {
416
- failed: true,
417
- reason: `Simulator ${udid} is now named "${resolved.notOwned}" and is not Stim-owned; refusing to boot it.`
418
- };
419
- const sim = resolved.sim;
420
- if (sim.state === "Booted") {
421
- const result = ready();
422
- if (result.ok && simulatorApp !== void 0) configuredIosSimulatorViewer(simulatorApp).open(udid);
423
- return result;
424
- }
425
- out(chalk.dim(phaseLine("device", `booting ${sim.name} (${udid})`)));
426
- const bootDeadline = Date.now() + timeoutMs;
427
- try {
428
- await bootIosSim(udid, {
429
- timeoutMs,
430
- label: sim.name,
431
- out,
432
- simulatorApp
433
- });
434
- } catch (e) {
435
- return {
436
- failed: true,
437
- reason: `Could not boot simulator ${udid}: ${e?.message || e}`
438
- };
439
- }
440
- const deadline = Math.max(bootDeadline, Date.now() + 2 * pollMs);
441
- while (Date.now() < deadline) {
442
- await sleep(pollMs);
443
- let state = null;
444
- try {
445
- state = listAllIosSims({ timeoutMs: 3e4 }).find((s) => s.udid === udid)?.state ?? null;
446
- } catch {}
447
- if (state === "Booted") return ready();
448
- }
449
- return {
450
- failed: true,
451
- reason: `Simulator ${udid} did not reach the Booted state within ${Math.round(timeoutMs / 1e3)}s.`
452
- };
453
- }
454
- function sleep(ms) {
455
- return new Promise((r) => setTimeout(r, ms));
456
- }
457
- //#endregion
458
95
  //#region src/engine/android-avd-setup.ts
459
96
  var AvdBootError = class extends Error {
460
97
  remedy;
@@ -863,7 +500,8 @@ async function bootOwnedAvdOnFreshPort({ avdName, metadata, projectPath, slot, d
863
500
  const { bootPending: _booted, ...booted } = claim;
864
501
  return {
865
502
  ...booted,
866
- serial
503
+ serial,
504
+ setup: true
867
505
  };
868
506
  } catch (error) {
869
507
  releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
@@ -990,7 +628,15 @@ async function ensureAndroidBooted({ device, projectPath, slot, timeoutMs, out,
990
628
  }
991
629
  //#endregion
992
630
  //#region src/engine/device.ts
631
+ /**
632
+ * Whether `ensureOwnedDevice` created, adopted or started booting the device, rather than finding it booted.
633
+ * Undefined for a remote device, which those functions do not describe.
634
+ */
635
+ function didSetUpDevice(device, remote) {
636
+ return remote ? void 0 : Boolean(device.setup || device.booting);
637
+ }
993
638
  async function ensureOwnedDevice({ platform, project, projectPath, slot, settingsRoot = projectPath, label = ownedDeviceLabel(projectPath), settings, flags = {}, note = () => {}, out = () => {}, logFile = null, alive = pidExists, configureAvd = configureNewOwnedAvd, teardownAvd = teardownOwnedAvd, reconcileIosSimulator = reconcileSimSlim }) {
639
+ clearDeviceIdleShutdown(projectPath, platform, slot);
994
640
  if (slot && slot !== "default") label = `${label}-${slot}`;
995
641
  const record = deviceSlotPlatforms(project, slot)?.[platform] ?? null;
996
642
  if (platform === "ios") return ensureOwnedIosDevice({
@@ -1045,210 +691,6 @@ async function ensureBooted({ platform, device, simulatorApp, timeoutMs, pollMs
1045
691
  };
1046
692
  }
1047
693
  //#endregion
1048
- //#region src/engine/prebuild.ts
1049
- const PREBUILD_ERROR = "STIM_PREBUILD_FAILED";
1050
- const LAST_LINES$1 = 20;
1051
- function nativeDirName(platform) {
1052
- return platform === "android" ? "android" : "ios";
1053
- }
1054
- function nativeDir(root, platform) {
1055
- return join(root, nativeDirName(platform));
1056
- }
1057
- function nativeDirInFingerprint(sources, platform) {
1058
- const dir = nativeDirName(platform);
1059
- return sources.some((source) => source.type === "dir" && source.filePath === dir && source.hash != null);
1060
- }
1061
- /**
1062
- * @expo/fingerprint leaves a native directory out of the hash when git ignores it (CNG), so the cache
1063
- * key only covers that directory when this workspace's recorded prebuild ran on the same fingerprint.
1064
- */
1065
- function prebuildAction({ isExpo, nativeDirExists, nativeDirFingerprinted, nativeDirTracked, generatedFrom, fingerprint }) {
1066
- if (!isExpo) return "none";
1067
- if (!nativeDirExists) return "generate";
1068
- if (nativeDirFingerprinted || generatedFrom === fingerprint) return "none";
1069
- return nativeDirTracked ? "refuse" : "regenerate";
1070
- }
1071
- function prebuildRecord(state) {
1072
- const record = state?.prebuild;
1073
- return record && typeof record === "object" ? record : {};
1074
- }
1075
- function gitTracksNativeDir(root, platform) {
1076
- try {
1077
- return getExecutor().runFile("git", [
1078
- "-C",
1079
- root,
1080
- "ls-files",
1081
- "--",
1082
- nativeDirName(platform)
1083
- ], { env: {
1084
- LC_ALL: "C",
1085
- LANGUAGE: "C"
1086
- } }).trim() !== "";
1087
- } catch (error) {
1088
- if (error?.code === "ENOENT") return false;
1089
- return !/not a git repository \(or any (of the parent directories|parent up to mount point)/.test(String(error?.message));
1090
- }
1091
- }
1092
- function planPrebuild(root, platform, { isExpo, fingerprint, sources }) {
1093
- const nativeDirExists = existsSync(nativeDir(root, platform));
1094
- const fingerprinted = nativeDirInFingerprint(sources, platform);
1095
- const generatedFrom = prebuildRecord(readWorkspaceState$1(root))[platform];
1096
- return prebuildAction({
1097
- isExpo,
1098
- nativeDirExists,
1099
- nativeDirFingerprinted: fingerprinted,
1100
- nativeDirTracked: nativeDirExists && !fingerprinted && gitTracksNativeDir(root, platform),
1101
- generatedFrom: typeof generatedFrom === "string" ? generatedFrom : null,
1102
- fingerprint
1103
- });
1104
- }
1105
- function recordPrebuild(root, platform, fingerprint) {
1106
- updateWorkspaceState(root, (state) => ({
1107
- ...state,
1108
- prebuild: {
1109
- ...prebuildRecord(state),
1110
- [platform]: fingerprint
1111
- }
1112
- }));
1113
- }
1114
- function staleNativeDirRefusal(platform) {
1115
- const dir = nativeDirName(platform);
1116
- return {
1117
- code: PREBUILD_ERROR,
1118
- message: `The fingerprint leaves ${dir}/ out of the cache key, and Stim cannot show that ${dir}/ was generated from the current app config. Git tracks files in it, or could not say whether it does, so Stim will not regenerate it with \`expo prebuild --clean\`.`,
1119
- remedy: `Stop excluding ${dir}/ from the fingerprint (check .fingerprintignore) so the cache key covers its files, or add ${dir}/ to .gitignore and untrack it so Stim regenerates it from the app config.`
1120
- };
1121
- }
1122
- function prebuildRefusal({ isExpo, platform, nativeDirExists }) {
1123
- if (nativeDirExists || isExpo) return null;
1124
- const dir = nativeDirName(platform);
1125
- return {
1126
- code: PREBUILD_ERROR,
1127
- message: `This project has no ${dir}/ directory and is not an Expo (CNG) project, so Stim cannot generate one.`,
1128
- remedy: `Check out or generate the native project (\`npx @react-native-community/cli init\` produced one originally), or add \`expo\` to the project so \`expo prebuild\` can create ${dir}/.`
1129
- };
1130
- }
1131
- function prebuildFailure(logWriter, result) {
1132
- logWriter?.write?.({
1133
- src: "build",
1134
- level: "error",
1135
- msg: result.reason || "Expo prebuild failed.",
1136
- event: "prebuild_failed"
1137
- });
1138
- return result;
1139
- }
1140
- async function runPrebuild(root, platform, logWriter, { spawnFn = null, now = Date.now, isExpo = null, clean = false } = {}) {
1141
- const dirExists = existsSync(nativeDir(root, platform));
1142
- const refusal = prebuildRefusal({
1143
- isExpo: isExpo === null ? detectIsExpo(root) : isExpo,
1144
- platform,
1145
- nativeDirExists: dirExists
1146
- });
1147
- if (refusal) return prebuildFailure(logWriter, {
1148
- failed: true,
1149
- code: refusal.code,
1150
- reason: refusal.message,
1151
- remedy: refusal.remedy,
1152
- error: refusal,
1153
- lastLines: []
1154
- });
1155
- const bin = expoBinPath(root);
1156
- if (!bin) {
1157
- const binRefusal = expoBinRefusal(root, "prebuild");
1158
- return prebuildFailure(logWriter, {
1159
- failed: true,
1160
- code: PREBUILD_ERROR,
1161
- reason: binRefusal.message,
1162
- remedy: binRefusal.remedy,
1163
- lastLines: []
1164
- });
1165
- }
1166
- const spawn = spawnFn || ((cmd, args, opts) => getExecutor().spawn(cmd, args, opts));
1167
- const startedAt = now();
1168
- const tail = [];
1169
- const push = (line) => {
1170
- const msg = stripAnsi(String(line)).trimEnd();
1171
- if (!msg.trim()) return;
1172
- tail.push(msg);
1173
- if (tail.length > LAST_LINES$1) tail.shift();
1174
- logWriter?.write?.({
1175
- src: "build",
1176
- level: "debug",
1177
- msg,
1178
- raw: true,
1179
- event: "prebuild"
1180
- });
1181
- };
1182
- let child;
1183
- try {
1184
- child = spawnDeclared(() => spawn(bin, [
1185
- "prebuild",
1186
- "-p",
1187
- platform,
1188
- "--no-install",
1189
- ...clean ? ["--clean"] : []
1190
- ], {
1191
- cwd: root,
1192
- stdio: [
1193
- "ignore",
1194
- "pipe",
1195
- "pipe"
1196
- ],
1197
- env: {
1198
- ...process.env,
1199
- FORCE_COLOR: "0",
1200
- CI: "1"
1201
- }
1202
- }));
1203
- } catch (err) {
1204
- return prebuildFailure(logWriter, {
1205
- failed: true,
1206
- code: PREBUILD_ERROR,
1207
- reason: `Could not run \`expo prebuild\`: ${err?.message || err}`,
1208
- lastLines: []
1209
- });
1210
- }
1211
- const outReader = createLineReader(push);
1212
- const errReader = createLineReader(push);
1213
- child.stdout?.setEncoding?.("utf-8");
1214
- child.stderr?.setEncoding?.("utf-8");
1215
- child.stdout?.on("data", (chunk) => outReader.push(chunk));
1216
- child.stderr?.on("data", (chunk) => errReader.push(chunk));
1217
- const result = await waitForChild(child);
1218
- outReader.flush();
1219
- errReader.flush();
1220
- const durationMs = now() - startedAt;
1221
- if (result.error) return prebuildFailure(logWriter, {
1222
- failed: true,
1223
- code: PREBUILD_ERROR,
1224
- reason: `Could not run \`expo prebuild\`: ${result.error?.message || result.error}`,
1225
- lastLines: tail.slice(),
1226
- durationMs
1227
- });
1228
- if (result.code !== 0) {
1229
- const how = result.signal ? `signal ${result.signal}` : `exit code ${result.code}`;
1230
- return prebuildFailure(logWriter, {
1231
- failed: true,
1232
- code: PREBUILD_ERROR,
1233
- reason: `\`expo prebuild -p ${platform}\` failed (${how}).`,
1234
- lastLines: tail.slice(),
1235
- durationMs
1236
- });
1237
- }
1238
- if (!existsSync(nativeDir(root, platform))) return prebuildFailure(logWriter, {
1239
- failed: true,
1240
- code: PREBUILD_ERROR,
1241
- reason: `\`expo prebuild -p ${platform}\` succeeded but did not create ${nativeDirName(platform)}/.`,
1242
- lastLines: tail.slice(),
1243
- durationMs
1244
- });
1245
- return {
1246
- ok: true,
1247
- durationMs,
1248
- nativeDir: nativeDir(root, platform)
1249
- };
1250
- }
1251
- //#endregion
1252
694
  //#region src/engine/js-swap.ts
1253
695
  const JS_BUNDLE_NAME = "main.jsbundle";
1254
696
  const LAST_LINES = 5;
@@ -1290,7 +732,8 @@ function detectEntryFile(root) {
1290
732
  }
1291
733
  return pickEntryFile(entries);
1292
734
  }
1293
- function bundleCommand({ isExpo, entryFile, bundleOutput, assetsDest }) {
735
+ function bundleCommand({ isExpo, entryFile, bundleOutput, assetsDest, assetCatalogDest = null }) {
736
+ const catalogArgs = assetCatalogDest ? ["--asset-catalog-dest", assetCatalogDest] : [];
1294
737
  if (isExpo) return {
1295
738
  file: "npx",
1296
739
  args: [
@@ -1303,7 +746,8 @@ function bundleCommand({ isExpo, entryFile, bundleOutput, assetsDest }) {
1303
746
  "--bundle-output",
1304
747
  bundleOutput,
1305
748
  "--assets-dest",
1306
- assetsDest
749
+ assetsDest,
750
+ ...catalogArgs
1307
751
  ]
1308
752
  };
1309
753
  return {
@@ -1320,10 +764,117 @@ function bundleCommand({ isExpo, entryFile, bundleOutput, assetsDest }) {
1320
764
  "--bundle-output",
1321
765
  bundleOutput,
1322
766
  "--assets-dest",
1323
- assetsDest
767
+ assetsDest,
768
+ ...catalogArgs
1324
769
  ]
1325
770
  };
1326
771
  }
772
+ const ASSET_CATALOG_BUNDLE = "RNAssets.bundle";
773
+ /**
774
+ * Where React Native 0.88+ keeps the app's packager images. With the `RCTUseAssetCatalog` Info.plist
775
+ * key, `react-native-xcode.sh` compiles them with actool into RNAssets.bundle and the native image
776
+ * loader reads only that catalog, so a swap has to rebuild it. Null means loose files under assets/.
777
+ */
778
+ function assetCatalogTarget(infoPlist) {
779
+ if (!infoPlist || typeof infoPlist !== "object") return null;
780
+ const plist = infoPlist;
781
+ const flag = plist.RCTUseAssetCatalog;
782
+ if (!(flag === true || flag === 1 || typeof flag === "string" && [
783
+ "true",
784
+ "yes",
785
+ "1"
786
+ ].includes(flag.toLowerCase()))) return null;
787
+ const families = Array.isArray(plist.UIDeviceFamily) ? plist.UIDeviceFamily : [1];
788
+ const targetDevices = [...families.includes(1) ? ["iphone"] : [], ...families.includes(2) ? ["ipad"] : []];
789
+ return {
790
+ platform: typeof plist.DTPlatformName === "string" ? plist.DTPlatformName : "iphoneos",
791
+ minimumDeploymentTarget: typeof plist.MinimumOSVersion === "string" ? plist.MinimumOSVersion : "15.1",
792
+ targetDevices: targetDevices.length ? targetDevices : ["iphone"]
793
+ };
794
+ }
795
+ function readPlistKey(e, plist, key, format) {
796
+ try {
797
+ return e.runFile("plutil", [
798
+ "-extract",
799
+ key,
800
+ format,
801
+ "-o",
802
+ "-",
803
+ plist
804
+ ]);
805
+ } catch (err) {
806
+ if (/No value at that key path/.test(describe(err))) return null;
807
+ throw err;
808
+ }
809
+ }
810
+ function readAssetCatalogTarget(e, plist) {
811
+ const flag = readPlistKey(e, plist, "RCTUseAssetCatalog", "raw");
812
+ if (flag === null) return null;
813
+ const families = readPlistKey(e, plist, "UIDeviceFamily", "json");
814
+ return assetCatalogTarget({
815
+ RCTUseAssetCatalog: flag,
816
+ DTPlatformName: readPlistKey(e, plist, "DTPlatformName", "raw"),
817
+ MinimumOSVersion: readPlistKey(e, plist, "MinimumOSVersion", "raw"),
818
+ UIDeviceFamily: families === null ? void 0 : JSON.parse(families)
819
+ });
820
+ }
821
+ function actoolArgs({ catalog, out, target }) {
822
+ return [
823
+ "actool",
824
+ catalog,
825
+ "--compile",
826
+ out,
827
+ "--output-format",
828
+ "human-readable-text",
829
+ "--errors",
830
+ "--warnings",
831
+ "--notices",
832
+ "--platform",
833
+ target.platform,
834
+ "--minimum-deployment-target",
835
+ target.minimumDeploymentTarget,
836
+ ...target.targetDevices.flatMap((device) => ["--target-device", device])
837
+ ];
838
+ }
839
+ const ASSET_CATALOG_BUNDLE_INFO_PLIST = `<?xml version="1.0" encoding="UTF-8"?>
840
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
841
+ <plist version="1.0">
842
+ <dict>
843
+ <key>CFBundleIdentifier</key>
844
+ <string>org.reactjs.RNAssets</string>
845
+ <key>CFBundleInfoDictionaryVersion</key>
846
+ <string>6.0</string>
847
+ <key>CFBundleName</key>
848
+ <string>RNAssets</string>
849
+ <key>CFBundlePackageType</key>
850
+ <string>BNDL</string>
851
+ </dict>
852
+ </plist>
853
+ `;
854
+ const UPDATES_MANIFEST_NAME = "app.manifest";
855
+ /**
856
+ * expo-updates launches the update with the newest `commitTime` among those built for the binary's
857
+ * runtime version, and the embedded one is only a row in that race. A swapped copy keeps the id and
858
+ * `commitTime` the cached build wrote, so an update downloaded after that build would launch instead
859
+ * of the injected bundle. A full build writes a fresh id and `commitTime`; this does the same.
860
+ */
861
+ function refreshUpdatesManifest(text, { id, commitTime }) {
862
+ const manifest = JSON.parse(text);
863
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) throw new Error(`${UPDATES_MANIFEST_NAME} is not a JSON object`);
864
+ return JSON.stringify({
865
+ ...manifest,
866
+ id,
867
+ commitTime
868
+ });
869
+ }
870
+ function refreshUpdatesManifestFile(path, commitTime) {
871
+ writeFileSync(path, refreshUpdatesManifest(readFileSync(path, "utf-8"), {
872
+ id: randomUUID(),
873
+ commitTime
874
+ }));
875
+ }
876
+ const IOS_UPDATES_MANIFESTS = [join("EXUpdates.bundle", UPDATES_MANIFEST_NAME), UPDATES_MANIFEST_NAME];
877
+ const IOS_FRAMEWORK_UPDATES_MANIFEST = join("Frameworks", "EXUpdates.framework", "EXUpdates.bundle", UPDATES_MANIFEST_NAME);
1327
878
  function hermescPath(root, { exists = existsSync } = {}) {
1328
879
  const candidates = [
1329
880
  join(root, "node_modules", "hermes-compiler", "hermesc", "osx-bin", "hermesc"),
@@ -1383,19 +934,34 @@ async function swapJsBundle({ root, isExpo, cachedAppPath, logWriter = null, exe
1383
934
  } catch (err) {
1384
935
  return fail("copy", `could not copy ${cachedAppPath} aside: ${describe(err)}`);
1385
936
  }
937
+ let catalogTarget;
938
+ const infoPlistPath = join(appCopy, "Info.plist");
939
+ try {
940
+ catalogTarget = readAssetCatalogTarget(e, infoPlistPath);
941
+ } catch (err) {
942
+ return fail("catalog", `could not read ${infoPlistPath}: ${describe(err)}`);
943
+ }
1386
944
  const bundleOutput = join(tmp, JS_BUNDLE_NAME);
1387
945
  const assetsDest = join(tmp, "assets");
946
+ const catalogStaging = catalogTarget ? join(tmp, "rn-assets") : null;
947
+ const catalogDir = catalogStaging ? join(catalogStaging, "RNAssets.xcassets") : null;
1388
948
  const command = bundleCommand({
1389
949
  isExpo,
1390
950
  entryFile: isExpo ? "index.js" : detectEntryFile(root),
1391
951
  bundleOutput,
1392
- assetsDest
952
+ assetsDest,
953
+ assetCatalogDest: catalogStaging
1393
954
  });
1394
955
  try {
1395
956
  mkdirSync(assetsDest, { recursive: true });
1396
957
  } catch (err) {
1397
958
  return fail("bundle", `could not create ${assetsDest}: ${describe(err)}`);
1398
959
  }
960
+ if (catalogDir) try {
961
+ mkdirSync(catalogDir, { recursive: true });
962
+ } catch (err) {
963
+ return fail("bundle", `could not create ${catalogDir}: ${describe(err)}`);
964
+ }
1399
965
  logWriter?.write?.({
1400
966
  src: "build",
1401
967
  level: "info",
@@ -1490,6 +1056,34 @@ async function swapJsBundle({ root, isExpo, cachedAppPath, logWriter = null, exe
1490
1056
  } catch (err) {
1491
1057
  return fail("replace", `could not replace the JS bundle inside ${appCopy}: ${describe(err)}`);
1492
1058
  }
1059
+ if (catalogTarget && catalogDir) {
1060
+ const catalogBundle = join(appCopy, ASSET_CATALOG_BUNDLE);
1061
+ try {
1062
+ rmSync(catalogBundle, {
1063
+ recursive: true,
1064
+ force: true
1065
+ });
1066
+ if (readdirSync(catalogDir).some((name) => name.endsWith(".imageset"))) {
1067
+ mkdirSync(catalogBundle);
1068
+ const output = e.runFile("xcrun", actoolArgs({
1069
+ catalog: catalogDir,
1070
+ out: catalogBundle,
1071
+ target: catalogTarget
1072
+ }));
1073
+ if (!exists(join(catalogBundle, "Assets.car"))) return fail("actool", `actool wrote no Assets.car into ${catalogBundle}`, tailLines(output.split("\n"), LAST_LINES));
1074
+ writeFileSync(join(catalogBundle, "Info.plist"), ASSET_CATALOG_BUNDLE_INFO_PLIST);
1075
+ }
1076
+ } catch (err) {
1077
+ const stdout = String(err?.stdout ?? "");
1078
+ return fail("actool", `could not compile the image asset catalog into ${catalogBundle}: ${describe(err)}`, tailLines(stdout.split("\n"), LAST_LINES));
1079
+ }
1080
+ }
1081
+ if (exists(join(appCopy, IOS_FRAMEWORK_UPDATES_MANIFEST))) return fail("updates", `the expo-updates manifest sits inside a nested framework (${IOS_FRAMEWORK_UPDATES_MANIFEST}), where a swap cannot refresh it without re-signing that framework`);
1082
+ for (const manifest of IOS_UPDATES_MANIFESTS.map((name) => join(appCopy, name)).filter(exists)) try {
1083
+ refreshUpdatesManifestFile(manifest, now());
1084
+ } catch (err) {
1085
+ return fail("updates", `could not refresh the expo-updates manifest ${manifest}: ${describe(err)}`);
1086
+ }
1493
1087
  try {
1494
1088
  e.runFile("codesign", [
1495
1089
  "--force",
@@ -1520,61 +1114,6 @@ function describe(err) {
1520
1114
  return String(err?.message || err);
1521
1115
  }
1522
1116
  //#endregion
1523
- //#region src/commands/dev-client.ts
1524
- function devClientScheme(root, appPath = null, { exec = null } = {}) {
1525
- if (!hasDevClient(root)) return void 0;
1526
- const fromBundle = pickDevClientScheme(readBundleSchemes(appPath, { exec }));
1527
- if (fromBundle) return fromBundle;
1528
- const app = readJsonObject(join(root, "app.json"));
1529
- const expo = app?.expo;
1530
- const raw = (isJsonObject(expo) ? expo.scheme : void 0) ?? app?.scheme ?? null;
1531
- const scheme = Array.isArray(raw) ? raw.find((s) => typeof s === "string" && s.trim() !== "") : raw;
1532
- if (typeof scheme !== "string" || scheme.trim() === "") return void 0;
1533
- return scheme.trim();
1534
- }
1535
- function schemesFromInfoPlist(plist) {
1536
- const types = plist?.CFBundleURLTypes;
1537
- if (!Array.isArray(types)) return [];
1538
- const out = [];
1539
- for (const type of types) {
1540
- const schemes = type?.CFBundleURLSchemes;
1541
- if (Array.isArray(schemes)) out.push(...schemes.filter((s) => typeof s === "string" && s.trim() !== ""));
1542
- }
1543
- return out;
1544
- }
1545
- function readBundleSchemes(appPath, { exec = null } = {}) {
1546
- if (typeof appPath !== "string" || appPath.trim() === "") return [];
1547
- const e = exec || getExecutor();
1548
- let out;
1549
- try {
1550
- out = e.runFile("plutil", [
1551
- "-convert",
1552
- "json",
1553
- "-o",
1554
- "-",
1555
- join(appPath, "Info.plist")
1556
- ]);
1557
- } catch {
1558
- return [];
1559
- }
1560
- try {
1561
- return schemesFromInfoPlist(JSON.parse(String(out)));
1562
- } catch {
1563
- return [];
1564
- }
1565
- }
1566
- const THIRD_PARTY_SCHEME = /^(?:fb\d+|com\.googleusercontent\.apps\.|msauth\.|msauthv2|twitterkit-|db-[a-z0-9]+$|spotify|snapchat|com\.facebook)/i;
1567
- function pickDevClientScheme(schemes) {
1568
- const all = (Array.isArray(schemes) ? schemes : []).filter((s) => typeof s === "string" && s.trim() !== "").map((s) => s.trim()).filter((s) => !/^(?:https?|mailto|tel|sms|itms(?:-apps)?)$/i.test(s));
1569
- const expo = all.filter((s) => s.startsWith("exp+"));
1570
- return (expo.length ? expo : all.filter((s) => !THIRD_PARTY_SCHEME.test(s))).toSorted((a, b) => b.length - a.length)[0] ?? null;
1571
- }
1572
- function hasDevClient(root) {
1573
- const pkg = readJsonObject(join(root, "package.json"));
1574
- if ([pkg?.dependencies, pkg?.devDependencies].some((deps) => isJsonObject(deps) && "expo-dev-client" in deps)) return true;
1575
- return isPackageResolvable(root, "expo-dev-client");
1576
- }
1577
- //#endregion
1578
1117
  //#region src/cache/miss-reason.ts
1579
1118
  const MISS_CHANGE_CAP = 20;
1580
1119
  function recordedBuilds(state, platform) {
@@ -1927,7 +1466,7 @@ async function planCachedBuild(lookup, deps) {
1927
1466
  function planPayload({ platform, slot, projectKey }, found) {
1928
1467
  const { refusal, missReason, ...rest } = found;
1929
1468
  const outcome = refusal ? null : found.cacheHit ? "hit" : "cold";
1930
- const estimate = outcome ? estimateBuild(readStats().record?.history?.[projectKey], platform, outcome, "prepare") : {
1469
+ const estimate = outcome ? estimateBuild(readStats$1().record?.history?.[projectKey], platform, outcome) : {
1931
1470
  expectedMs: null,
1932
1471
  basis: 0
1933
1472
  };
@@ -1984,4 +1523,4 @@ function planFlagRefusal(flag) {
1984
1523
  };
1985
1524
  }
1986
1525
  //#endregion
1987
- export { clearIosAdoptionPending as C, IosDeviceMismatchError as S, staleNativeDirRefusal as _, refusePlan as a, AvdBootError as b, skippedMissReason as c, schemesFromInfoPlist as d, detectEntryFile as f, runPrebuild as g, recordPrebuild as h, printPlan as i, devClientScheme as l, planPrebuild as m, planFlagRefusal as n, explainBuildMiss as o, swapJsBundle as p, planPayload as r, fingerprintErrorMissReason as s, planCachedBuild as t, pickDevClientScheme as u, ensureBooted as v, warmMetro as w, AvdRecoveryError as x, ensureOwnedDevice as y };
1526
+ export { AvdRecoveryError as _, refusePlan as a, skippedMissReason as c, refreshUpdatesManifestFile as d, swapJsBundle as f, AvdBootError as g, ensureOwnedDevice as h, printPlan as i, UPDATES_MANIFEST_NAME as l, ensureBooted as m, planFlagRefusal as n, explainBuildMiss as o, didSetUpDevice as p, planPayload as r, fingerprintErrorMissReason as s, planCachedBuild as t, detectEntryFile as u, warmMetro as v };