stim 1.9.0 → 1.10.1

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 (83) hide show
  1. package/dist/{android-_y4Ohi5z.mjs → android-787HhP5w.mjs} +119 -29
  2. package/dist/{android-BPGizezh.mjs → android-Bu4mbDzc.mjs} +168 -95
  3. package/dist/{android-DEgo4oCo.mjs → android-DPov-fv2.mjs} +2 -2
  4. package/dist/{android-cas-eiExNLf7.mjs → android-cas-CITmIeWY.mjs} +9 -8
  5. package/dist/android-cas-compiler.mjs +1 -1
  6. package/dist/{app-install-Uf3a83Tv.mjs → app-install-Cfmqr3TQ.mjs} +2 -2
  7. package/dist/budget-nDCEwtJv.mjs +620 -0
  8. package/dist/build-progress-BXx-ZBe4.mjs +512 -0
  9. package/dist/{cache-manifest-Dg7OnMrp.mjs → cache-manifest-Pubfp8wV.mjs} +1 -1
  10. package/dist/cache-manifest.mjs +1 -1
  11. package/dist/cli.mjs +16 -16
  12. package/dist/collector-run.d.mts +5 -1
  13. package/dist/collector-run.mjs +7 -7
  14. package/dist/{command-output-gByVmIjy.mjs → command-output-D5z2_bcF.mjs} +1 -1
  15. package/dist/{config-Cp9lM3wX.mjs → config-BEq0Qw_a.mjs} +14 -131
  16. package/dist/{dependency-state-Bq5DcLGn.mjs → dependency-state-r9UesA9a.mjs} +2 -2
  17. package/dist/{deps-Cip5vUm1.mjs → deps-DP4eWqea.mjs} +3 -3
  18. package/dist/{detached-entry-KEu0_AXL.mjs → detached-entry-DJHoca49.mjs} +1 -1
  19. package/dist/{dev-client-HyqISM0h.mjs → dev-client-DExzPiOa.mjs} +137 -250
  20. package/dist/{device-D4U-xLty.mjs → device-Czh5kO8A.mjs} +7 -7
  21. package/dist/{build-lock-DPXPLgTc.mjs → device-lease-CbEaIk7b.mjs} +370 -12
  22. package/dist/{device-pool-CnTAz8X1.mjs → device-pool-BBWLAr55.mjs} +4 -4
  23. package/dist/{device-remote-AQ5rQPqr.mjs → device-remote-powUHdbK.mjs} +547 -468
  24. package/dist/{doctor-B_Ei3bZu.mjs → doctor-CmqK1LcQ.mjs} +58 -22
  25. package/dist/{error-diagnostics-Vfz83GCF.mjs → error-diagnostics-xxWKNO3-.mjs} +11 -12
  26. package/dist/{exec-jIAlgilk.mjs → exec-DK8KaVHK.mjs} +3 -2
  27. package/dist/{gc-Ckw8RQHi.mjs → gc-zCDpmdHU.mjs} +500 -1114
  28. package/dist/{guide-BiAbhs1J.mjs → guide-DwmaD_Wa.mjs} +882 -528
  29. package/dist/guide-status-CWvti2Lg.mjs +125 -0
  30. package/dist/idle--jn7pBNe.mjs +157 -0
  31. package/dist/in-use-CNIWU-r-.mjs +245 -0
  32. package/dist/{ios-BL2NFckr.mjs → ios-COGcGEcN.mjs} +39 -65
  33. package/dist/{ios-BzUJWk1K.mjs → ios-CbdqTqOc.mjs} +130 -89
  34. package/dist/{ios-device-DPsSm0v_.mjs → ios-device-BiOsSVzd.mjs} +1 -1
  35. package/dist/{ios-device-D972i9wf.mjs → ios-device-DNjPXBnb.mjs} +3 -3
  36. package/dist/{launch-verify-Q_OtW1Rf.mjs → launch-verify-BhgkYAws.mjs} +5 -5
  37. package/dist/{logs-CI9NQSK7.mjs → logs-MARIy6m9.mjs} +9 -9
  38. package/dist/{metro-DrLHaMn2.mjs → metro-BWLOJ_Wr.mjs} +116 -10
  39. package/dist/{named-ports-CmLQSIRj.mjs → named-ports-DiTfSz6p.mjs} +4 -4
  40. package/dist/{ndjson-9uFNRwfe.mjs → ndjson-CDY9IkVC.mjs} +3 -56
  41. package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
  42. package/dist/ownership-BfPNRf3-.mjs +1180 -0
  43. package/dist/{ownership-D15Op8vN.mjs → ownership-D2xbLVoz.mjs} +2 -3
  44. package/dist/{ports-BNKoWVWZ.mjs → ports-CLSSFOuz.mjs} +3 -3
  45. package/dist/{project-DS112DYB.mjs → project-BXZzOABt.mjs} +2 -2
  46. package/dist/{reload-ZNJUo1pl.mjs → reload-CymyF6Kk.mjs} +9 -10
  47. package/dist/{server-bare-DWkkcIxV.mjs → server-bare-DVbb7kXH.mjs} +1 -1
  48. package/dist/{server-expo-B_lctBR3.mjs → server-expo-BvOCeakp.mjs} +4 -4
  49. package/dist/server-expo-DBVTdult.mjs +2 -0
  50. package/dist/{settings-D030k0t4.mjs → settings-DOugPlPz.mjs} +16 -428
  51. package/dist/{settings-BKNh5rl4.mjs → settings-EWYNwe72.mjs} +16 -14
  52. package/dist/settings.schema.json +75 -0
  53. package/dist/{simslim-B_LsGLuJ.mjs → simslim-DZUxzsjq.mjs} +182 -5
  54. package/dist/{slot-launch-DU9A-gYw.mjs → slot-launch-Dk4oTREM.mjs} +4 -4
  55. package/dist/{spawn-claims-Dn3C_N6w.mjs → spawn-claims-CUleC2cM.mjs} +1 -1
  56. package/dist/{start-DYkkKF0s.mjs → start-RbmFPeZ7.mjs} +147 -98
  57. package/dist/start-qWapeB4W.mjs +2 -0
  58. package/dist/{state-NcJzMEBF.mjs → state-WGdIbi_d.mjs} +2 -2
  59. package/dist/{stats-DG9e66Rz.mjs → stats-BAgJaLA4.mjs} +1 -1
  60. package/dist/{stats-B63-JVAY.mjs → stats-DBYJ5bxL.mjs} +4 -4
  61. package/dist/status-Dr4fRkym.mjs +216 -0
  62. package/dist/{status-S6ehL4Ui.mjs → status-Zp8kvexe.mjs} +63 -231
  63. package/dist/{guide-status-BAMVU0ZA.mjs → stim-installations-CINXy_su.mjs} +3 -124
  64. package/dist/{stop-T1R-cZRz.mjs → stop-DI4BG4Os.mjs} +2 -2
  65. package/dist/{stop-BO28HGqF.mjs → stop-DcEv5sk-.mjs} +22 -18
  66. package/dist/supervisor-run.d.mts +11 -2
  67. package/dist/supervisor-run.mjs +185 -17
  68. package/dist/{workspace-process-lock-DWaiRD0e.mjs → workspace-process-lock-BXA96myD.mjs} +2 -2
  69. package/dist/workspaces-wrH6dXfu.mjs +1003 -0
  70. package/dist/{worktree-LGB-UwgG.mjs → worktree-Bd4dRvx7.mjs} +40 -122
  71. package/dist/{worktree-R_cUlE7s.mjs → worktree-BlizKDpZ.mjs} +143 -18
  72. package/dist/{worktree-DKhSF0UX.mjs → worktree-DEG2o9F8.mjs} +1 -1
  73. package/dist/{xcode-BZqyxh3e.mjs → xcode-B2tBkf0m.mjs} +9 -158
  74. package/package.json +6 -5
  75. package/dist/build-progress-CVqkfPC6.mjs +0 -188
  76. package/dist/device-lease-Crs-HkJY.mjs +0 -366
  77. package/dist/logs-query-Cqma3WIu.mjs +0 -298
  78. package/dist/ownership-DCQPuWEc.mjs +0 -489
  79. package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
  80. package/dist/server-expo-CeILEu86.mjs +0 -2
  81. package/dist/sim-pool-KaIsp2Ft.mjs +0 -723
  82. package/dist/spawn-entry-nCE6m770.mjs +0 -14
  83. package/dist/workspace-state-DF2gCFhx.mjs +0 -169
