stim 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +1 -1
  2. package/dist/{activity-BxnkDZDi.mjs → activity-BSw0x84R.mjs} +3 -3
  3. package/dist/{agent-device-usage-output-CZ66YA4T.mjs → agent-device-usage-output-Bjxu_VmX.mjs} +5 -5
  4. package/dist/{android-BvwuMUJ9.mjs → android-BXc6BmEi.mjs} +507 -347
  5. package/dist/{android-DC3ZLuw0.mjs → android-BdQC6zs1.mjs} +6 -6
  6. package/dist/{android-BKA16DTf.mjs → android-BvV1gGdN.mjs} +3 -3
  7. package/dist/{android-CBwCNicO.mjs → android-DE5g_5gv.mjs} +2 -2
  8. package/dist/{android-cas-BBFyr9CA.mjs → android-cas-C87geAg-.mjs} +3 -3
  9. package/dist/android-cas-compiler.mjs +1 -1
  10. package/dist/{api-D0VtFhqk.mjs → api-DIf_s86s.mjs} +5 -7
  11. package/dist/api-run.mjs +8 -8
  12. package/dist/api.d.mts +8 -2
  13. package/dist/api.mjs +1 -1
  14. package/dist/{app-install-D-PUENug.mjs → app-install-CFI4ptUL.mjs} +33 -13
  15. package/dist/{web-DTU0uiWj.mjs → browser-web-CDhqQxhv.mjs} +134 -172
  16. package/dist/{budget-DIhL2BKx.mjs → budget-Dz-kyXLv.mjs} +15 -202
  17. package/dist/{build-facts-DcLv2HSd.d.mts → build-facts-B87vo-Im.d.mts} +32 -1
  18. package/dist/{cache-manifest-BI7ozfmH.mjs → cache-manifest-DiwKSyv9.mjs} +1 -1
  19. package/dist/cache-manifest.mjs +1 -1
  20. package/dist/{chrome-DJnmnS5q.mjs → chrome-BTUOo8p2.mjs} +1 -1
  21. package/dist/{cli-DNJMwADx.mjs → cli-C9Zvgbdc.mjs} +20 -20
  22. package/dist/cli.mjs +1 -1
  23. package/dist/{client-DzhapoMB.mjs → client-CuUo8Nvg.mjs} +248 -23
  24. package/dist/collector-run.d.mts +6 -0
  25. package/dist/collector-run.mjs +6 -6
  26. package/dist/{command-output-CBtzyIgF.mjs → command-output-Bt8ygHE7.mjs} +1 -1
  27. package/dist/{config-CgYMahz7.mjs → config-DC5jEtd1.mjs} +6 -2
  28. package/dist/{created-devices-DKbeWsiK.mjs → created-devices-D0s26Hx5.mjs} +2 -2
  29. package/dist/{dependency-state-xKoOypBm.mjs → dependency-state-DKaS1OM9.mjs} +2 -2
  30. package/dist/{detached-entry-BLfcgc4O.mjs → detached-entry-pB_IPfV4.mjs} +1 -1
  31. package/dist/{dev-client-CLnIH02x.mjs → dev-client-CDY_zAmD.mjs} +2 -2
  32. package/dist/{device-D7oCHGWi.mjs → device-BHw0WlHl.mjs} +8 -8
  33. package/dist/{device-capacity-EUOOYr8T.mjs → device-capacity-BoN1jQkO.mjs} +23 -12
  34. package/dist/device-host-worker.mjs +14 -14
  35. package/dist/{gradle-C6GKnunQ.mjs → device-ios-B2ruohE-.mjs} +659 -105
  36. package/dist/{device-lease-Co-EwifR.mjs → device-lease-Cu7PtQpZ.mjs} +5 -5
  37. package/dist/{device-lease-run-w1xhCqIW.mjs → device-lease-run-D38SnZtt.mjs} +4 -4
  38. package/dist/{device-pool-F5WvRPtD.mjs → device-pool-CP4rAsHx.mjs} +3 -3
  39. package/dist/{device-remote-tHbd1fKy.mjs → device-remote-B5GiUifk.mjs} +15 -16
  40. package/dist/{doctor-B9ckYUoU.mjs → doctor-PRJH94v6.mjs} +53 -29
  41. package/dist/doctor-watchman-wZhyTdp4.mjs +259 -0
  42. package/dist/{error-diagnostics-BRXOqDzG.mjs → error-diagnostics--v5HGOU0.mjs} +10 -10
  43. package/dist/{exec-DWhCZDGm.mjs → exec-i8GDPwq9.mjs} +32 -8
  44. package/dist/{gc-Dh6mXOKC.mjs → gc-CyHMcKcn.mjs} +19 -19
  45. package/dist/{guide-tm6igAaa.mjs → guide-dRaQJJk9.mjs} +409 -472
  46. package/dist/{hosted-android-Bv285t_t.mjs → hosted-android-Bs7fytj7.mjs} +3 -3
  47. package/dist/{hosted-client-C-rMtppF.mjs → hosted-client-C5G4h_OJ.mjs} +1 -1
  48. package/dist/{hosted-ios-D5mNmtj4.mjs → hosted-ios-Bik035eL.mjs} +1 -1
  49. package/dist/{hosted-logs-DN8Iz4mJ.mjs → hosted-logs-CI9Az9a4.mjs} +7 -7
  50. package/dist/{hosted-macos-B-wEjlwG.mjs → hosted-macos-DABm3ScO.mjs} +4 -4
  51. package/dist/{hosted-native-DeeOG8xZ.mjs → hosted-native-DbV2aUVw.mjs} +12 -8
  52. package/dist/{idle-shutdown-jjtxAn5C.mjs → idle-shutdown-DYiMBn34.mjs} +7 -7
  53. package/dist/{ios-B3SbOWdw.mjs → ios-BWbHFZVz.mjs} +5 -5
  54. package/dist/{ios-Bvbf0SGY.mjs → ios-DCrkgXEM.mjs} +1 -1
  55. package/dist/{ios-DNpdygiv.mjs → ios-DYUr9LGc.mjs} +7 -6
  56. package/dist/ios-ITYno9rO.mjs +2241 -0
  57. package/dist/{ios-device-vOaByMhw.mjs → ios-device-CJ_ZOGky.mjs} +3 -3
  58. package/dist/{ios-device-Bn7jWDB1.mjs → ios-device-CrW8aMb5.mjs} +1 -1
  59. package/dist/{ios-state-B2pES6Vs.mjs → ios-state-By0XuLTQ.mjs} +1 -1
  60. package/dist/{launch-verify-hskNcWkZ.mjs → launch-verify-DCveSE6d.mjs} +4 -4
  61. package/dist/{logs-1Ylc1Ri6.mjs → logs-Bao1HOMy.mjs} +10 -10
  62. package/dist/{macos-DF6C7wUU.mjs → macos-ciyBdTY5.mjs} +108 -130
  63. package/dist/macos-nIOATJmR.mjs +2 -0
  64. package/dist/macos-run.mjs +1 -1
  65. package/dist/maintenance-run.mjs +7 -7
  66. package/dist/manifest-CX0d1IkZ.mjs +148 -0
  67. package/dist/{metro-Dh8T4Ytd.mjs → metro-DI2AjPF_.mjs} +194 -41
  68. package/dist/{metro-gateway-CEUw9RMH.mjs → metro-gateway-BxpJLIwh.mjs} +2 -2
  69. package/dist/{plan-placement-CtxkCO54.mjs → miss-reason-CjQCOKqq.mjs} +180 -296
  70. package/dist/{named-ports-BFIz1Shz.mjs → named-ports-B4iWH4E-.mjs} +12 -5
  71. package/dist/{native-runtime-D_603JiC.mjs → native-runtime-BGxsbfny.mjs} +6 -6
  72. package/dist/offload-worker.d.mts +1 -29
  73. package/dist/offload-worker.mjs +9 -8
  74. package/dist/{ownership-D6Se7gg_.mjs → ownership-EB0ySdGK.mjs} +2 -2
  75. package/dist/{placement-log-BcPhRwR4.mjs → placement-log-Dxe34rJm.mjs} +7 -3
  76. package/dist/plan-placement-BVY59qa8.mjs +290 -0
  77. package/dist/{ports-BLTtfBq-.mjs → ports-ByPc0_97.mjs} +3 -3
  78. package/dist/{prebuild-juCyreaR.mjs → prebuild-BKJ3wJI1.mjs} +4 -4
  79. package/dist/{preview-D5V07Nrl.mjs → preview-BbSottmO.mjs} +16 -17
  80. package/dist/project-B5eQ1USs.mjs +612 -0
  81. package/dist/{pull-request-u4vp7zmK.mjs → pull-request-IHNdMnG8.mjs} +1 -1
  82. package/dist/pull-requests.mjs +1 -1
  83. package/dist/react-native-ios-BMh2i25I.mjs +2 -0
  84. package/dist/react-native-ios-CSpIxexZ.mjs +2083 -0
  85. package/dist/{recordings-BAWltsVt.mjs → recordings-Bv8McZN4.mjs} +1 -1
  86. package/dist/{reload-DvyWzVjJ.mjs → reload-rAPdqeRR.mjs} +13 -14
  87. package/dist/{remote-cache-BGg7aGIZ.mjs → remote-cache-N3hAEaAL.mjs} +4 -4
  88. package/dist/{run-vhtDgpHO.mjs → run-lAkle1-c.mjs} +2 -2
  89. package/dist/{server-bare-B9E7S1aR.mjs → server-bare-CkIjnegq.mjs} +3 -3
  90. package/dist/server-expo-DGsdUGX2.mjs +2 -0
  91. package/dist/{server-expo-DoBHhuzK.mjs → server-expo-n3tUi3Em.mjs} +3 -3
  92. package/dist/{settings-Byp2AQ_E.mjs → settings-2am7TSxp.mjs} +5 -6
  93. package/dist/{settings-CQyMElEP.mjs → settings-CxgngUZk.mjs} +767 -15
  94. package/dist/settings.schema.json +72 -6
  95. package/dist/{simslim-EHBQ2R4G.mjs → simslim-D9LkJxIO.mjs} +5 -5
  96. package/dist/{slot-launch-C1nqh4mu.mjs → slot-launch-HV_KtPIw.mjs} +6 -6
  97. package/dist/{stage-g_WqWeaE.mjs → stage-BgaZJVZY.mjs} +1 -1
  98. package/dist/{start-NTc1Ifqs.mjs → start-D8NbxNBb.mjs} +1 -1
  99. package/dist/{start-D-IeOyoK.mjs → start-DtxthMfI.mjs} +29 -22
  100. package/dist/{state-BG-aAk4k.mjs → state-D2KtnALG.mjs} +2 -2
  101. package/dist/{state-xnqpx3AD.mjs → state-Tp4uImDS.mjs} +4 -2
  102. package/dist/{stats-DY6ekz8F.mjs → stats-8EJ5ivFT.mjs} +6 -6
  103. package/dist/{status-DCjZeE-K.mjs → status-BOyp0CUl.mjs} +25 -27
  104. package/dist/{stim-desktop-Behws_fi.mjs → stim-desktop-C65jk_q2.mjs} +1 -1
  105. package/dist/{stim-installations-aR_WsNOA.mjs → stim-installations-DbmUjsLv.mjs} +1 -1
  106. package/dist/stop-D0UIpZuh.mjs +72 -0
  107. package/dist/{stop-DIJtD2GL.mjs → stop-DRipAg-e.mjs} +2 -2
  108. package/dist/{stop-BTjs3xbc.mjs → stop-zDlknFoz.mjs} +78 -31
  109. package/dist/supervisor-run.mjs +14 -14
  110. package/dist/{support-BaayeYCf.mjs → support-CNqYkV0y.mjs} +7 -7
  111. package/dist/{support-Bwvbrq5-.mjs → support-DsNaFg6D.mjs} +5 -5
  112. package/dist/swiftpm-macos-aJzPAj1t.mjs +119 -0
  113. package/dist/{toolchain-Bnyd_K3A.mjs → toolchain-C3xSoyUl.mjs} +27 -270
  114. package/dist/{trigger-CJMhwXe8.mjs → trigger-CAhmG-4s.mjs} +3 -3
  115. package/dist/trigger-CoalsMTV.mjs +2 -0
  116. package/dist/{warm-progress-CjB-MP6H.mjs → warm-progress-pFLB2e6P.mjs} +133 -36
  117. package/dist/{status-Dcr0Qexd.mjs → watchman-BL1IdOtq.mjs} +75 -7
  118. package/dist/web-DFpFeZu5.mjs +2 -0
  119. package/dist/web-DbbrPUn0.mjs +127 -0
  120. package/dist/web-run.mjs +7 -7
  121. package/dist/{workspace-state-C59dqG-Z.mjs → workspace-state-CR9X86xJ.mjs} +1 -1
  122. package/dist/{workspaces-xXItD8Dj.mjs → workspaces-Djb2gfi9.mjs} +102 -18
  123. package/dist/{worktree-BSYlofkq.mjs → worktree-BL4M1rhB.mjs} +45 -42
  124. package/dist/{worktree-BJfnU6r5.mjs → worktree-DV-A0LNL.mjs} +1 -1
  125. package/package.json +5 -5
  126. package/shim/bundle-response.cjs +2 -1
  127. package/shim/bundle-response.d.cts +1 -1
  128. package/shim/expo-metro-config.cjs +1 -1
  129. package/dist/device-ios-6MewKKnc.mjs +0 -412
  130. package/dist/guide-status-Bhf9SJXc.mjs +0 -125
  131. package/dist/ios-CPdkoKaa.mjs +0 -3953
  132. package/dist/macos-Ck7OBMNY.mjs +0 -2
  133. package/dist/native-run-B06FkmZx.mjs +0 -158
  134. package/dist/project-C3ChRLZy.mjs +0 -212
  135. package/dist/projects-DD7dBTCj.mjs +0 -92
  136. package/dist/server-expo-Db9HBUoa.mjs +0 -2
  137. package/dist/stop-BGhKMOuH.mjs +0 -48
  138. package/dist/trigger-DWAq9hkw.mjs +0 -2
  139. package/dist/web-B6_bFDn7.mjs +0 -2
  140. package/dist/worktree-CsKYexJM.mjs +0 -753