@@ -1,201 +1,30 @@
1
- import { K as projectDeviceSlots, N as readJsonObject, O as acquireAvdClaim, S as setDevice, T as withConfigLock, U as deviceSlotPlatforms, g as loadConfig, i as clearDevice, j as isJsonObject, k as ownership_claim_exports, t as allConsolePortsAndSerials, u as getConfigDir, v as releaseAndroidConsolePort, x as saveConfig } from "./config-Cp9lM3wX.mjs";
2
- import { t as getExecutor } from "./exec-jIAlgilk.mjs";
3
- import { u as ensureWorkspaceStorage, v as workspaceId } from "./workspace-state-DF2gCFhx.mjs";
4
- import { E as removeTemporaryEntry, T as makeTemporaryDirectory } from "./worktree-R_cUlE7s.mjs";
5
- import { a as detectIsExpo, c as ownedDeviceLabel, s as isPackageResolvable } from "./project-DS112DYB.mjs";
1
+ import { H as projectDeviceSlots, O as acquireAvdClaim, S as setDevice, T as withConfigLock$1, g as loadConfig$1, i as clearDevice, t as allConsolePortsAndSerials, u as getConfigDir, v as releaseAndroidConsolePort, x as saveConfig, z as deviceSlotPlatforms } from "./config-BEq0Qw_a.mjs";
2
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
+ import { g as updateWorkspaceState, i as pidExists, k as workspaceId, m as readWorkspaceState$1, s as resolveProjectMetro, x as ensureWorkspaceStorage } from "./metro-BWLOJ_Wr.mjs";
4
+ import { D as makeTemporaryDirectory, O as removeTemporaryEntry } from "./worktree-BlizKDpZ.mjs";
5
+ import { a as detectIsExpo, c as ownedDeviceLabel, s as isPackageResolvable } from "./project-BXZzOABt.mjs";
6
+ import { n as spawnDeclared } from "./spawn-claims-CUleC2cM.mjs";
7
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BXA96myD.mjs";
6
8
  import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