@@ -0,0 +1,612 @@
1
+ import { t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { B as parseDeviceSlotKey, R as deviceSlotKey, _ as loadConfig$1, g as isPathPrefix, l as findEnclosingWorktreeRoot, p as getProject } from "./config-DC5jEtd1.mjs";
3
+ import { C as workspaceStateFile, a as updateWorkspaceState, h as supervisorPidFile, o as withWorkspaceStateLock, r as readWorkspaceState$1, t as clearWorkspaceStateKey } from "./workspace-state-CR9X86xJ.mjs";
4
+ import { G as webSettings, V as settingValueAt, mt as repoRoot } from "./settings-CxgngUZk.mjs";
5
+ import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
6
+ import { dirname, join, resolve } from "node:path";
7
+ import { basename as basename$1, dirname as dirname$1, join as join$1, resolve as resolve$1 } from "path";
8
+ import { projectRootDirectories } from "@stim-cli/core/state";
9
+ import { existsSync as existsSync$1, readFileSync as readFileSync$1, realpathSync as realpathSync$1 } from "fs";
10
+ //#region src/supervisor/state.ts
11
+ const MODE_BARE = "bare-inproc";
12
+ const MODE_EXPO = "expo-child";
13
+ function readMetroTunnel(root) {
14
+ const record = readWorkspaceState$1(root)?.metroTunnel;
15
+ if (!record || typeof record !== "object") return null;
16
+ const kind = record.kind;
17
+ const url = record.url;
18
+ if (typeof url !== "string" || url.length === 0) return null;
19
+ if (kind === "expo") return {
20
+ kind: "expo",
21
+ url
22
+ };
23
+ if (kind === "managed") {
24
+ const provider = record.provider;
25
+ const pid = record.pid;
26
+ const port = record.port;
27
+ const startedAt = record.startedAt;
28
+ const processToken = record.processToken;
29
+ const logFile = record.logFile;
30
+ if (provider !== "ngrok" && provider !== "cloudflared" && provider !== "tailscale" || typeof pid !== "number" || typeof port !== "number") return null;
31
+ return {
32
+ kind: "managed",
33
+ provider,
34
+ pid,
35
+ url,
36
+ port,
37
+ startedAt: typeof startedAt === "string" ? startedAt : "",
38
+ processToken: typeof processToken === "string" && processToken.length > 0 ? processToken : null,
39
+ ...typeof logFile === "string" && logFile.length > 0 ? { logFile } : {}
40
+ };
41
+ }
42
+ return null;
43
+ }
44
+ function parseWorkspaceLaunchRecord(value) {
45
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
46
+ const record = value;
47
+ if (typeof record.appId !== "string" || record.appId.length === 0) return null;
48
+ if (typeof record.deviceId !== "string" || record.deviceId.length === 0) return null;
49
+ if (record.metroPort !== null && typeof record.metroPort !== "number") return null;
50
+ if (typeof record.release !== "boolean") return null;
51
+ if (typeof record.launchedAt !== "string") return null;
52
+ return record;
53
+ }
54
+ function readWorkspaceLaunches(root) {
55
+ const launches = readWorkspaceState$1(root)?.launches;
56
+ if (!launches || typeof launches !== "object" || Array.isArray(launches)) return {};
57
+ const records = {};
58
+ for (const [key, value] of Object.entries(launches)) {
59
+ const record = parseWorkspaceLaunchRecord(value);
60
+ if (parseDeviceSlotKey(key) && record) records[key] = record;
61
+ }
62
+ return records;
63
+ }
64
+ function writeWorkspaceLaunch(root, platform, record, slot = "default") {
65
+ updateWorkspaceState(root, (state) => {
66
+ const launches = state.launches && typeof state.launches === "object" ? state.launches : {};
67
+ return {
68
+ ...state,
69
+ launches: {
70
+ ...launches,
71
+ [deviceSlotKey(platform, slot)]: record
72
+ }
73
+ };
74
+ });
75
+ }
76
+ function clearWorkspaceLaunches(root, slot) {
77
+ updateWorkspaceState(root, (state) => {
78
+ if (!state.launches || typeof state.launches !== "object") return state;
79
+ const launches = { ...state.launches };
80
+ delete launches[deviceSlotKey("ios", slot)];
81
+ delete launches[deviceSlotKey("android", slot)];
82
+ return {
83
+ ...state,
84
+ launches
85
+ };
86
+ });
87
+ }
88
+ function readRemoteSession(root) {
89
+ const record = readWorkspaceState$1(root)?.remoteDevice;
90
+ if (!record || typeof record !== "object") return null;
91
+ const id = record.sessionId;
92
+ if (typeof id !== "string" || id.length === 0) return null;
93
+ const platform = record.platform;
94
+ const startedAt = record.startedAt;
95
+ const webPreviewUrl = record.webPreviewUrl;
96
+ const deviceType = record.deviceType;
97
+ return {
98
+ platform: platform === "ios" || platform === "android" ? platform : null,
99
+ sessionId: id,
100
+ startedAt: typeof startedAt === "string" ? startedAt : null,
101
+ webPreviewUrl: typeof webPreviewUrl === "string" && webPreviewUrl.length > 0 ? webPreviewUrl : null,
102
+ deviceType: typeof deviceType === "string" && deviceType.length > 0 ? deviceType : null
103
+ };
104
+ }
105
+ function readRemoteSessionId(root) {
106
+ return readRemoteSession(root)?.sessionId ?? null;
107
+ }
108
+ function clearRemoteSession(root, expectedSessionId) {
109
+ clearWorkspaceStateKey(root, "remoteDevice", (value) => {
110
+ if (typeof value !== "object" || value === null) return false;
111
+ return value.sessionId === expectedSessionId;
112
+ });
113
+ }
114
+ function clearWorkspaceSupervisor(root, expected) {
115
+ return withWorkspaceStateLock(root, () => {
116
+ const current = readWorkspaceState$1(root)?.supervisor;
117
+ if (current && expected !== void 0 && !(0, process_identity_exports.sameProcessRecord)(current, expected)) return false;
118
+ clearWorkspaceStateKey(root, "supervisor", () => true);
119
+ clearExpoMetroTunnel(root);
120
+ if (expected === void 0 || readPidFile(root) === expected?.pid) rmSync(supervisorPidFile(root), { force: true });
121
+ return true;
122
+ });
123
+ }
124
+ function clearExpoMetroTunnel(root) {
125
+ clearWorkspaceStateKey(root, "metroTunnel", (value) => {
126
+ return typeof value === "object" && value !== null && value.kind === "expo";
127
+ });
128
+ }
129
+ function clearManagedMetroTunnel(root, expected) {
130
+ if (!existsSync(workspaceStateFile(root))) return true;
131
+ return clearWorkspaceStateKey(root, "metroTunnel", (value) => {
132
+ if (typeof value !== "object" || value === null) return false;
133
+ const record = value;
134
+ return record.kind === "managed" && record.provider === expected.provider && record.pid === expected.pid && record.url === expected.url && record.port === expected.port && record.startedAt === expected.startedAt && (record.processToken ?? null) === (expected.processToken ?? null) && (record.logFile ?? null) === (expected.logFile ?? null);
135
+ });
136
+ }
137
+ function writePidFile(root, pid) {
138
+ const file = supervisorPidFile(root);
139
+ mkdirSync(dirname(file), { recursive: true });
140
+ writeFileSync(file, `${pid}\n`);
141
+ return file;
142
+ }
143
+ function readPidFile(root) {
144
+ try {
145
+ const pid = parseInt(readFileSync(supervisorPidFile(root), "utf-8").trim(), 10);
146
+ return Number.isFinite(pid) ? pid : null;
147
+ } catch {
148
+ return null;
149
+ }
150
+ }
151
+ //#endregion
152
+ //#region src/workspace/project-files.ts
153
+ /**
154
+ * The realpath of `name`'s package.json in the nearest `node_modules` at or above `projectRoot`.
155
+ * Unlike `require.resolve`, it ignores `NODE_PATH`, which pnpm's bin shims point at the workspace's
156
+ * hidden hoist directory holding every member's dependencies.
157
+ */
158
+ function resolvePackageJson(projectRoot, name) {
159
+ for (let dir = resolve$1(projectRoot);; dir = dirname$1(dir)) {
160
+ const candidate = join$1(dir, "node_modules", name, "package.json");
161
+ if (existsSync$1(candidate)) return realpathSync$1(candidate);
162
+ if (dirname$1(dir) === dir) return null;
163
+ }
164
+ }
165
+ function isPackageResolvable(projectRoot, name) {
166
+ return resolvePackageJson(projectRoot, name) !== null;
167
+ }
168
+ function loadPackageJson(projectRoot) {
169
+ const p = join$1(projectRoot, "package.json");
170
+ if (!existsSync$1(p)) return {
171
+ pkg: null,
172
+ parseError: null
173
+ };
174
+ try {
175
+ return {
176
+ pkg: JSON.parse(readFileSync$1(p, "utf-8")),
177
+ parseError: null
178
+ };
179
+ } catch (error) {
180
+ return {
181
+ pkg: null,
182
+ parseError: error?.message || String(error)
183
+ };
184
+ }
185
+ }
186
+ function readPackageJson(projectRoot) {
187
+ return loadPackageJson(projectRoot).pkg;
188
+ }
189
+ const APP_DEPENDENCIES = ["react-native", "expo"];
190
+ function declaresAppDependency(pkg) {
191
+ if (!pkg || typeof pkg !== "object") return false;
192
+ const { dependencies, devDependencies } = pkg;
193
+ const deps = {
194
+ ...dependencies,
195
+ ...devDependencies
196
+ };
197
+ return APP_DEPENDENCIES.some((name) => name in deps);
198
+ }
199
+ function appProjectProblem(projectRoot, { pkg, parseError } = loadPackageJson(projectRoot)) {
200
+ const file = join$1(projectRoot, "package.json");
201
+ if (parseError) return {
202
+ kind: "unreadable",
203
+ message: `${file} is not valid JSON (${parseError}), so Stim cannot tell whether this is a React Native or Expo app.`,
204
+ remedy: `Fix the JSON in ${file} -- an unfinished edit or a merge conflict leaves it unparseable -- then run the command again.`
205
+ };
206
+ if (declaresAppDependency(pkg)) return null;
207
+ return {
208
+ kind: "not-an-app",
209
+ message: `${file} depends on neither react-native nor expo, so this is not a React Native or Expo app.`,
210
+ remedy: "Run this from the app directory -- the one whose package.json depends on react-native or expo -- or from that directory inside a worktree."
211
+ };
212
+ }
213
+ function detectIsExpo(projectRoot) {
214
+ const pkg = readPackageJson(projectRoot);
215
+ const iosScript = pkg?.scripts?.ios;
216
+ if (typeof iosScript === "string") {
217
+ if (/\bexpo\s+run:ios\b/.test(iosScript)) return true;
218
+ if (/\breact-native\s+run-ios\b/.test(iosScript)) return false;
219
+ }
220
+ if (!pkg) return false;
221
+ const hasExpoDep = "expo" in {
222
+ ...pkg.dependencies,
223
+ ...pkg.devDependencies
224
+ };
225
+ const resolvable = () => isPackageResolvable(projectRoot, "expo");
226
+ if (!hasExpoDep && !resolvable()) return false;
227
+ const appJson = readAppJson(projectRoot);
228
+ if (appJson?.expo) return true;
229
+ const text = readAppConfigText(projectRoot);
230
+ if (text && /\b(?:from\s+['"]expo['"]|expo\/config|ExpoConfig)\b/.test(text)) return true;
231
+ if (hasExpoDep && resolvable()) return true;
232
+ if (looksLikeExpoConfig(appJson)) return true;
233
+ if (podfileUsesExpoModules(projectRoot)) return true;
234
+ return false;
235
+ }
236
+ function detectTutorial(appJson) {
237
+ const extra = appJson?.expo?.extra;
238
+ const version = typeof extra === "object" && extra !== null ? extra.stimTutorial : null;
239
+ return typeof version === "number" && Number.isInteger(version) && version > 0 ? { version } : void 0;
240
+ }
241
+ function looksLikeExpoConfig(appJson) {
242
+ if (!appJson || typeof appJson !== "object" || Array.isArray(appJson)) return false;
243
+ if (appJson.expo) return true;
244
+ if ([
245
+ "slug",
246
+ "plugins",
247
+ "sdkVersion",
248
+ "experiments",
249
+ "runtimeVersion",
250
+ "updates",
251
+ "buildCacheProvider",
252
+ "assetBundlePatterns",
253
+ "splash",
254
+ "orientation",
255
+ "userInterfaceStyle"
256
+ ].some((key) => appJson[key] !== void 0)) return true;
257
+ return Boolean(appJson.extra && typeof appJson.extra === "object" && appJson.extra.eas);
258
+ }
259
+ function podfileUsesExpoModules(projectRoot) {
260
+ const p = join$1(projectRoot, "ios", "Podfile");
261
+ if (!existsSync$1(p)) return false;
262
+ try {
263
+ return /^[^#\n]*use_expo_modules!/m.test(readFileSync$1(p, "utf-8"));
264
+ } catch {
265
+ return false;
266
+ }
267
+ }
268
+ function readAppJson(projectRoot) {
269
+ const p = join$1(projectRoot, "app.json");
270
+ if (!existsSync$1(p)) return null;
271
+ try {
272
+ return JSON.parse(readFileSync$1(p, "utf-8"));
273
+ } catch {
274
+ return null;
275
+ }
276
+ }
277
+ function readAppConfigText(projectRoot) {
278
+ for (const name of [
279
+ "app.config.js",
280
+ "app.config.ts",
281
+ "app.config.cjs",
282
+ "app.config.mjs"
283
+ ]) {
284
+ const p = join$1(projectRoot, name);
285
+ if (existsSync$1(p)) try {
286
+ return readFileSync$1(p, "utf-8");
287
+ } catch {}
288
+ }
289
+ return null;
290
+ }
291
+ //#endregion
292
+ //#region src/integrations/project-registry.ts
293
+ function canonicalPath(path) {
294
+ try {
295
+ return realpathSync(resolve(path));
296
+ } catch {
297
+ return resolve(path);
298
+ }
299
+ }
300
+ function operationSelection(root, matches, operation) {
301
+ const results = matches.flatMap((match) => {
302
+ const problem = match.validate?.(operation);
303
+ return problem === void 0 ? [] : [{
304
+ match,
305
+ problem
306
+ }];
307
+ });
308
+ const unreadable = results.find(({ problem }) => problem?.kind === "unreadable");
309
+ if (unreadable?.problem) return { problem: unreadable.problem };
310
+ const providers = results.filter(({ problem }) => problem === null);
311
+ if (providers.length > 1) return { problem: {
312
+ kind: "ambiguous",
313
+ message: `Multiple project integrations support ${operation} at ${root}: ${providers.map(({ match }) => match.id).join(", ")}.`,
314
+ remedy: "Run this from the directory of the app you intend to use, with one integration for this operation."
315
+ } };
316
+ if (providers.length === 1) return { match: providers[0].match };
317
+ return { problem: results[0]?.problem ?? {
318
+ kind: "not-an-app",
319
+ message: `No project integration supports ${operation} at ${root}.`,
320
+ remedy: "Run this from the directory of a supported app."
321
+ } };
322
+ }
323
+ function operationProblem(root, matches, operation) {
324
+ const selected = operationSelection(root, matches, operation);
325
+ return "problem" in selected ? selected.problem : null;
326
+ }
327
+ function createProjectRegistry(integrations) {
328
+ const inspect = (root) => integrations.flatMap((integration) => {
329
+ const match = integration.inspect(root);
330
+ return match ? [{
331
+ id: integration.id,
332
+ ...match
333
+ }] : [];
334
+ });
335
+ function findProjectRoot(startDir, read = inspect) {
336
+ let candidates = [];
337
+ for (const root of projectRootDirectories(startDir)) {
338
+ const matches = read(root);
339
+ const owned = new Set(matches.flatMap((match) => match.ownedRoots ?? []).map(canonicalPath));
340
+ candidates = candidates.filter((candidate) => !owned.has(candidate));
341
+ if (matches.some((match) => match.root === "explicit")) return candidates[0] ?? root;
342
+ if (matches.some((match) => match.root === "candidate")) candidates.push(root);
343
+ }
344
+ return candidates[0] ?? null;
345
+ }
346
+ function ownedRootProblem(root, matches) {
347
+ if (matches.some((match) => match.root === "candidate") && !matches.some((match) => match.root === "explicit")) {
348
+ const owner = findProjectRoot(root, (dir) => dir === root ? matches : inspect(dir));
349
+ if (owner !== null && owner !== root) return {
350
+ kind: "not-an-app",
351
+ message: `${root} belongs to the project at ${owner}.`,
352
+ remedy: `Run this from ${owner}.`
353
+ };
354
+ }
355
+ return null;
356
+ }
357
+ function projectProblem(root, operation) {
358
+ root = canonicalPath(root);
359
+ const matches = inspect(root);
360
+ return ownedRootProblem(root, matches) ?? operationProblem(root, matches, operation);
361
+ }
362
+ function selectMacos(root) {
363
+ root = canonicalPath(root);
364
+ const matches = inspect(root);
365
+ const problem = ownedRootProblem(root, matches);
366
+ if (problem) return { problem };
367
+ const selected = operationSelection(root, matches, "macos");
368
+ if ("problem" in selected) return selected;
369
+ if (selected.match.macos) return { load: selected.match.macos };
370
+ return { problem: {
371
+ kind: "not-an-app",
372
+ message: `The ${selected.match.id} integration does not provide a macOS operation.`,
373
+ remedy: "Use an integration with a macOS build recipe."
374
+ } };
375
+ }
376
+ function selectWeb(root) {
377
+ root = canonicalPath(root);
378
+ const matches = inspect(root);
379
+ const problem = ownedRootProblem(root, matches);
380
+ if (problem) return { problem };
381
+ const selected = operationSelection(root, matches, "web");
382
+ if ("problem" in selected) return selected;
383
+ if (selected.match.web) return { load: selected.match.web };
384
+ return { problem: {
385
+ kind: "not-an-app",
386
+ message: `The ${selected.match.id} integration does not provide a web operation.`,
387
+ remedy: "Use an integration with a web runtime recipe."
388
+ } };
389
+ }
390
+ function selectIos(root) {
391
+ root = canonicalPath(root);
392
+ const matches = inspect(root);
393
+ const problem = ownedRootProblem(root, matches);
394
+ if (problem) return { problem };
395
+ const selected = operationSelection(root, matches, "ios");
396
+ if ("problem" in selected) return selected;
397
+ if (selected.match.ios) return { load: selected.match.ios };
398
+ return { problem: {
399
+ kind: "not-an-app",
400
+ message: `The ${selected.match.id} integration does not provide an iOS operation.`,
401
+ remedy: "Use an integration with an iOS build and runtime recipe."
402
+ } };
403
+ }
404
+ function isMobileProject(root) {
405
+ root = canonicalPath(root);
406
+ const matches = inspect(root);
407
+ return ownedRootProblem(root, matches) === null && (operationProblem(root, matches, "ios") === null || operationProblem(root, matches, "android") === null);
408
+ }
409
+ function keepsWorkspace(root) {
410
+ const matches = inspect(root);
411
+ return matches.some((match) => match.application) || [
412
+ "ios",
413
+ "android",
414
+ "dev-server"
415
+ ].some((operation) => operationProblem(root, matches, operation)?.kind === "unreadable");
416
+ }
417
+ function detectPlatforms(root, settings) {
418
+ const platforms = new Set(inspect(root).flatMap((match) => match.platforms(settings)));
419
+ return [
420
+ "ios",
421
+ "android",
422
+ "macos",
423
+ "web"
424
+ ].filter((platform) => platforms.has(platform));
425
+ }
426
+ return {
427
+ findProjectRoot,
428
+ projectProblem,
429
+ selectIos,
430
+ selectMacos,
431
+ selectWeb,
432
+ isMobileProject,
433
+ keepsWorkspace,
434
+ detectPlatforms
435
+ };
436
+ }
437
+ //#endregion
438
+ //#region src/integrations/projects.ts
439
+ const expo = {
440
+ mode: MODE_EXPO,
441
+ loadDevServer: async () => (await import("./server-expo-DGsdUGX2.mjs")).startExpoServer,
442
+ platforms(root) {
443
+ const appJson = readAppJson(root);
444
+ const config = appJson?.expo ?? appJson;
445
+ const text = readAppConfigText(root);
446
+ const explicit = (text ? literalPlatforms(text) : null) ?? config?.platforms;
447
+ if (Array.isArray(explicit)) return explicit.filter((platform) => platform === "ios" || platform === "android" || platform === "web");
448
+ const pkg = readPackageJson(root);
449
+ return [
450
+ "ios",
451
+ "android",
452
+ ..."react-native-web" in {
453
+ ...pkg?.dependencies,
454
+ ...pkg?.devDependencies
455
+ } || isPackageResolvable(root, "react-native-web") ? ["web"] : []
456
+ ];
457
+ }
458
+ };
459
+ const reactNative = {
460
+ mode: MODE_BARE,
461
+ loadDevServer: async () => (await import("./server-bare-CkIjnegq.mjs")).startBareServer,
462
+ platforms(root) {
463
+ if (!declaresAppDependency(readPackageJson(root))) return [];
464
+ const platforms = [];
465
+ let ios = [];
466
+ try {
467
+ ios = readdirSync(join(root, "ios"));
468
+ } catch {}
469
+ if (ios.some((name) => name.endsWith(".xcodeproj") || name.endsWith(".xcworkspace"))) platforms.push("ios");
470
+ if ([
471
+ "build.gradle",
472
+ "build.gradle.kts",
473
+ "settings.gradle",
474
+ "settings.gradle.kts"
475
+ ].some((name) => existsSync(join(root, "android", name)))) platforms.push("android");
476
+ return platforms;
477
+ }
478
+ };
479
+ const projectRegistry = createProjectRegistry([
480
+ {
481
+ id: "react-native",
482
+ inspect(root) {
483
+ const problem = appProjectProblem(root, loadPackageJson(root));
484
+ return {
485
+ root: existsSync(join(root, "package.json")) ? "explicit" : false,
486
+ application: problem === null,
487
+ ownedRoots: problem === null ? [join(root, "ios"), join(root, "android")] : [],
488
+ platforms: (settings) => nativeProjectIntegration(root).platforms(root, settings),
489
+ validate: (operation) => operation === "ios" || operation === "android" || operation === "dev-server" ? problem : void 0,
490
+ ios: async () => (await import("./react-native-ios-BMh2i25I.mjs")).reactNativeIosProject(root)
491
+ };
492
+ }
493
+ },
494
+ {
495
+ id: "swift-package",
496
+ inspect(root) {
497
+ if (!existsSync(join(root, "Package.swift"))) return null;
498
+ return {
499
+ root: "explicit",
500
+ application: true,
501
+ platforms: (settings) => settingValueAt(settings, "macos.product") && settingValueAt(settings, "macos.infoPlist") ? ["macos"] : [],
502
+ validate: (operation) => operation === "macos" ? null : void 0,
503
+ macos: async () => (await import("./swiftpm-macos-aJzPAj1t.mjs")).swiftpmMacosProject(root)
504
+ };
505
+ }
506
+ },
507
+ {
508
+ id: "browser-web",
509
+ inspect(root) {
510
+ return {
511
+ root: false,
512
+ application: false,
513
+ platforms: (settings) => webSettings(settings).url !== null ? ["web"] : [],
514
+ validate: (operation) => operation === "web" ? null : void 0,
515
+ web: async () => (await import("./browser-web-CDhqQxhv.mjs")).browserWebProject(root)
516
+ };
517
+ }
518
+ }
519
+ ]);
520
+ const detectPlatforms = projectRegistry.detectPlatforms;
521
+ function nativeProjectIntegration(root, isExpo = detectIsExpo) {
522
+ return isExpo(root) ? expo : reactNative;
523
+ }
524
+ function literalPlatforms(text) {
525
+ const tokens = text.match(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|[A-Za-z_$][\w$]*|[^\s]/g)?.filter((token) => !token.startsWith("//") && !token.startsWith("/*")) ?? [];
526
+ let open = 0;
527
+ const index = tokens.findIndex((token, i) => {
528
+ if (token === "[") open++;
529
+ else if (token === "]") open--;
530
+ return open === 0 && (token === "platforms" || token === "\"platforms\"" || token === "'platforms'") && tokens[i + 1] === ":";
531
+ });
532
+ if (index < 0 || tokens[index + 2] !== "[") return null;
533
+ const platforms = [];
534
+ let i = index + 3;
535
+ while (tokens[i] !== "]") {
536
+ const value = tokens[i]?.match(/^(['"])([a-z]+)\1$/)?.[2];
537
+ if (value === void 0) return null;
538
+ platforms.push(value);
539
+ i++;
540
+ if (tokens[i] === ",") i++;
541
+ else if (tokens[i] !== "]") return null;
542
+ }
543
+ return tokens[i + 1] === "," || tokens[i + 1] === "}" ? platforms : null;
544
+ }
545
+ //#endregion
546
+ //#region src/workspace/project.ts
547
+ const findProjectRoot = projectRegistry.findProjectRoot;
548
+ const projectProblem = projectRegistry.projectProblem;
549
+ const isMobileProject = projectRegistry.isMobileProject;
550
+ function projectShortcut(path, proj) {
551
+ if (proj?.label) return proj.label;
552
+ const rootPath = findEnclosingWorktreeRoot(path);
553
+ if (rootPath && rootPath !== path) return `${projectShortcut(rootPath, getProject(rootPath))}/${path.split("/").pop() || path}`;
554
+ return path.split("/").pop() || path;
555
+ }
556
+ function ownedDeviceLabel(projectPath) {
557
+ const app = realpathSync$1(projectPath);
558
+ const root = repoRoot(app);
559
+ const appLabel = basename$1(app);
560
+ if (!root) return appLabel;
561
+ const worktreeLabel = basename$1(realpathSync$1(root));
562
+ return worktreeLabel === appLabel ? appLabel : `${worktreeLabel}-${appLabel}`;
563
+ }
564
+ const NO_PROJECT_REFUSAL = {
565
+ code: "STIM_NO_PROJECT",
566
+ message: "Not in a React Native project (no package.json found).",
567
+ remedy: "Run this from the app directory -- the one holding package.json."
568
+ };
569
+ function findServerWorkspace(startDir, registry = projectRegistry) {
570
+ const nearest = registry.findProjectRoot(startDir);
571
+ if (!nearest) return null;
572
+ if (registry.keepsWorkspace(nearest)) return {
573
+ root: nearest,
574
+ from: null
575
+ };
576
+ if (Object.keys(getProject(nearest)?.ports ?? {}).length) return {
577
+ root: nearest,
578
+ from: null
579
+ };
580
+ const top = repoRoot(nearest);
581
+ if (!top) return {
582
+ root: nearest,
583
+ from: null
584
+ };
585
+ const worktree = expandedRealpath(top);
586
+ const apps = Object.keys(loadConfig$1()?.projects ?? {}).filter((path) => {
587
+ if (path === nearest || !isPathPrefix(worktree, expandedRealpath(path)) || !registry.isMobileProject(path)) return false;
588
+ const owner = repoRoot(path);
589
+ return owner !== null && expandedRealpath(owner) === worktree;
590
+ });
591
+ return apps.length === 1 ? {
592
+ root: apps[0],
593
+ from: nearest
594
+ } : {
595
+ root: nearest,
596
+ from: null
597
+ };
598
+ }
599
+ function findCommandWorkspace(startDir, note = (line) => console.error(line)) {
600
+ const workspace = findServerWorkspace(startDir);
601
+ if (workspace?.from) note(`Using the Stim workspace ${workspace.root}: ${workspace.from} is not a React Native or Expo app.`);
602
+ return workspace?.root ?? null;
603
+ }
604
+ function expandedRealpath(path) {
605
+ try {
606
+ return realpathSync$1.native(path);
607
+ } catch {
608
+ return resolve$1(path);
609
+ }
610
+ }
611
+ //#endregion
612
+ export { readWorkspaceLaunches as A, clearRemoteSession as C, readPidFile as D, readMetroTunnel as E, writeWorkspaceLaunch as M, readRemoteSession as O, clearManagedMetroTunnel as S, clearWorkspaceSupervisor as T, readAppJson as _, isMobileProject as a, MODE_EXPO as b, projectShortcut as c, projectRegistry as d, appProjectProblem as f, readAppConfigText as g, isPackageResolvable as h, findServerWorkspace as i, writePidFile as j, readRemoteSessionId as k, detectPlatforms as l, detectTutorial as m, findCommandWorkspace as n, ownedDeviceLabel as o, detectIsExpo as p, findProjectRoot as r, projectProblem as s, NO_PROJECT_REFUSAL as t, nativeProjectIntegration as u, resolvePackageJson as v, clearWorkspaceLaunches as w, clearExpoMetroTunnel as x, MODE_BARE as y };
@@ -1,4 +1,4 @@
1
- import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
1
+ import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
2
2
  //#region src/workspace/pull-request.ts
3
3
  const GH_TIMEOUT_MS = 2e4;
4
4
  const GH_ENV = {
@@ -1,2 +1,2 @@
1
- import { a as pullRequestLookup, c as worktreePullRequests, i as parseRemoteRepo, n as describePullRequest, o as pullRequestLookups, r as endedPullRequest, s as selectPullRequest, t as checkCounts } from "./pull-request-u4vp7zmK.mjs";
1
+ import { a as pullRequestLookup, c as worktreePullRequests, i as parseRemoteRepo, n as describePullRequest, o as pullRequestLookups, r as endedPullRequest, s as selectPullRequest, t as checkCounts } from "./pull-request-IHNdMnG8.mjs";
2
2
  export { checkCounts, describePullRequest, endedPullRequest, parseRemoteRepo, pullRequestLookup, pullRequestLookups, selectPullRequest, worktreePullRequests };
@@ -0,0 +1,2 @@
1
+ import { t as reactNativeIosProject } from "./react-native-ios-CSpIxexZ.mjs";
2
+ export { reactNativeIosProject };