7
- import { a as pidExists, t as NOT_OURS_FOREIGN_CWD } from "./metro-DrLHaMn2.mjs";
8
- import { _ as iosSimSlimProfileSetting, a as androidAvdConfigSetting, c as androidDataPartitionSizeGbSetting } from "./settings-D030k0t4.mjs";
9
- import { A as ownedAvdMatchesConfiguration, C as listAdbDevices, F as pickDefaultSystemImage, H as waitForBoot, N as ownedAvdSystemImage, O as nextConsolePort, R as resolveOwnedAvdSerial, T as listInstalledSystemImages, d as avdPoolConfiguration, h as createOwnedAvd, j as ownedAvdName, l as assertOwnedAvdStopped, m as configureNewOwnedAvd, p as bootAndroidEmulator, w as listAvds, x as getAvdNameForSerial } from "./android-_y4Ohi5z.mjs";
10
- import { l as phaseLine } from "./command-output-gByVmIjy.mjs";
11
- import { r as releaseBuildLock, t as acquireBuildLock } from "./build-lock-DPXPLgTc.mjs";
12
- import { n as spawnDeclared } from "./spawn-claims-Dn3C_N6w.mjs";
13
- import { i as expoBinRefusal, r as expoBinPath, t as cleanLine } from "./server-expo-B_lctBR3.mjs";
14
- import { b as resolveEasCliBin, i as isLegacyDeletionClaim, l as selectParked, n as adoptParked, o as parkedMaxSetting, r as dropParked, s as readParked } from "./sim-pool-KaIsp2Ft.mjs";
15
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-DWaiRD0e.mjs";
16
- import { r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING } from "./launch-verify-Q_OtW1Rf.mjs";
17
- import { A as readHostMemoryPressure, C as resolveOwnedIosSim, D as configuredIosSimulatorViewer, S as resolveIosCreation, _ as parseRuntimeVersion, b as resetIosKeychain, f as listIosDeviceTypes, h as ownedSimName, i as createOwnedIosSim, k as hostMemoryPressureAdvice, l as iosSimulatorFailureAdvice, n as bootIosSim, u as listAllIosSims, x as resetIosPrivacy, y as renameIosSim } from "./ios-BL2NFckr.mjs";
18
- import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-BZqyxh3e.mjs";
19
- import { a as liveOwnedDeviceCount, i as deviceTypeMismatch, t as reconcileSimSlim } from "./simslim-B_LsGLuJ.mjs";
20
- import { D as teardownParkedAvd, O as teardownParkedIosSim, T as teardownOwnedAvd } from "./device-remote-AQ5rQPqr.mjs";
21
- import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
9
+ import { l as phaseLine } from "./command-output-D5z2_bcF.mjs";
10
+ import { E as listInstalledSystemImages, I as pickDefaultSystemImage, M as ownedAvdName, P as ownedAvdSystemImage, T as listAvds, W as waitForBoot, d as avdPoolConfiguration, h as createOwnedAvd, j as ownedAvdMatchesConfiguration, k as nextConsolePort, l as assertOwnedAvdStopped, m as configureNewOwnedAvd, p as bootAndroidEmulator, w as listAdbDevices, x as getAvdNameForSerial, z as resolveOwnedAvdSerial } from "./android-787HhP5w.mjs";
11
+ import { l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting, v as iosSimSlimProfileSetting } from "./settings-DOugPlPz.mjs";
12
+ import { _ as dropParked, b as readParked, d as teardownOwnedAvd, g as adoptParked, m as teardownParkedIosSim, p as teardownParkedAvd, v as isLegacyDeletionClaim, x as selectParked, y as parkedMaxSetting } from "./ownership-BfPNRf3-.mjs";
13
+ import { E as configuredIosSimulatorViewer, O as hostMemoryPressureAdvice, S as resolveOwnedIosSim, b as resetIosPrivacy, c as iosSimulatorFailureAdvice, d as listIosDeviceTypes, 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-COGcGEcN.mjs";
14
+ import { i as expoBinRefusal, r as expoBinPath, t as cleanLine } from "./server-expo-BvOCeakp.mjs";
15
+ import { r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING } from "./launch-verify-BhgkYAws.mjs";
16
+ import { a as liveOwnedDeviceCount, i as deviceTypeMismatch, t as reconcileSimSlim } from "./simslim-DZUxzsjq.mjs";
17
+ import { s as startDevServer } from "./start-RbmFPeZ7.mjs";
18
+ import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-B2tBkf0m.mjs";
19
+ import { existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
20
+ import { isJsonObject, readIdleStop, readJsonObject } from "@stim-cli/core/state";
22
21
  import { basename, join } from "node:path";
23
22
  import { claimRemoveCommand, clearClaimChild, markClaimChildPending, processGroupAlive, releaseClaim, setClaimChild } from "@stim-cli/core/ownership-claim";
24
23
  import { captureProcessIdentity } from "@stim-cli/core/process-identity";
25
- import { createHash, randomUUID } from "node:crypto";
26
- import { isDeepStrictEqual } from "node:util";
24
+ import { randomUUID } from "node:crypto";
27
25
  import chalk from "chalk";
26
+ import { isDeepStrictEqual } from "node:util";
28
27
  import { request } from "node:http";
29
- //#region src/engine/eas-build.ts
30
- function isEasBuildFailure(result) {
31
- return result?.ok === false;
32
- }
33
- function easOptionRefusal({ profile, isExpo, buildSelector, buildCache }) {
34
- if (profile === void 0) return null;
35
- if (profile.trim() && isExpo && buildSelector === void 0 && buildCache !== false) return null;
36
- return {
37
- code: "STIM_BAD_ARG",
38
- message: "--eas-profile requires an Expo project and a non-empty development profile.",
39
- remedy: "Use --eas-profile <name> without --scheme, --configuration, --variant, or --no-build-cache."
40
- };
41
- }
42
- function object(value) {
43
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
44
- }
45
- function shellArg(value) {
46
- return /^[a-zA-Z0-9_.-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
47
- }
48
- function easDeviceBuildRemedy(profile) {
49
- return `If the device is not registered, run npx eas-cli device:create. With approval for cloud build costs and signing changes, run npx eas-cli build --platform ios --profile ${shellArg(profile)} to produce a build with a valid profile for this device, then retry the same Stim command.`;
50
- }
51
- var EasFailure = class extends Error {
52
- code;
53
- remedy;
54
- constructor(code, message, remedy) {
55
- super(message);
56
- this.code = code;
57
- this.remedy = remedy;
58
- }
59
- };
60
- function selectEasBuild(builds, { platform, profile, fingerprint, projectId, physical = false }) {
61
- if (!Array.isArray(builds)) throw new Error("EAS build:list did not return an array.");
62
- for (const value of builds) {
63
- const build = object(value);
64
- if (typeof build.id === "string" && build.id.length > 0 && build.status === "FINISHED" && build.platform === platform.toUpperCase() && build.buildProfile === profile && object(build.fingerprint).hash === fingerprint && (object(build.app).id ?? object(build.project).id) === projectId && (platform !== "ios" || build.isForIosSimulator === !physical) && build.distribution === "INTERNAL" && typeof object(build.artifacts).applicationArchiveUrl === "string") return build.id;
65
- }
66
- return null;
67
- }
68
- async function resolveEasDevelopmentBuild({ root, platform, profile, note, isExpo = true, physical = false, selectors = [], buildCache }) {
69
- if (profile === void 0) return null;
70
- const refusal = easOptionRefusal({
71
- profile,
72
- isExpo,
73
- buildSelector: selectors.find((value) => value != null) ?? void 0,
74
- buildCache
75
- });
76
- if (refusal) return {
77
- ok: false,
78
- ...refusal
79
- };
80
- const retry = "the same Stim command";
81
- const buildCommand = `npx eas-cli build --platform ${platform} --profile ${shellArg(profile)}`;
82
- let lock = null;
83
- try {
84
- const cli = resolveEasCliBin(root);
85
- if (!cli) throw new EasFailure("STIM_EAS_UNAVAILABLE", "EAS CLI is not installed.", "Install eas-cli, run eas login, then retry.");
86
- let profileEnv = {};
87
- const run = (args, timeoutMs = 12e4) => {
88
- try {
89
- return JSON.parse(getExecutor().runFile(cli.file, [
90
- ...args,
91
- "--json",
92
- "--non-interactive"
93
- ], {
94
- cwd: root,
95
- timeoutMs,
96
- env: profileEnv
97
- }));
98
- } catch {
99
- throw new EasFailure("STIM_EAS_UNAVAILABLE", `EAS ${args[0]} failed or returned invalid JSON.`, `Run npx eas-cli ${args.map(shellArg).join(" ")} to inspect the error, then retry ${retry}.`);
100
- }
101
- };
102
- note(`eas resolving profile ${profile}`);
103
- const config = object(run([
104
- "config",
105
- "--platform",
106
- platform,
107
- "--profile",
108
- profile
109
- ]));
110
- const buildProfile = object(config.buildProfile);
111
- profileEnv = object(buildProfile.env);
112
- const projectId = object(object(object(config.appConfig).extra).eas).projectId;
113
- if (buildProfile.developmentClient !== true || buildProfile.distribution !== "internal" || platform === "ios" && buildProfile.simulator === true !== !physical) throw new EasFailure("STIM_BAD_ARG", `EAS profile ${profile} is not an internal development build for this ${platform} target.`, `Set developmentClient: true and distribution: internal${platform === "ios" ? `, with ios.simulator: ${!physical}` : ""} in that eas.json profile.`);
114
- if (typeof projectId !== "string" || !projectId) throw new EasFailure("STIM_BAD_ARG", "The Expo app is not linked to an EAS project.", "Link the intended EAS project with eas init, then retry.");
115
- if (platform === "ios" ? buildProfile.buildConfiguration !== void 0 && buildProfile.buildConfiguration !== "Debug" : buildProfile.gradleCommand !== void 0 && (typeof buildProfile.gradleCommand !== "string" || !/^:app:assemble\w*Debug$/.test(buildProfile.gradleCommand))) throw new EasFailure("STIM_BAD_ARG", `EAS profile ${profile} overrides the development build configuration.`, "Use ios.buildConfiguration: Debug or an android.gradleCommand that assembles a single Debug APK, or remove the override.");
116
- note(`eas fingerprinting ${platform} with profile ${profile}`);
117
- const fingerprint = object(run([
118
- "fingerprint:generate",
119
- "--platform",
120
- platform,
121
- "--build-profile",
122
- profile
123
- ])).hash;
124
- if (typeof fingerprint !== "string" || !/^[a-f0-9]{40,64}$/.test(fingerprint)) throw new EasFailure("STIM_EAS_UNAVAILABLE", "EAS returned no valid native fingerprint.", `Run npx eas-cli fingerprint:generate --platform ${platform} --build-profile ${shellArg(profile)} to inspect the result.`);
125
- note(`eas looking for ${fingerprint.slice(0, 8)}`);
126
- const buildId = selectEasBuild(run([
127
- "build:list",
128
- "--platform",
129
- platform,
130
- "--build-profile",
131
- profile,
132
- "--fingerprint-hash",
133
- fingerprint,
134
- "--status",
135
- "finished",
136
- ...platform === "ios" && !physical ? ["--simulator"] : ["--distribution", "internal"],
137
- "--limit",
138
- "1"
139
- ]), {
140
- platform,
141
- profile,
142
- fingerprint,
143
- projectId,
144
- physical
145
- });
146
- if (!buildId) throw new EasFailure("STIM_EAS_BUILD_MISSING", `No compatible EAS ${platform} build exists for profile ${profile} and fingerprint ${fingerprint}.`, `A cloud build may incur charges. With approval to run it, use ${buildCommand}, then retry ${retry}.`);
147
- const cacheKey = `eas-${createHash("sha256").update(JSON.stringify({
148
- projectId,
149
- buildId
150
- })).digest("hex")}-debug-${physical && platform === "ios" ? "device" : "sim"}`;
151
- lock = acquireBuildLock({
152
- platform,
153
- key: cacheKey,
154
- root
155
- });
156
- if (!lock.acquired) throw new EasFailure("STIM_EAS_UNAVAILABLE", `Another run holds the EAS artifact claim at ${lock.path}.`, `Wait for that run to finish, then retry ${retry}.`);
157
- note(`eas resolving artifact for build ${buildId}`);
158
- const result = object(run([
159
- "build:download",
160
- "--build-id",
161
- buildId
162
- ], 6e5));
163
- if (typeof result.path !== "string" || !existsSync(result.path)) throw new Error("EAS returned no downloaded artifact.");
164
- const path = realpathSync(result.path);
165
- if (platform === "ios" ? !path.endsWith(".app") || !statSync(path).isDirectory() : !path.endsWith(".apk") || !statSync(path).isFile()) throw new Error("EAS returned an unexpected artifact path or type.");
166
- note(`eas artifact ready ${fingerprint.slice(0, 8)}`);
167
- return {
168
- ok: true,
169
- path,
170
- fingerprint,
171
- cacheKey,
172
- cacheHit: "remote"
173
- };
174
- } catch (error) {
175
- const claim = (0, ownership_claim_exports.claimFailure)(error, null);
176
- if (claim) return {
177
- ok: false,
178
- code: claim.code,
179
- message: claim.message,
180
- remedy: claim.remedy
181
- };
182
- if (error instanceof EasFailure) return {
183
- ok: false,
184
- code: error.code,
185
- message: error.message,
186
- remedy: error.remedy
187
- };
188
- return {
189
- ok: false,
190
- code: "STIM_EAS_UNAVAILABLE",
191
- message: `Could not resolve the EAS development build: ${error.message}`,
192
- remedy: `Retry ${retry}. No cloud build was started.`
193
- };
194
- } finally {
195
- releaseBuildLock(lock);
196
- }
197
- }
198
- //#endregion
199
28
  //#region src/engine/metro-warmup.ts
200
29
  const WARMUP_TIMEOUT_MS = 6e4;
201
30
  async function requestLocal(url, platform, prefetch) {
@@ -260,42 +89,43 @@ async function warmMetro({ port, platform, isExpo, appId, bundleUrl }) {
260
89
  //#endregion
261
90
  //#region src/commands/native-runtime.ts
262
91
  const sleep$1 = (ms) => new Promise((r) => setTimeout(r, ms));
263
- const GATE_RETRY_DELAYS_MS = [
264
- 3e3,
265
- 7e3,
266
- 1e4
267
- ];
268
- function gateShouldRetry(resolution) {
269
- if (resolution?.metro) return false;
270
- return resolution?.kind !== NOT_OURS_FOREIGN_CWD;
271
- }
272
- async function resolveMetroWithRetry(resolve, port, root, { delays = GATE_RETRY_DELAYS_MS, sleep: wait = sleep$1, onRetry = (_info) => {} } = {}) {
273
- let resolution = await resolve(port, root);
274
- for (let i = 0; i < delays.length && gateShouldRetry(resolution); i++) {
275
- const delayMs = delays[i];
276
- if (delayMs === void 0) break;
277
- onRetry({
278
- attempt: i + 1,
279
- delayMs,
280
- resolution
281
- });
282
- await wait(delayMs);
283
- resolution = await resolve(port, root);
284
- }
285
- return resolution;
286
- }
287
- function noMetroMessage({ port, resolution, supervisor, supervisorAlive }) {
288
- const foreign = resolution?.notOurs;
289
- if (supervisor && supervisor.port === port && supervisorAlive) {
290
- const mode = supervisor.mode ? `${supervisor.mode} ` : "";
291
- return `A supervisor record exists for port ${port} (pid ${supervisor.pid}, ${mode}dev server) but it did not verify as this workspace's Metro${foreign ? `: ${foreign}` : " -- nothing answered /status"}. Metro may still be indexing this project (a monorepo file-map crawl blocks its event loop for ~20s after the port opens).`;
292
- }
293
- if (foreign) return `Port ${port} is in use but is NOT this workspace's dev server: ${foreign}.`;
294
- return `Nothing is serving this workspace's dev server on port ${port}.`;
295
- }
296
- function noMetroRemedy({ port, supervisor, supervisorAlive }) {
297
- if (supervisor && supervisor.port === port && supervisorAlive) return "Re-run `stim ios` in a few seconds, or give the dev server longer to verify with `stim start --wait <seconds>`.";
298
- return "Run `stim start` first, or pass --no-metro-check.";
92
+ async function ensureDevServer({ root, port, settings, remote, note, resolve = resolveProjectMetro, start = startDevServer, readState = readWorkspaceState$1 }) {
93
+ const held = port === null ? null : (await resolve(port, root)).metro;
94
+ if (port !== null && held) return {
95
+ ok: true,
96
+ port,
97
+ pid: held.pid ?? null,
98
+ devServer: null,
99
+ reclaimed: []
100
+ };
101
+ const reason = readIdleStop(readState(root)) ? "stopped (idle)" : "not running";
102
+ note(chalk.dim(phaseLine("metro", `dev server ${reason}; starting it${remote ? " for a remote device" : ""}`)));
103
+ const result = await start({
104
+ root,
105
+ settings,
106
+ remote,
107
+ out: note,
108
+ note
109
+ });
110
+ if (!result.ok) return {
111
+ ok: false,
112
+ code: result.error.code,
113
+ message: `Could not start this workspace's dev server: ${result.error.message}`,
114
+ remedy: result.error.remedy,
115
+ lines: result.lines,
116
+ reclaimed: result.reclaimed
117
+ };
118
+ const { facts } = result;
119
+ return {
120
+ ok: true,
121
+ port: facts.port,
122
+ pid: facts.supervisorPid,
123
+ devServer: facts.alreadyRunning ? null : {
124
+ started: true,
125
+ reason
126
+ },
127
+ reclaimed: result.reclaimed
128
+ };
299
129
  }
300
130
  async function ensureWorkspaceStorageSafely(root, { note = (_line) => {} } = {}) {
301
131
  try {
@@ -406,8 +236,8 @@ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, l
406
236
  return record;
407
237
  }
408
238
  }
409
- withConfigLock(() => {
410
- const current = deviceSlotPlatforms(loadConfig()?.projects?.[projectPath], slot)?.ios;
239
+ withConfigLock$1(() => {
240
+ const current = deviceSlotPlatforms(loadConfig$1()?.projects?.[projectPath], slot)?.ios;
411
241
  if (!current) return;
412
242
  if (current.deviceUdid !== record.deviceUdid || current.owned !== record.owned) throw new Error("The simulator assignment changed during recovery. Run `stim ios` again.");
413
243
  clearDevice(projectPath, "ios", slot);
@@ -483,7 +313,7 @@ function withIosDeviceNameLock(fn) {
483
313
  }
484
314
  function ownedIosNameSuffix(label, model, projectPath, udid) {
485
315
  const names = new Set(listAllIosSims({ includeUnavailable: true }).filter((sim) => sim.udid !== udid).map((sim) => sim.name));
486
- for (const project of Object.values(loadConfig()?.projects ?? {})) for (const { platforms } of projectDeviceSlots(project)) {
316
+ for (const project of Object.values(loadConfig$1()?.projects ?? {})) for (const { platforms } of projectDeviceSlots(project)) {
487
317
  const record = platforms.ios;
488
318
  if (record?.deviceUdid !== udid && record?.deviceName) names.add(record.deviceName);
489
319
  }
@@ -555,8 +385,8 @@ function takeParkedIosSim({ projectPath, slot, label, choice, out }) {
555
385
  return null;
556
386
  }
557
387
  function clearIosAdoptionPending(projectPath, slot = "default") {
558
- withConfigLock(() => {
559
- const cfg = loadConfig();
388
+ withConfigLock$1(() => {
389
+ const cfg = loadConfig$1();
560
390
  const ios = deviceSlotPlatforms(cfg?.projects?.[projectPath], slot)?.ios;
561
391
  if (!cfg || !ios?.adoptionPending) return;
562
392
  delete ios.adopted;
@@ -693,12 +523,12 @@ var AvdBootError = class extends Error {
693
523
  };
694
524
  var AvdRecoveryError = class extends AvdBootError {};
695
525
  function findOtherOwner(avdName, projectPath, selectedSlot = "default") {
696
- for (const [path, project] of Object.entries(loadConfig()?.projects ?? {})) if (projectDeviceSlots(project).some(({ slot, platforms }) => (path !== projectPath || slot !== selectedSlot) && platforms.android?.avdName?.toLowerCase() === avdName.toLowerCase())) return path;
526
+ for (const [path, project] of Object.entries(loadConfig$1()?.projects ?? {})) if (projectDeviceSlots(project).some(({ slot, platforms }) => (path !== projectPath || slot !== selectedSlot) && platforms.android?.avdName?.toLowerCase() === avdName.toLowerCase())) return path;
697
527
  return null;
698
528
  }
699
529
  async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, systemImage, configuration, configure, teardown = teardownOwnedAvd }) {
700
- const { claim, reservation, previous, avdName } = withConfigLock(() => {
701
- const previous = deviceSlotPlatforms(loadConfig()?.projects?.[projectPath], slot)?.android;
530
+ const { claim, reservation, previous, avdName } = withConfigLock$1(() => {
531
+ const previous = deviceSlotPlatforms(loadConfig$1()?.projects?.[projectPath], slot)?.android;
702
532
  if (previous?.avdName && previous.avdName !== previousAvdName) throw new Error(`Another Stim run assigned AVD ${previous.avdName} to this workspace. Retry to use it.`);
703
533
  if (readParked("android").some((entry) => entry.name === ownedAvdName(label)) || findOtherOwner(ownedAvdName(label), projectPath, slot)) label = `${label}-${randomUUID().slice(0, 8)}`;
704
534
  const avdName = ownedAvdName(label);
@@ -723,7 +553,7 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
723
553
  throw error;
724
554
  }
725
555
  });
726
- const currentRecord = () => deviceSlotPlatforms(loadConfig()?.projects?.[projectPath], slot)?.android;
556
+ const currentRecord = () => deviceSlotPlatforms(loadConfig$1()?.projects?.[projectPath], slot)?.android;
727
557
  const assertReservation = () => {
728
558
  if (readParked("android").some((entry) => entry.name === avdName)) throw new Error(`AVD ${avdName} was parked by another Stim run. Retry to adopt it safely.`);
729
559
  const owner = findOtherOwner(avdName, projectPath, slot);
@@ -769,7 +599,7 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
769
599
  if (!message.includes("already exists")) throw error;
770
600
  try {
771
601
  if (!listAvds().includes(avdName)) throw new AvdRecoveryError(`AVD ${avdName} already exists on disk but is not listed by the emulator. ${message}`, "Run `npx stim gc` to inspect orphaned owned AVDs, then `npx stim gc --delete` to reclaim those safe to delete. Retry `stim android` after cleanup; keep any AVD that GC cannot verify.", error);
772
- withConfigLock(assertReservation);
602
+ withConfigLock$1(assertReservation);
773
603
  const resolved = resolveOwnedAvdSerial(avdName);
774
604
  if (resolved.missing || resolved.notOwned) throw new Error(`AVD ${avdName} could not be verified for recovery. Retry after checking its registration.`, { cause: error });
775
605
  if (!resolved.serial) assertOwnedAvdStopped(avdName);
@@ -783,7 +613,7 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
783
613
  } : {}
784
614
  };
785
615
  } catch (recoveryError) {
786
- withConfigLock(() => {
616
+ withConfigLock$1(() => {
787
617
  if (!isDeepStrictEqual(currentRecord(), reservation)) return;
788
618
  if (previous) setDevice(projectPath, "android", previous, slot);
789
619
  else clearDevice(projectPath, "android", slot, avdName);
@@ -797,7 +627,7 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
797
627
  } catch (error) {
798
628
  configurationFailure = { error };
799
629
  }
800
- if (!configurationFailure) withConfigLock(() => {
630
+ if (!configurationFailure) withConfigLock$1(() => {
801
631
  assertReservation();
802
632
  setDevice(projectPath, "android", {
803
633
  avdName,
@@ -990,7 +820,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
990
820
  systemImage: created.systemImage
991
821
  };
992
822
  }
993
- function claimAndroidConsolePort({ projectPath, slot, avdName, deviceName, livePorts = [], metadata }, { lock = withConfigLock, recordedPorts = () => allConsolePortsAndSerials().androidConsolePorts, record = setDevice } = {}) {
823
+ function claimAndroidConsolePort({ projectPath, slot, avdName, deviceName, livePorts = [], metadata }, { lock = withConfigLock$1, recordedPorts = () => allConsolePortsAndSerials().androidConsolePorts, record = setDevice } = {}) {
994
824
  return lock(() => {
995
825
  const consolePort = nextConsolePort([...recordedPorts(), ...livePorts]);
996
826
  const claim = {
@@ -1052,7 +882,7 @@ function androidBootRemedy(pressure) {
1052
882
  count = liveOwnedDeviceCount({
1053
883
  sims: process.platform === "darwin" ? listAllIosSims({ timeoutMs: 2e3 }) : [],
1054
884
  adbEmulators: listAdbDevices({ timeoutMs: 2e3 }).emulators,
1055
- config: loadConfig()
885
+ config: loadConfig$1()
1056
886
  });
1057
887
  } catch {}
1058
888
  const observation = pressure === "warning" || pressure === "critical" ? `macOS reports ${pressure} host memory pressure. ` : "";
@@ -1221,15 +1051,71 @@ function nativeDirName(platform) {
1221
1051
  function nativeDir(root, platform) {
1222
1052
  return join(root, nativeDirName(platform));
1223
1053
  }
1224
- function shouldPrebuild({ isExpo, nativeDirExists }) {
1225
- return Boolean(isExpo) && !nativeDirExists;
1054
+ function nativeDirInFingerprint(sources, platform) {
1055
+ const dir = nativeDirName(platform);
1056
+ return sources.some((source) => source.type === "dir" && source.filePath === dir && source.hash != null);
1057
+ }
1058
+ /**
1059
+ * @expo/fingerprint leaves a native directory out of the hash when git ignores it (CNG), so the cache
1060
+ * key only covers that directory when this workspace's recorded prebuild ran on the same fingerprint.
1061
+ */
1062
+ function prebuildAction({ isExpo, nativeDirExists, nativeDirFingerprinted, nativeDirTracked, generatedFrom, fingerprint }) {
1063
+ if (!isExpo) return "none";
1064
+ if (!nativeDirExists) return "generate";
1065
+ if (nativeDirFingerprinted || generatedFrom === fingerprint) return "none";
1066
+ return nativeDirTracked ? "refuse" : "regenerate";
1067
+ }
1068
+ function prebuildRecord(state) {
1069
+ const record = state?.prebuild;
1070
+ return record && typeof record === "object" ? record : {};
1226
1071
  }
1227
- function needsPrebuild(root, platform, isExpo) {
1228
- return shouldPrebuild({
1072
+ function gitTracksNativeDir(root, platform) {
1073
+ try {
1074
+ return getExecutor().runFile("git", [
1075
+ "-C",
1076
+ root,
1077
+ "ls-files",
1078
+ "--",
1079
+ nativeDirName(platform)
1080
+ ], { env: {
1081
+ LC_ALL: "C",
1082
+ LANGUAGE: "C"
1083
+ } }).trim() !== "";
1084
+ } catch (error) {
1085
+ if (error?.code === "ENOENT") return false;
1086
+ return !/not a git repository \(or any (of the parent directories|parent up to mount point)/.test(String(error?.message));
1087
+ }
1088
+ }
1089
+ function planPrebuild(root, platform, { isExpo, fingerprint, sources }) {
1090
+ const nativeDirExists = existsSync(nativeDir(root, platform));
1091
+ const fingerprinted = nativeDirInFingerprint(sources, platform);
1092
+ const generatedFrom = prebuildRecord(readWorkspaceState$1(root))[platform];
1093
+ return prebuildAction({
1229
1094
  isExpo,
1230
- nativeDirExists: existsSync(nativeDir(root, platform))
1095
+ nativeDirExists,
1096
+ nativeDirFingerprinted: fingerprinted,
1097
+ nativeDirTracked: nativeDirExists && !fingerprinted && gitTracksNativeDir(root, platform),
1098
+ generatedFrom: typeof generatedFrom === "string" ? generatedFrom : null,
1099
+ fingerprint
1231
1100
  });
1232
1101
  }
1102
+ function recordPrebuild(root, platform, fingerprint) {
1103
+ updateWorkspaceState(root, (state) => ({
1104
+ ...state,
1105
+ prebuild: {
1106
+ ...prebuildRecord(state),
1107
+ [platform]: fingerprint
1108
+ }
1109
+ }));
1110
+ }
1111
+ function staleNativeDirRefusal(platform) {
1112
+ const dir = nativeDirName(platform);
1113
+ return {
1114
+ code: PREBUILD_ERROR,
1115
+ 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\`.`,
1116
+ 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.`
1117
+ };
1118
+ }
1233
1119
  function prebuildRefusal({ isExpo, platform, nativeDirExists }) {
1234
1120
  if (nativeDirExists || isExpo) return null;
1235
1121
  const dir = nativeDirName(platform);
@@ -1248,7 +1134,7 @@ function prebuildFailure(logWriter, result) {
1248
1134
  });
1249
1135
  return result;
1250
1136
  }
1251
- async function runPrebuild(root, platform, logWriter, { spawnFn = null, now = Date.now, isExpo = null } = {}) {
1137
+ async function runPrebuild(root, platform, logWriter, { spawnFn = null, now = Date.now, isExpo = null, clean = false } = {}) {
1252
1138
  const dirExists = existsSync(nativeDir(root, platform));
1253
1139
  const refusal = prebuildRefusal({
1254
1140
  isExpo: isExpo === null ? detectIsExpo(root) : isExpo,
@@ -1296,7 +1182,8 @@ async function runPrebuild(root, platform, logWriter, { spawnFn = null, now = Da
1296
1182
  "prebuild",
1297
1183
  "-p",
1298
1184
  platform,
1299
- "--no-install"
1185
+ "--no-install",
1186
+ ...clean ? ["--clean"] : []
1300
1187
  ], {
1301
1188
  cwd: root,
1302
1189
  stdio: [
@@ -1685,4 +1572,4 @@ function hasDevClient(root) {
1685
1572
  return isPackageResolvable(root, "expo-dev-client");
1686
1573
  }
1687
1574
  //#endregion
1688
- export { isEasBuildFailure as C, easDeviceBuildRemedy as S, noMetroMessage as _, swapJsBundle as a, sleep$1 as b, ensureBooted as c, AvdRecoveryError as d, clearIosAdoptionPending as f, launchOutcomeRecord as g, isPhysicalDeviceRequest as h, detectEntryFile as i, ensureOwnedDevice as l, gateShouldRetry as m, pickDevClientScheme as n, needsPrebuild as o, ensureWorkspaceStorageSafely as p, schemesFromInfoPlist as r, runPrebuild as s, devClientScheme as t, AvdBootError as u, noMetroRemedy as v, resolveEasDevelopmentBuild as w, warmMetro as x, resolveMetroWithRetry as y };
1575
+ export { isPhysicalDeviceRequest as _, swapJsBundle as a, warmMetro as b, runPrebuild as c, ensureOwnedDevice as d, AvdBootError as f, ensureWorkspaceStorageSafely as g, ensureDevServer as h, detectEntryFile as i, staleNativeDirRefusal as l, clearIosAdoptionPending as m, pickDevClientScheme as n, planPrebuild as o, AvdRecoveryError as p, schemesFromInfoPlist as r, recordPrebuild as s, devClientScheme as t, ensureBooted as u, launchOutcomeRecord as v, sleep$1 as y };
@@ -1,10 +1,10 @@
1
- import { G as parseDeviceSlotOption } from "./config-Cp9lM3wX.mjs";
2
- import { o as findProjectRoot, t as NO_PROJECT_REFUSAL } from "./project-DS112DYB.mjs";
3
- import { C as listAdbDevices, D as memoizeEmulatorProbe, I as probeEmulatorSerial, P as physicalDeviceModel, a as androidPoolNoCandidatesRefusal, i as androidPoolCandidates, z as resolvePhysicalDevice } from "./android-_y4Ohi5z.mjs";
4
- import { r as clockTime } from "./command-output-gByVmIjy.mjs";
5
- import { f as takeLease, l as releaseWorkspaceLeases, n as fileLeaseIo, o as parseLeaseDuration } from "./device-lease-Crs-HkJY.mjs";
6
- import { d as waitForDevice, s as parseDeviceWait, t as selectFromPool } from "./device-pool-CnTAz8X1.mjs";
7
- import { a as iosPoolCandidates, c as listIosDevices, o as iosPoolNoCandidatesRefusal, s as isWirelessIosDevice, u as resolveIosPhysicalDevice } from "./ios-device-D972i9wf.mjs";
1
+ import { V as parseDeviceSlotOption } from "./config-BEq0Qw_a.mjs";
2
+ import { o as findProjectRoot, t as NO_PROJECT_REFUSAL } from "./project-BXZzOABt.mjs";
3
+ import { r as clockTime } from "./command-output-D5z2_bcF.mjs";
4
+ import { n as fileLeaseIo, o as parseLeaseDuration, p as takeLease, u as releaseWorkspaceLeases } from "./device-lease-CbEaIk7b.mjs";
5
+ import { B as resolvePhysicalDevice, F as physicalDeviceModel, L as probeEmulatorSerial, O as memoizeEmulatorProbe, a as androidPoolNoCandidatesRefusal, i as androidPoolCandidates, w as listAdbDevices } from "./android-787HhP5w.mjs";
6
+ import { d as waitForDevice, s as parseDeviceWait, t as selectFromPool } from "./device-pool-BBWLAr55.mjs";
7
+ import { a as iosPoolCandidates, c as listIosDevices, o as iosPoolNoCandidatesRefusal, s as isWirelessIosDevice, u as resolveIosPhysicalDevice } from "./ios-device-DNjPXBnb.mjs";
8
8
  import chalk from "chalk";
9
9
  //#region src/commands/device.ts
10
10
  const DEFAULT_FOR = "5m";