stim 1.19.0 → 1.20.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 (172) hide show
  1. package/README.md +20 -3
  2. package/dist/{activity-BSw0x84R.mjs → activity-BrGh9EAI.mjs} +3 -3
  3. package/dist/{agent-device-usage-output-Bjxu_VmX.mjs → agent-device-usage-output-DVyv_hWj.mjs} +7 -7
  4. package/dist/android-0vv-OruW.mjs +6 -0
  5. package/dist/android-Brx6EKvJ.mjs +1258 -0
  6. package/dist/{android-BdQC6zs1.mjs → android-BsYNR81W.mjs} +103 -45
  7. package/dist/{android-DE5g_5gv.mjs → android-CK3fpEyZ.mjs} +2 -2
  8. package/dist/{android-cas-C87geAg-.mjs → android-cas-DCAuXryb.mjs} +9 -5
  9. package/dist/android-cas-compiler.mjs +1 -1
  10. package/dist/api-run.mjs +159 -8
  11. package/dist/{api-DIf_s86s.mjs → api-zuX3ynYN.mjs} +5 -1
  12. package/dist/api.d.mts +98 -4
  13. package/dist/api.mjs +1 -1
  14. package/dist/{app-install-CFI4ptUL.mjs → app-install-0LgaQRT7.mjs} +9 -8
  15. package/dist/artifact-CnC0mfov.mjs +807 -0
  16. package/dist/artifact-DClXJNWx.mjs +772 -0
  17. package/dist/artifact-lifecycle-DzPpBYvK.mjs +30 -0
  18. package/dist/{browser-web-CDhqQxhv.mjs → browser-web-CzBmhmgs.mjs} +19 -19
  19. package/dist/{budget-Dz-kyXLv.mjs → budget-BONq9vXY.mjs} +3101 -3062
  20. package/dist/build-BixRI6sB.mjs +462 -0
  21. package/dist/{build-facts-B87vo-Im.d.mts → build-facts-DcLv2HSd.d.mts} +1 -32
  22. package/dist/build-plan-CP0tckNU.mjs +553 -0
  23. package/dist/{cache-manifest-DiwKSyv9.mjs → cache-manifest-qA3dT2x2.mjs} +1 -1
  24. package/dist/cache-manifest.mjs +1 -1
  25. package/dist/{manifest-CX0d1IkZ.mjs → ccache-CjHQ5F8g.mjs} +13 -19
  26. package/dist/{chrome-BTUOo8p2.mjs → chrome-Dp2lMLBe.mjs} +1 -1
  27. package/dist/{cli-C9Zvgbdc.mjs → cli-B2R8wH1F.mjs} +20 -20
  28. package/dist/cli.mjs +1 -1
  29. package/dist/{client-CuUo8Nvg.mjs → client-BLy1Pw2V.mjs} +89 -307
  30. package/dist/collector-run.d.mts +4 -0
  31. package/dist/collector-run.mjs +7 -7
  32. package/dist/{command-output-Bt8ygHE7.mjs → command-output-BjPN99ZB.mjs} +2 -2
  33. package/dist/{config-DC5jEtd1.mjs → config-D0LhTY3S.mjs} +8 -12
  34. package/dist/{created-devices-D0s26Hx5.mjs → created-devices-HU_DAb_Q.mjs} +7 -6
  35. package/dist/{dependency-state-DKaS1OM9.mjs → dependency-state-B2m8eqsS.mjs} +2 -2
  36. package/dist/{detached-entry-pB_IPfV4.mjs → detached-entry-BXq4fIl-.mjs} +1 -1
  37. package/dist/{dev-client-CDY_zAmD.mjs → dev-client-ClATaJoH.mjs} +2 -2
  38. package/dist/{device-BHw0WlHl.mjs → device-B7lsC0K1.mjs} +8 -8
  39. package/dist/device-DVCwAFlX.mjs +727 -0
  40. package/dist/{device-capacity-BoN1jQkO.mjs → device-capacity-Ca_ke1LE.mjs} +13 -14
  41. package/dist/device-host-worker.mjs +38 -18
  42. package/dist/device-ios-CRUqPxKw.mjs +413 -0
  43. package/dist/{device-lease-Cu7PtQpZ.mjs → device-lease-DHOBXGgS.mjs} +135 -22
  44. package/dist/{device-lease-run-D38SnZtt.mjs → device-lease-run-CvDeHTkG.mjs} +4 -4
  45. package/dist/{device-pool-CP4rAsHx.mjs → device-pool-L2-5GxMy.mjs} +3 -3
  46. package/dist/{device-remote-B5GiUifk.mjs → device-remote-Bmg42063.mjs} +17 -68
  47. package/dist/doctor-C2MSuMMg.mjs +515 -0
  48. package/dist/{doctor-PRJH94v6.mjs → doctor-DPAqhVCK.mjs} +333 -744
  49. package/dist/{doctor-watchman-wZhyTdp4.mjs → doctor-watchman-CB4NnVZm.mjs} +13 -12
  50. package/dist/{simslim-D9LkJxIO.mjs → eas-build-Dw_ec2Km.mjs} +6 -152
  51. package/dist/{stim-installations-DbmUjsLv.mjs → eas-session-ledger-DZlC-Fq3.mjs} +57 -4
  52. package/dist/{error-diagnostics--v5HGOU0.mjs → error-diagnostics-Du7I_GuY.mjs} +17 -12
  53. package/dist/errors-DzkMYh8Q.mjs +205 -0
  54. package/dist/{exec-i8GDPwq9.mjs → exec-B81Qodjn.mjs} +40 -10
  55. package/dist/{gc-CyHMcKcn.mjs → gc-PWp34zGb.mjs} +21 -20
  56. package/dist/{device-ios-B2ruohE-.mjs → gradle-C2OEKCCI.mjs} +21 -650
  57. package/dist/{guide-dRaQJJk9.mjs → guide-Dai9lphv.mjs} +504 -117
  58. package/dist/{hosted-android-Bs7fytj7.mjs → hosted-android-DQ-Agsd8.mjs} +5 -5
  59. package/dist/{hosted-client-C5G4h_OJ.mjs → hosted-client-Io01s5By.mjs} +9 -3
  60. package/dist/{hosted-ios-Bik035eL.mjs → hosted-ios-DIxFnYp-.mjs} +3 -3
  61. package/dist/{hosted-logs-CI9Az9a4.mjs → hosted-logs-DP-ABU_M.mjs} +8 -8
  62. package/dist/{hosted-macos-DABm3ScO.mjs → hosted-macos-Aziw9T_9.mjs} +7 -6
  63. package/dist/{hosted-native-DbV2aUVw.mjs → hosted-native-BdAQi21M.mjs} +17 -14
  64. package/dist/{idle-shutdown-DYiMBn34.mjs → idle-shutdown-DdMqoSUB.mjs} +7 -7
  65. package/dist/{ios-DYUr9LGc.mjs → ios-BZ9dg2Fr.mjs} +8 -7
  66. package/dist/{ios-BWbHFZVz.mjs → ios-CXLIUxXJ.mjs} +180 -24
  67. package/dist/ios-CZuv6gRv.mjs +1112 -0
  68. package/dist/{ios-DCrkgXEM.mjs → ios-VGvNdsv2.mjs} +1 -1
  69. package/dist/{ios-device-CrW8aMb5.mjs → ios-device-B1B14xND.mjs} +1 -1
  70. package/dist/{ios-device-CJ_ZOGky.mjs → ios-device-BreZOpM0.mjs} +3 -3
  71. package/dist/ios-project-B9drt_aY.mjs +197 -0
  72. package/dist/{ios-state-By0XuLTQ.mjs → ios-state-CjrL2qv-.mjs} +1 -1
  73. package/dist/js-swap-C8icNYIY.mjs +432 -0
  74. package/dist/launch-DxH0THLP.mjs +745 -0
  75. package/dist/{react-native-ios-CSpIxexZ.mjs → launch-YrbqaIFj.mjs} +59 -612
  76. package/dist/{launch-verify-DCveSE6d.mjs → launch-verify-BzRdbuO1.mjs} +26 -7
  77. package/dist/{logs-Bao1HOMy.mjs → logs-DHaRNfg7.mjs} +11 -11
  78. package/dist/macos-C0iljhsN.mjs +2 -0
  79. package/dist/{macos-ciyBdTY5.mjs → macos-V_3Ui7g4.mjs} +42 -50
  80. package/dist/macos-run.mjs +1 -1
  81. package/dist/maintenance-run.mjs +13 -10
  82. package/dist/{metro-DI2AjPF_.mjs → metro-D_j9sN-B.mjs} +18 -5
  83. package/dist/{metro-gateway-BxpJLIwh.mjs → metro-gateway-BBJldOdi.mjs} +2 -2
  84. package/dist/miss-reason-BPAzZS0r.mjs +238 -0
  85. package/dist/{named-ports-B4iWH4E-.mjs → named-ports-C2lvoPsb.mjs} +3 -3
  86. package/dist/native-android-lHzaew7r.mjs +226 -0
  87. package/dist/native-gradle-inputs-BQnIdAlL.mjs +356 -0
  88. package/dist/{native-runtime-BGxsbfny.mjs → native-runtime-BRmWL7ru.mjs} +9 -8
  89. package/dist/native-xcode-inputs-eutS19jU.mjs +307 -0
  90. package/dist/native-xcode-ios-DP3cHLwu.mjs +434 -0
  91. package/dist/{ndjson-LGZAMsqa.mjs → ndjson-GOnbyvso.mjs} +6 -6
  92. package/dist/offload-worker.d.mts +41 -7
  93. package/dist/offload-worker.mjs +409 -74
  94. package/dist/{ownership-EB0ySdGK.mjs → ownership-DXzMMSx6.mjs} +2 -2
  95. package/dist/{placement-log-Dxe34rJm.mjs → placement-log-reFbjqEr.mjs} +1 -1
  96. package/dist/plan-BDk2AeY_.mjs +260 -0
  97. package/dist/plan-placement-D_YYlE6N.mjs +73 -0
  98. package/dist/{ports-ByPc0_97.mjs → ports-jzvAJXXL.mjs} +3 -3
  99. package/dist/{prebuild-BKJ3wJI1.mjs → prebuild-Bw44QxVh.mjs} +4 -4
  100. package/dist/{preview-BbSottmO.mjs → preview-BaBJ72Al.mjs} +50 -32
  101. package/dist/project-B8_duAzW.mjs +1067 -0
  102. package/dist/project-plan-BPwbLwQu.mjs +10 -0
  103. package/dist/{pull-request-IHNdMnG8.mjs → pull-request-vOhLNmIb.mjs} +1 -1
  104. package/dist/pull-requests.mjs +1 -1
  105. package/dist/react-native-android-chm1DMlA.mjs +921 -0
  106. package/dist/react-native-build-oAw6gQeW.mjs +239 -0
  107. package/dist/react-native-doctor-DYXoh-6I.mjs +78 -0
  108. package/dist/react-native-ios-SCTK_CT5.mjs +595 -0
  109. package/dist/{recordings-Bv8McZN4.mjs → recordings-Bk-VivUV.mjs} +1 -1
  110. package/dist/{reload-rAPdqeRR.mjs → reload-D34GkY78.mjs} +17 -13
  111. package/dist/remote-BU9UyhCJ.mjs +192 -0
  112. package/dist/{remote-cache-N3hAEaAL.mjs → remote-cache-Bsts6f6Y.mjs} +5 -5
  113. package/dist/result-BDoRk1k3.mjs +186 -0
  114. package/dist/{run-lAkle1-c.mjs → run-qvff78hv.mjs} +3 -3
  115. package/dist/{server-bare-CkIjnegq.mjs → server-bare-dWjAarki.mjs} +5 -5
  116. package/dist/server-command-7XIKlWtO.mjs +69 -0
  117. package/dist/{server-expo-n3tUi3Em.mjs → server-expo-CsYu2aKL.mjs} +107 -82
  118. package/dist/server-expo-DV43vep4.mjs +2 -0
  119. package/dist/{settings-CxgngUZk.mjs → settings-Bbztm4oV.mjs} +179 -129
  120. package/dist/{settings-2am7TSxp.mjs → settings-CT3E71-i.mjs} +6 -6
  121. package/dist/{state-lKh8shBp.d.mts → settings-vk3mJGSm.d.mts} +4 -2
  122. package/dist/settings.schema.json +84 -0
  123. package/dist/simslim-ChMfe8rg.mjs +149 -0
  124. package/dist/{slot-launch-HV_KtPIw.mjs → slot-launch-B9hn96wd.mjs} +6 -6
  125. package/dist/{stage-BgaZJVZY.mjs → stage-BNKKdDNc.mjs} +6 -3
  126. package/dist/start-CbQCSnXQ.mjs +2 -0
  127. package/dist/{start-DtxthMfI.mjs → start-CdvcTaXr.mjs} +97 -68
  128. package/dist/{state-Tp4uImDS.mjs → state-Boo0IMJq.mjs} +1 -1
  129. package/dist/{state-D2KtnALG.mjs → state-DI84A-uu.mjs} +29 -10
  130. package/dist/{stats-8EJ5ivFT.mjs → stats-BIIDrjA8.mjs} +5 -5
  131. package/dist/{status-BEeL6IKz.mjs → status-BDGHwkCs.mjs} +3 -1
  132. package/dist/{status-BOyp0CUl.mjs → status-DARMkc9q.mjs} +81 -293
  133. package/dist/status-watch-DvmI3SmN.mjs +296 -0
  134. package/dist/{stim-desktop-C65jk_q2.mjs → stim-desktop-8GQX2OV8.mjs} +1 -1
  135. package/dist/{stop-DRipAg-e.mjs → stop-CCZS-C9m.mjs} +2 -2
  136. package/dist/{stop-D0UIpZuh.mjs → stop-WQPQHLuy.mjs} +5 -5
  137. package/dist/{stop-zDlknFoz.mjs → stop-iNemHsJC.mjs} +21 -20
  138. package/dist/supervisor-run.d.mts +7 -2
  139. package/dist/supervisor-run.mjs +32 -20
  140. package/dist/{support-DsNaFg6D.mjs → support-CnE_SIjt.mjs} +17 -7
  141. package/dist/{support-CNqYkV0y.mjs → support-DCco5fut.mjs} +61 -8
  142. package/dist/{swiftpm-macos-aJzPAj1t.mjs → swiftpm-macos-afeNUExW.mjs} +5 -5
  143. package/dist/tailnet-CoJUb2tB.mjs +265 -0
  144. package/dist/toolchain-BIupIro1.mjs +2 -0
  145. package/dist/{toolchain-C3xSoyUl.mjs → toolchain-DqyDXs30.mjs} +206 -53
  146. package/dist/{trigger-CAhmG-4s.mjs → trigger-C0MWBi8O.mjs} +2 -2
  147. package/dist/trigger-SHLOWJ4O.mjs +2 -0
  148. package/dist/{warm-progress-pFLB2e6P.mjs → warm-progress-C3ASD6z5.mjs} +6 -6
  149. package/dist/{watchman-BL1IdOtq.mjs → watchman-CQl51YZu.mjs} +6 -6
  150. package/dist/{web-DbbrPUn0.mjs → web-B3eldHFH.mjs} +9 -13
  151. package/dist/web-BLXpOe6b.mjs +2 -0
  152. package/dist/web-run.mjs +10 -10
  153. package/dist/{workspace-state-CR9X86xJ.mjs → workspace-state-CSIFuuDa.mjs} +33 -33
  154. package/dist/{workspaces-Djb2gfi9.mjs → workspaces-D2xSHBDY.mjs} +1260 -1258
  155. package/dist/{worktree-BL4M1rhB.mjs → worktree-BxDqaTev.mjs} +84 -48
  156. package/dist/worktree-q7_OZNY0.mjs +2 -0
  157. package/package.json +8 -6
  158. package/shim/native-android.gradle +101 -0
  159. package/dist/android-BXc6BmEi.mjs +0 -3732
  160. package/dist/android-BvV1gGdN.mjs +0 -4
  161. package/dist/ios-ITYno9rO.mjs +0 -2241
  162. package/dist/macos-nIOATJmR.mjs +0 -2
  163. package/dist/metro-store-BAHFcJm6.mjs +0 -106
  164. package/dist/miss-reason-CjQCOKqq.mjs +0 -1743
  165. package/dist/plan-placement-BVY59qa8.mjs +0 -290
  166. package/dist/project-B5eQ1USs.mjs +0 -612
  167. package/dist/react-native-ios-BMh2i25I.mjs +0 -2
  168. package/dist/server-expo-DGsdUGX2.mjs +0 -2
  169. package/dist/start-D8NbxNBb.mjs +0 -2
  170. package/dist/trigger-CoalsMTV.mjs +0 -2
  171. package/dist/web-DFpFeZu5.mjs +0 -2
  172. package/dist/worktree-DV-A0LNL.mjs +0 -2
@@ -0,0 +1,1067 @@
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-D0LhTY3S.mjs";
3
+ import { $ as settingValueAt, Et as repoRoot, G as resolveIosProjectDir, U as resolveAndroidLayout, rt as webSettings } from "./settings-Bbztm4oV.mjs";
4
+ import { E as workspaceStateFile, a as updateWorkspaceState, o as withWorkspaceStateLock, r as readWorkspaceState$1, t as clearWorkspaceStateKey, v as supervisorPidFile } from "./workspace-state-CSIFuuDa.mjs";
5
+ import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
6
+ import { basename, dirname, isAbsolute, join, relative, 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
+ import { DOMParser } from "@xmldom/xmldom";
11
+ import pbxParser from "xcode/lib/parser/pbxproj.js";
12
+ //#region src/supervisor/state.ts
13
+ const MODE_BARE = "bare-inproc";
14
+ const MODE_EXPO = "expo-child";
15
+ const MODE_COMMAND = "command-child";
16
+ function workspaceDevServerMode(root, isExpo) {
17
+ const mode = readWorkspaceState$1(root)?.supervisor?.mode;
18
+ return typeof mode === "string" ? mode : isExpo ? MODE_EXPO : MODE_BARE;
19
+ }
20
+ function readMetroTunnel(root) {
21
+ const record = readWorkspaceState$1(root)?.metroTunnel;
22
+ if (!record || typeof record !== "object") return null;
23
+ const kind = record.kind;
24
+ const url = record.url;
25
+ if (typeof url !== "string" || url.length === 0) return null;
26
+ if (kind === "expo") return {
27
+ kind: "expo",
28
+ url
29
+ };
30
+ if (kind === "managed") {
31
+ const provider = record.provider;
32
+ const pid = record.pid;
33
+ const port = record.port;
34
+ const startedAt = record.startedAt;
35
+ const processToken = record.processToken;
36
+ const logFile = record.logFile;
37
+ if (provider !== "ngrok" && provider !== "cloudflared" && provider !== "tailscale" || typeof pid !== "number" || typeof port !== "number") return null;
38
+ return {
39
+ kind: "managed",
40
+ provider,
41
+ pid,
42
+ url,
43
+ port,
44
+ startedAt: typeof startedAt === "string" ? startedAt : "",
45
+ processToken: typeof processToken === "string" && processToken.length > 0 ? processToken : null,
46
+ ...typeof logFile === "string" && logFile.length > 0 ? { logFile } : {}
47
+ };
48
+ }
49
+ return null;
50
+ }
51
+ function parseWorkspaceLaunchRecord(value) {
52
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
53
+ const record = value;
54
+ if (typeof record.appId !== "string" || record.appId.length === 0) return null;
55
+ if (typeof record.deviceId !== "string" || record.deviceId.length === 0) return null;
56
+ if (record.metroPort !== null && typeof record.metroPort !== "number") return null;
57
+ if (typeof record.release !== "boolean") return null;
58
+ if (record.runtime !== void 0 && (record.runtime !== "process" || record.metroPort !== null)) return null;
59
+ if (typeof record.launchedAt !== "string") return null;
60
+ return record;
61
+ }
62
+ function readWorkspaceLaunches(root) {
63
+ const launches = readWorkspaceState$1(root)?.launches;
64
+ if (!launches || typeof launches !== "object" || Array.isArray(launches)) return {};
65
+ const records = {};
66
+ for (const [key, value] of Object.entries(launches)) {
67
+ const record = parseWorkspaceLaunchRecord(value);
68
+ if (parseDeviceSlotKey(key) && record) records[key] = record;
69
+ }
70
+ return records;
71
+ }
72
+ function writeWorkspaceLaunch(root, platform, record, slot = "default") {
73
+ updateWorkspaceState(root, (state) => {
74
+ const launches = state.launches && typeof state.launches === "object" ? state.launches : {};
75
+ return {
76
+ ...state,
77
+ launches: {
78
+ ...launches,
79
+ [deviceSlotKey(platform, slot)]: record
80
+ }
81
+ };
82
+ });
83
+ }
84
+ function clearWorkspaceLaunches(root, slot) {
85
+ updateWorkspaceState(root, (state) => {
86
+ if (!state.launches || typeof state.launches !== "object") return state;
87
+ const launches = { ...state.launches };
88
+ delete launches[deviceSlotKey("ios", slot)];
89
+ delete launches[deviceSlotKey("android", slot)];
90
+ return {
91
+ ...state,
92
+ launches
93
+ };
94
+ });
95
+ }
96
+ function readRemoteSession(root) {
97
+ const record = readWorkspaceState$1(root)?.remoteDevice;
98
+ if (!record || typeof record !== "object") return null;
99
+ const id = record.sessionId;
100
+ if (typeof id !== "string" || id.length === 0) return null;
101
+ const platform = record.platform;
102
+ const startedAt = record.startedAt;
103
+ const webPreviewUrl = record.webPreviewUrl;
104
+ const deviceType = record.deviceType;
105
+ return {
106
+ platform: platform === "ios" || platform === "android" ? platform : null,
107
+ sessionId: id,
108
+ startedAt: typeof startedAt === "string" ? startedAt : null,
109
+ webPreviewUrl: typeof webPreviewUrl === "string" && webPreviewUrl.length > 0 ? webPreviewUrl : null,
110
+ deviceType: typeof deviceType === "string" && deviceType.length > 0 ? deviceType : null
111
+ };
112
+ }
113
+ function readRemoteSessionId(root) {
114
+ return readRemoteSession(root)?.sessionId ?? null;
115
+ }
116
+ function clearRemoteSession(root, expectedSessionId) {
117
+ clearWorkspaceStateKey(root, "remoteDevice", (value) => {
118
+ if (typeof value !== "object" || value === null) return false;
119
+ return value.sessionId === expectedSessionId;
120
+ });
121
+ }
122
+ function clearWorkspaceSupervisor(root, expected) {
123
+ return withWorkspaceStateLock(root, () => {
124
+ const current = readWorkspaceState$1(root)?.supervisor;
125
+ if (current && expected !== void 0 && !(0, process_identity_exports.sameProcessRecord)(current, expected)) return false;
126
+ clearWorkspaceStateKey(root, "supervisor", () => true);
127
+ clearExpoMetroTunnel(root);
128
+ if (expected === void 0 || readPidFile(root) === expected?.pid) rmSync(supervisorPidFile(root), { force: true });
129
+ return true;
130
+ });
131
+ }
132
+ function clearExpoMetroTunnel(root) {
133
+ clearWorkspaceStateKey(root, "metroTunnel", (value) => {
134
+ return typeof value === "object" && value !== null && value.kind === "expo";
135
+ });
136
+ }
137
+ function clearManagedMetroTunnel(root, expected) {
138
+ if (!existsSync(workspaceStateFile(root))) return true;
139
+ return clearWorkspaceStateKey(root, "metroTunnel", (value) => {
140
+ if (typeof value !== "object" || value === null) return false;
141
+ const record = value;
142
+ 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);
143
+ });
144
+ }
145
+ function writePidFile(root, pid) {
146
+ const file = supervisorPidFile(root);
147
+ mkdirSync(dirname(file), { recursive: true });
148
+ writeFileSync(file, `${pid}\n`);
149
+ return file;
150
+ }
151
+ function readPidFile(root) {
152
+ try {
153
+ const pid = parseInt(readFileSync(supervisorPidFile(root), "utf-8").trim(), 10);
154
+ return Number.isFinite(pid) ? pid : null;
155
+ } catch {
156
+ return null;
157
+ }
158
+ }
159
+ //#endregion
160
+ //#region src/workspace/project-files.ts
161
+ /**
162
+ * The realpath of `name`'s package.json in the nearest `node_modules` at or above `projectRoot`.
163
+ * Unlike `require.resolve`, it ignores `NODE_PATH`, which pnpm's bin shims point at the workspace's
164
+ * hidden hoist directory holding every member's dependencies.
165
+ */
166
+ function resolvePackageJson(projectRoot, name) {
167
+ for (let dir = resolve$1(projectRoot);; dir = dirname$1(dir)) {
168
+ const candidate = join$1(dir, "node_modules", name, "package.json");
169
+ if (existsSync$1(candidate)) return realpathSync$1(candidate);
170
+ if (dirname$1(dir) === dir) return null;
171
+ }
172
+ }
173
+ function isPackageResolvable(projectRoot, name) {
174
+ return resolvePackageJson(projectRoot, name) !== null;
175
+ }
176
+ function loadPackageJson(projectRoot) {
177
+ const p = join$1(projectRoot, "package.json");
178
+ if (!existsSync$1(p)) return {
179
+ pkg: null,
180
+ parseError: null
181
+ };
182
+ try {
183
+ return {
184
+ pkg: JSON.parse(readFileSync$1(p, "utf-8")),
185
+ parseError: null
186
+ };
187
+ } catch (error) {
188
+ return {
189
+ pkg: null,
190
+ parseError: error?.message || String(error)
191
+ };
192
+ }
193
+ }
194
+ function readPackageJson(projectRoot) {
195
+ return loadPackageJson(projectRoot).pkg;
196
+ }
197
+ const APP_DEPENDENCIES = ["react-native", "expo"];
198
+ function declaresAppDependency(pkg) {
199
+ if (!pkg || typeof pkg !== "object") return false;
200
+ const { dependencies, devDependencies, peerDependencies } = pkg;
201
+ const deps = {
202
+ ...dependencies,
203
+ ...devDependencies,
204
+ ...peerDependencies
205
+ };
206
+ return APP_DEPENDENCIES.some((name) => name in deps);
207
+ }
208
+ function appProjectProblem(projectRoot, { pkg, parseError } = loadPackageJson(projectRoot)) {
209
+ const file = join$1(projectRoot, "package.json");
210
+ if (parseError) return {
211
+ kind: "unreadable",
212
+ message: `${file} is not valid JSON (${parseError}), so Stim cannot tell whether this is a React Native or Expo app.`,
213
+ remedy: `Fix the JSON in ${file} -- an unfinished edit or a merge conflict leaves it unparseable -- then run the command again.`
214
+ };
215
+ if (declaresAppDependency(pkg)) return null;
216
+ return {
217
+ kind: "not-an-app",
218
+ message: `${file} depends on neither react-native nor expo, so this is not a React Native or Expo app.`,
219
+ 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."
220
+ };
221
+ }
222
+ function detectIsExpo(projectRoot) {
223
+ const pkg = readPackageJson(projectRoot);
224
+ const iosScript = pkg?.scripts?.ios;
225
+ if (typeof iosScript === "string") {
226
+ if (/\bexpo\s+run:ios\b/.test(iosScript)) return true;
227
+ if (/\breact-native\s+run-ios\b/.test(iosScript)) return false;
228
+ }
229
+ if (!pkg) return false;
230
+ const hasExpoDep = "expo" in {
231
+ ...pkg.dependencies,
232
+ ...pkg.devDependencies
233
+ };
234
+ const resolvable = () => isPackageResolvable(projectRoot, "expo");
235
+ if (!hasExpoDep && !resolvable()) return false;
236
+ const appJson = readAppJson(projectRoot);
237
+ if (appJson?.expo) return true;
238
+ const text = readAppConfigText(projectRoot);
239
+ if (text && /\b(?:from\s+['"]expo['"]|expo\/config|ExpoConfig)\b/.test(text)) return true;
240
+ if (hasExpoDep && resolvable()) return true;
241
+ if (looksLikeExpoConfig(appJson)) return true;
242
+ if (podfileUsesExpoModules(projectRoot)) return true;
243
+ return false;
244
+ }
245
+ function detectTutorial(appJson) {
246
+ const extra = appJson?.expo?.extra;
247
+ const version = typeof extra === "object" && extra !== null ? extra.stimTutorial : null;
248
+ return typeof version === "number" && Number.isInteger(version) && version > 0 ? { version } : void 0;
249
+ }
250
+ function looksLikeExpoConfig(appJson) {
251
+ if (!appJson || typeof appJson !== "object" || Array.isArray(appJson)) return false;
252
+ if (appJson.expo) return true;
253
+ if ([
254
+ "slug",
255
+ "plugins",
256
+ "sdkVersion",
257
+ "experiments",
258
+ "runtimeVersion",
259
+ "updates",
260
+ "buildCacheProvider",
261
+ "assetBundlePatterns",
262
+ "splash",
263
+ "orientation",
264
+ "userInterfaceStyle"
265
+ ].some((key) => appJson[key] !== void 0)) return true;
266
+ return Boolean(appJson.extra && typeof appJson.extra === "object" && appJson.extra.eas);
267
+ }
268
+ function podfileUsesExpoModules(projectRoot) {
269
+ const p = join$1(projectRoot, "ios", "Podfile");
270
+ if (!existsSync$1(p)) return false;
271
+ try {
272
+ return /^[^#\n]*use_expo_modules!/m.test(readFileSync$1(p, "utf-8"));
273
+ } catch {
274
+ return false;
275
+ }
276
+ }
277
+ function readAppJson(projectRoot) {
278
+ const p = join$1(projectRoot, "app.json");
279
+ if (!existsSync$1(p)) return null;
280
+ try {
281
+ return JSON.parse(readFileSync$1(p, "utf-8"));
282
+ } catch {
283
+ return null;
284
+ }
285
+ }
286
+ function readAppConfigText(projectRoot) {
287
+ for (const name of [
288
+ "app.config.js",
289
+ "app.config.ts",
290
+ "app.config.cjs",
291
+ "app.config.mjs"
292
+ ]) {
293
+ const p = join$1(projectRoot, name);
294
+ if (existsSync$1(p)) try {
295
+ return readFileSync$1(p, "utf-8");
296
+ } catch {}
297
+ }
298
+ return null;
299
+ }
300
+ //#endregion
301
+ //#region src/integrations/native-android-discovery.ts
302
+ const nativeAndroidIntegration = {
303
+ id: "native-android",
304
+ inspect(root) {
305
+ if (declaresAppDependency(readPackageJson(root)) || !["settings.gradle", "settings.gradle.kts"].some((name) => existsSync(join(root, name))) || !["gradlew", "gradlew.bat"].some((name) => existsSync(join(root, name)))) return null;
306
+ return {
307
+ root: "candidate",
308
+ application: true,
309
+ platforms: () => ["android"],
310
+ validate: (operation) => operation === "android" ? null : void 0,
311
+ android: async () => (await import("./native-android-lHzaew7r.mjs")).nativeAndroidProject(root),
312
+ doctor: async () => (await import("./native-android-lHzaew7r.mjs")).nativeAndroidDoctor(root)
313
+ };
314
+ }
315
+ };
316
+ //#endregion
317
+ //#region src/integrations/native-xcode-syntax.ts
318
+ function parseNativeXcodeSyntax(source) {
319
+ let normalized = "";
320
+ for (let at = 0; at < source.length;) {
321
+ const start = at;
322
+ if (source[at] === "\"") {
323
+ at++;
324
+ while (at < source.length) if (source[at] === "\\") at += 2;
325
+ else if (source[at++] === "\"") break;
326
+ } else if (source.startsWith("/*", at)) {
327
+ const end = source.indexOf("*/", at + 2);
328
+ at = end < 0 ? source.length : end + 2;
329
+ } else if (source.startsWith("//", at)) {
330
+ const end = source.indexOf("\n", at + 2);
331
+ at = end < 0 ? source.length : end + 1;
332
+ } else if (source.startsWith("$(", at)) {
333
+ at += 2;
334
+ let depth = 1;
335
+ while (at < source.length && depth > 0) {
336
+ if (source[at] === "(") depth++;
337
+ else if (source[at] === ")") depth--;
338
+ at++;
339
+ }
340
+ } else {
341
+ if (source[at] === ")") normalized += "\n";
342
+ at++;
343
+ }
344
+ normalized += source.slice(start, at);
345
+ }
346
+ return pbxParser.parse(normalized);
347
+ }
348
+ //#endregion
349
+ //#region src/integrations/native-xcode-project.ts
350
+ var NativeXcodeError = class extends Error {
351
+ code = "STIM_BAD_ARG";
352
+ remedy;
353
+ constructor(message, remedy) {
354
+ super(message);
355
+ this.remedy = remedy;
356
+ }
357
+ };
358
+ function pbxString(value) {
359
+ if (typeof value === "number") return String(value);
360
+ if (typeof value !== "string") return null;
361
+ if (!value.startsWith("\"")) return value.trim();
362
+ try {
363
+ return JSON.parse(value);
364
+ } catch {
365
+ throw new NativeXcodeError("An Xcode project contains an unreadable quoted value.", "Open the project in Xcode and repair its project file.");
366
+ }
367
+ }
368
+ function object(value) {
369
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
370
+ }
371
+ function pbxReferences(value) {
372
+ return Array.isArray(value) ? value.flatMap((entry) => {
373
+ const name = pbxString(object(entry)?.value ?? entry);
374
+ return name === null ? [] : [name];
375
+ }) : [];
376
+ }
377
+ function readNativeXcodeModel(path) {
378
+ const project = object(object(parseNativeXcodeSyntax(readFileSync(join(path, "project.pbxproj"), "utf8")))?.project);
379
+ const objects = /* @__PURE__ */ new Map();
380
+ const collect = (value) => {
381
+ for (const [id, entry] of Object.entries(object(value) ?? {})) {
382
+ const record = object(entry);
383
+ if (!record) continue;
384
+ if (typeof record.isa === "string") objects.set(id, record);
385
+ else collect(record);
386
+ }
387
+ };
388
+ collect(project?.objects);
389
+ const root = objects.get(pbxString(project?.rootObject) ?? "");
390
+ if (!root || root.isa !== "PBXProject") throw new NativeXcodeError(`No Xcode project object in ${path}.`, "Repair the project in Xcode.");
391
+ return {
392
+ path: realpathSync(path),
393
+ directory: realpathSync(dirname(path)),
394
+ project: root,
395
+ objects
396
+ };
397
+ }
398
+ function xml(path) {
399
+ const document = new DOMParser({ onError: (_level, message) => {
400
+ throw new Error(message);
401
+ } }).parseFromString(readFileSync(path, "utf8"), "application/xml");
402
+ if (document.doctype || !document.documentElement) throw new Error(`Unsupported XML document at ${path}.`);
403
+ return document.documentElement;
404
+ }
405
+ function workspaceProjects(path) {
406
+ const projects = [];
407
+ const walk = (node, base) => {
408
+ const location = node.getAttribute("location");
409
+ let here = base;
410
+ if (location) {
411
+ const colon = location.indexOf(":");
412
+ const kind = location.slice(0, colon);
413
+ const value = location.slice(colon + 1);
414
+ if (kind === "group") here = resolve(base, value);
415
+ else if (kind === "container" || kind === "self") here = resolve(dirname(path), value);
416
+ else if (kind === "absolute" && isAbsolute(value)) here = value;
417
+ else throw new Error(`Unsupported workspace reference ${location}.`);
418
+ }
419
+ if (node.tagName === "FileRef") {
420
+ if (!here.endsWith(".xcodeproj")) throw new Error(`Workspace reference ${here} is not an Xcode project.`);
421
+ projects.push(here);
422
+ }
423
+ for (let i = 0; i < node.childNodes.length; i++) {
424
+ const child = node.childNodes.item(i);
425
+ if (child?.nodeType === 1) walk(child, here);
426
+ }
427
+ };
428
+ walk(xml(join(path, "contents.xcworkspacedata")), dirname(path));
429
+ return [...new Set(projects)];
430
+ }
431
+ function configurations(project, owner) {
432
+ return pbxReferences(project.objects.get(pbxString(owner.buildConfigurationList) ?? "")?.buildConfigurations).flatMap((id) => {
433
+ const configuration = project.objects.get(id);
434
+ return configuration ? [configuration] : [];
435
+ });
436
+ }
437
+ function buildSettings(project, target, configuration) {
438
+ const settings = (owner) => object(configurations(project, owner).find((candidate) => pbxString(candidate.name) === configuration)?.buildSettings) ?? {};
439
+ return {
440
+ ...settings(project.project),
441
+ ...settings(target)
442
+ };
443
+ }
444
+ const PLATFORM_KEYS = [
445
+ "SDKROOT",
446
+ "SUPPORTED_PLATFORMS",
447
+ "IPHONEOS_DEPLOYMENT_TARGET"
448
+ ];
449
+ const UNKNOWN_PLATFORM_SETTINGS = { unresolved: true };
450
+ function configurationPath(project, reference) {
451
+ const seen = /* @__PURE__ */ new Set();
452
+ const visit = (id, base) => {
453
+ if (seen.has(id)) return null;
454
+ seen.add(id);
455
+ const entry = project.objects.get(id);
456
+ if (!entry) return null;
457
+ const value = pbxString(entry.path) ?? "";
458
+ if (value.includes("$")) return null;
459
+ const tree = pbxString(entry.sourceTree) ?? "<group>";
460
+ const path = tree === "<group>" ? resolve(base, value) : tree === "SOURCE_ROOT" ? resolve(project.directory, value) : tree === "<absolute>" && isAbsolute(value) ? value : null;
461
+ if (path === null || id === reference) return path;
462
+ for (const child of pbxReferences(entry.children)) {
463
+ const found = visit(child, path);
464
+ if (found !== null) return found;
465
+ }
466
+ return null;
467
+ };
468
+ return visit(pbxString(project.project.mainGroup) ?? "", project.directory);
469
+ }
470
+ function configurationPlatforms(path, active = /* @__PURE__ */ new Set()) {
471
+ try {
472
+ path = realpathSync(path);
473
+ } catch {
474
+ return { ...UNKNOWN_PLATFORM_SETTINGS };
475
+ }
476
+ if (active.has(path)) return { ...UNKNOWN_PLATFORM_SETTINGS };
477
+ active.add(path);
478
+ const settings = {};
479
+ try {
480
+ for (const raw of readFileSync(path, "utf8").split(/\r\n?|\n|\u2028|\u2029/)) {
481
+ const line = raw.replace(/\/\/.*$/, "").trim();
482
+ if (!line) continue;
483
+ const include = line.match(/^#include(\?)?\s+"([^"$]+)"\s*$/);
484
+ if (include) {
485
+ const child = resolve(dirname(path), include[2]);
486
+ if (!include[1] || existsSync(child)) Object.assign(settings, configurationPlatforms(child, active));
487
+ continue;
488
+ }
489
+ const setting = line.match(/^([A-Za-z_][A-Za-z_0-9]*)(\[[^\]]+\])*\s*=\s*(.*)$/);
490
+ if (!setting || line.endsWith("\\") || /\/\*|\*\//.test(line)) {
491
+ Object.assign(settings, UNKNOWN_PLATFORM_SETTINGS);
492
+ continue;
493
+ }
494
+ const key = PLATFORM_KEYS.find((candidate) => candidate === setting[1]);
495
+ if (key) {
496
+ if (setting[2]) settings.unresolved = true;
497
+ const value = pbxString(setting[3]);
498
+ settings[key] = value === null || /[$'\\]/.test(value) ? null : value;
499
+ }
500
+ }
501
+ return settings;
502
+ } catch {
503
+ return { ...UNKNOWN_PLATFORM_SETTINGS };
504
+ } finally {
505
+ active.delete(path);
506
+ }
507
+ }
508
+ function applicationPlatform(project, target, configuration) {
509
+ const settings = {};
510
+ for (const owner of [project.project, target]) {
511
+ const config = configurations(project, owner).find((candidate) => pbxString(candidate.name) === configuration);
512
+ const base = pbxString(config?.baseConfigurationReference);
513
+ if (base) {
514
+ const path = configurationPath(project, base);
515
+ Object.assign(settings, path ? configurationPlatforms(path) : UNKNOWN_PLATFORM_SETTINGS);
516
+ }
517
+ for (const [rawKey, value] of Object.entries(object(config?.buildSettings) ?? {})) {
518
+ if (rawKey.endsWith("_comment")) continue;
519
+ const key = pbxString(rawKey);
520
+ const name = PLATFORM_KEYS.find((candidate) => key === candidate || key.startsWith(`${candidate}[`));
521
+ if (!name) continue;
522
+ const text = Array.isArray(value) ? pbxReferences(value).join(" ") : pbxString(value);
523
+ if (key !== name) settings.unresolved = true;
524
+ settings[name] = text?.includes("$") ? null : text;
525
+ }
526
+ }
527
+ if (settings.unresolved || Object.values(settings).some((value) => value === null)) return "unknown";
528
+ const platforms = (settings.SUPPORTED_PLATFORMS || settings.SDKROOT || "").split(/\s+/).filter(Boolean);
529
+ if (platforms.some((platform) => !/^(?:iphoneos|iphonesimulator|macosx|appletvos|appletvsimulator|watchos|watchsimulator|xros|xrsimulator|driverkit)(?:[0-9.]+)?$/.test(platform))) return "unknown";
530
+ if (platforms.length) return platforms.some((platform) => /^iphone(?:os|simulator)/.test(platform)) ? "ios" : "other";
531
+ return settings.IPHONEOS_DEPLOYMENT_TARGET ? "ios" : "unknown";
532
+ }
533
+ function applicationTargets(project) {
534
+ return [...project.objects].filter(([, target]) => target.isa === "PBXNativeTarget" && pbxString(target.productType) === "com.apple.product-type.application" && configurations(project, target).some((config) => applicationPlatform(project, target, pbxString(config.name) ?? "") !== "other"));
535
+ }
536
+ function sharedSchemes(container, projects) {
537
+ const schemes = [];
538
+ for (const owner of /* @__PURE__ */ new Set([container, ...projects.map((project) => project.path)])) {
539
+ const directory = join(owner, "xcshareddata", "xcschemes");
540
+ if (!existsSync(directory)) continue;
541
+ for (const file of readdirSync(directory).filter((name) => name.endsWith(".xcscheme")).toSorted()) {
542
+ const path = join(directory, file);
543
+ const scheme = xml(path);
544
+ const reference = (scheme.getElementsByTagName("LaunchAction").item(0)?.getElementsByTagName("BuildableProductRunnable").item(0))?.getElementsByTagName("BuildableReference").item(0);
545
+ if (!reference) continue;
546
+ const target = reference.getAttribute("BlueprintIdentifier");
547
+ const location = reference.getAttribute("ReferencedContainer");
548
+ if (!target || !location?.startsWith("container:")) continue;
549
+ const referenced = realpathSync(resolve(dirname(owner), location.slice(10)));
550
+ const project = projects.find((candidate) => candidate.path === referenced);
551
+ if (!project || !applicationTargets(project).some(([id]) => id === target)) continue;
552
+ schemes.push({
553
+ name: basename(file, ".xcscheme"),
554
+ path,
555
+ project,
556
+ target,
557
+ buildScripts: scheme.getElementsByTagName("ExecutionAction").length > 0
558
+ });
559
+ }
560
+ }
561
+ for (const project of projects) for (const [target, value] of applicationTargets(project)) {
562
+ const name = pbxString(value.name);
563
+ if (name && !schemes.some((scheme) => scheme.project === project && scheme.target === target)) schemes.push({
564
+ name,
565
+ path: null,
566
+ project,
567
+ target,
568
+ buildScripts: false
569
+ });
570
+ }
571
+ return schemes;
572
+ }
573
+ function containers(root) {
574
+ const entries = readdirSync(root).toSorted();
575
+ const workspaces = entries.filter((name) => name.endsWith(".xcworkspace"));
576
+ const projects = entries.filter((name) => name.endsWith(".xcodeproj"));
577
+ const result = [];
578
+ const claimed = /* @__PURE__ */ new Set();
579
+ for (const file of workspaces) {
580
+ const path = join(root, file);
581
+ const models = workspaceProjects(path).flatMap((project) => {
582
+ if (!existsSync(project) && existsSync(join(root, "Podfile")) && project === join(root, "Pods", "Pods.xcodeproj")) return [];
583
+ return [readNativeXcodeModel(project)];
584
+ });
585
+ for (const model of models) claimed.add(model.path);
586
+ result.push({
587
+ path,
588
+ projects: models,
589
+ schemes: sharedSchemes(path, models)
590
+ });
591
+ }
592
+ for (const file of projects) {
593
+ const path = join(root, file);
594
+ if (claimed.has(realpathSync(path))) continue;
595
+ const model = readNativeXcodeModel(path);
596
+ result.push({
597
+ path,
598
+ projects: [model],
599
+ schemes: sharedSchemes(path, [model])
600
+ });
601
+ }
602
+ return result;
603
+ }
604
+ function selectNativeXcodeProject(root, scheme, configuration = "Debug") {
605
+ let available;
606
+ try {
607
+ available = containers(root);
608
+ } catch (error) {
609
+ if (error instanceof NativeXcodeError) throw error;
610
+ throw new NativeXcodeError(`Could not read native Xcode project: ${error.message}`, "Repair the project/workspace and its referenced files in Xcode, then retry.");
611
+ }
612
+ const choices = available.flatMap((container) => container.schemes.map((candidate) => ({
613
+ container,
614
+ candidate
615
+ })));
616
+ const matching = scheme === void 0 ? choices : choices.filter(({ candidate }) => candidate.name === scheme);
617
+ if (matching.length !== 1) throw new NativeXcodeError(matching.length ? `Native Xcode application selection is ambiguous${scheme ? ` for scheme ${JSON.stringify(scheme)}` : ""}.` : `No runnable native iOS application${scheme ? ` with scheme ${JSON.stringify(scheme)}` : ""} was found.`, choices.length ? `Select one shared application scheme with --scheme or ios.scheme. Candidates: ${choices.map(({ container, candidate }) => `${relative(root, container.path)}: ${candidate.name}`).join(", ")}. Duplicate container/scheme names must be made unambiguous in Xcode.` : "Open the intended project in Xcode and add a shared scheme whose Run action selects an iOS application.");
618
+ const { container, candidate } = matching[0];
619
+ const target = candidate.project.objects.get(candidate.target);
620
+ const names = configurations(candidate.project, target).map((config) => pbxString(config.name));
621
+ if (!names.includes(configuration)) throw new NativeXcodeError(`Configuration ${JSON.stringify(configuration)} does not exist for ${candidate.name}.`, `Select --configuration or ios.configuration from: ${names.join(", ")}.`);
622
+ const platform = applicationPlatform(candidate.project, target, configuration);
623
+ if (platform === "other") throw new NativeXcodeError(`Configuration ${JSON.stringify(configuration)} does not target iOS for ${candidate.name}.`, "Select an iOS application configuration in Xcode.");
624
+ const workspace = container.path.endsWith(".xcworkspace");
625
+ return {
626
+ container: {
627
+ path: container.path,
628
+ dir: dirname(container.path),
629
+ kind: workspace ? "workspace" : "project",
630
+ flag: workspace ? "-workspace" : "-project",
631
+ name: basename(container.path, workspace ? ".xcworkspace" : ".xcodeproj")
632
+ },
633
+ projects: container.projects,
634
+ scheme: candidate.name,
635
+ targetId: candidate.target,
636
+ targetName: pbxString(target.name),
637
+ targetProject: candidate.project,
638
+ configuration,
639
+ settings: buildSettings(candidate.project, target, configuration),
640
+ platform,
641
+ schemePath: candidate.path,
642
+ schemeBuildScripts: candidate.buildScripts
643
+ };
644
+ }
645
+ const nativeXcodeProjectIntegration = {
646
+ id: "native-xcode",
647
+ inspect(root) {
648
+ if (declaresAppDependency(readPackageJson(root))) return null;
649
+ let entries;
650
+ try {
651
+ entries = readdirSync(root);
652
+ } catch {
653
+ return null;
654
+ }
655
+ if (!entries.some((entry) => (entry.endsWith(".xcodeproj") || entry.endsWith(".xcworkspace")) && statSync(join(root, entry), { throwIfNoEntry: false })?.isDirectory())) return null;
656
+ let problem = null;
657
+ try {
658
+ if (!containers(root).some((container) => container.schemes.length)) problem = {
659
+ kind: "not-an-app",
660
+ message: "This Xcode container has no runnable iOS application.",
661
+ remedy: "Use an iOS application target and shared Run scheme."
662
+ };
663
+ } catch (error) {
664
+ problem = {
665
+ kind: "unreadable",
666
+ message: `Could not read Xcode project inputs: ${error.message}`,
667
+ remedy: "Repair the project/workspace and its references in Xcode."
668
+ };
669
+ }
670
+ return {
671
+ root: "candidate",
672
+ application: problem === null,
673
+ platforms: () => problem ? [] : ["ios"],
674
+ validate: (operation) => operation === "ios" ? problem : void 0,
675
+ ios: async () => (await import("./native-xcode-ios-DP3cHLwu.mjs")).nativeXcodeIosProject(root),
676
+ doctor: async () => (await import("./native-xcode-ios-DP3cHLwu.mjs")).nativeXcodeDoctor(root)
677
+ };
678
+ }
679
+ };
680
+ //#endregion
681
+ //#region src/integrations/project-registry.ts
682
+ function canonicalPath(path) {
683
+ try {
684
+ return realpathSync(resolve(path));
685
+ } catch {
686
+ return resolve(path);
687
+ }
688
+ }
689
+ function operationSelection(root, matches, operation) {
690
+ const results = matches.flatMap((match) => {
691
+ const problem = match.validate?.(operation);
692
+ return problem === void 0 ? [] : [{
693
+ match,
694
+ problem
695
+ }];
696
+ });
697
+ const unreadable = results.find(({ problem }) => problem?.kind === "unreadable");
698
+ if (unreadable?.problem) return { problem: unreadable.problem };
699
+ const providers = results.filter(({ problem }) => problem === null);
700
+ if (providers.length > 1) return { problem: {
701
+ kind: "ambiguous",
702
+ message: `Multiple project integrations support ${operation} at ${root}: ${providers.map(({ match }) => match.id).join(", ")}.`,
703
+ remedy: "Run this from the directory of the app you intend to use, with one integration for this operation."
704
+ } };
705
+ if (providers.length === 1) return { match: providers[0].match };
706
+ return { problem: results[0]?.problem ?? {
707
+ kind: "not-an-app",
708
+ message: `No project integration supports ${operation} at ${root}.`,
709
+ remedy: "Run this from the directory of a supported app."
710
+ } };
711
+ }
712
+ function operationProblem(root, matches, operation) {
713
+ const selected = operationSelection(root, matches, operation);
714
+ return "problem" in selected ? selected.problem : null;
715
+ }
716
+ function createProjectRegistry(integrations) {
717
+ const inspect = (root) => integrations.flatMap((integration) => {
718
+ const match = integration.inspect(root);
719
+ return match ? [{
720
+ id: integration.id,
721
+ ...match
722
+ }] : [];
723
+ });
724
+ function findProjectRoot(startDir, read = inspect) {
725
+ let candidates = [];
726
+ for (const root of projectRootDirectories(startDir)) {
727
+ const matches = read(root);
728
+ const owned = new Set(matches.flatMap((match) => match.ownedRoots ?? []).map(canonicalPath));
729
+ candidates = candidates.filter((candidate) => !owned.has(candidate));
730
+ if (matches.some((match) => match.root === "explicit")) return candidates[0] ?? root;
731
+ if (matches.some((match) => match.root === "candidate")) candidates.push(root);
732
+ }
733
+ return candidates[0] ?? null;
734
+ }
735
+ function ownedRootProblem(root, matches) {
736
+ if (matches.some((match) => match.root === "candidate") && !matches.some((match) => match.root === "explicit")) {
737
+ const owner = findProjectRoot(root, (dir) => dir === root ? matches : inspect(dir));
738
+ if (owner !== null && owner !== root) return {
739
+ kind: "not-an-app",
740
+ message: `${root} belongs to the project at ${owner}.`,
741
+ remedy: `Run this from ${owner}.`
742
+ };
743
+ }
744
+ return null;
745
+ }
746
+ function projectProblem(root, operation) {
747
+ root = canonicalPath(root);
748
+ const matches = inspect(root);
749
+ return ownedRootProblem(root, matches) ?? operationProblem(root, matches, operation);
750
+ }
751
+ function selectAndroid(root) {
752
+ root = canonicalPath(root);
753
+ const matches = inspect(root);
754
+ const problem = ownedRootProblem(root, matches);
755
+ if (problem) return { problem };
756
+ const selected = operationSelection(root, matches, "android");
757
+ if ("problem" in selected) return selected;
758
+ if (selected.match.android) return {
759
+ id: selected.match.id,
760
+ load: selected.match.android
761
+ };
762
+ return { problem: {
763
+ kind: "not-an-app",
764
+ message: `The ${selected.match.id} integration does not provide an Android operation.`,
765
+ remedy: "Use an integration with an Android build and runtime recipe."
766
+ } };
767
+ }
768
+ function selectMacos(root) {
769
+ root = canonicalPath(root);
770
+ const matches = inspect(root);
771
+ const problem = ownedRootProblem(root, matches);
772
+ if (problem) return { problem };
773
+ const selected = operationSelection(root, matches, "macos");
774
+ if ("problem" in selected) return selected;
775
+ if (selected.match.macos) return { load: selected.match.macos };
776
+ return { problem: {
777
+ kind: "not-an-app",
778
+ message: `The ${selected.match.id} integration does not provide a macOS operation.`,
779
+ remedy: "Use an integration with a macOS build recipe."
780
+ } };
781
+ }
782
+ function selectWeb(root) {
783
+ root = canonicalPath(root);
784
+ const matches = inspect(root);
785
+ const problem = ownedRootProblem(root, matches);
786
+ if (problem) return { problem };
787
+ const selected = operationSelection(root, matches, "web");
788
+ if ("problem" in selected) return selected;
789
+ if (selected.match.web) return { load: selected.match.web };
790
+ return { problem: {
791
+ kind: "not-an-app",
792
+ message: `The ${selected.match.id} integration does not provide a web operation.`,
793
+ remedy: "Use an integration with a web runtime recipe."
794
+ } };
795
+ }
796
+ function selectIos(root) {
797
+ root = canonicalPath(root);
798
+ const matches = inspect(root);
799
+ const problem = ownedRootProblem(root, matches);
800
+ if (problem) return { problem };
801
+ const selected = operationSelection(root, matches, "ios");
802
+ if ("problem" in selected) return selected;
803
+ if (selected.match.ios) return {
804
+ id: selected.match.id,
805
+ load: selected.match.ios
806
+ };
807
+ return { problem: {
808
+ kind: "not-an-app",
809
+ message: `The ${selected.match.id} integration does not provide an iOS operation.`,
810
+ remedy: "Use an integration with an iOS build and runtime recipe."
811
+ } };
812
+ }
813
+ function isMobileProject(root) {
814
+ root = canonicalPath(root);
815
+ const matches = inspect(root);
816
+ return ownedRootProblem(root, matches) === null && (operationProblem(root, matches, "ios") === null || operationProblem(root, matches, "android") === null);
817
+ }
818
+ function selectDoctor(root, platform) {
819
+ root = canonicalPath(root);
820
+ const matches = inspect(root);
821
+ const selected = (platform ? [platform] : [
822
+ "ios",
823
+ "android",
824
+ "macos",
825
+ "dev-server"
826
+ ]).map((operation) => ({
827
+ operation,
828
+ result: operationSelection(root, matches, operation)
829
+ }));
830
+ const accepted = selected.flatMap(({ operation, result }) => "match" in result && result.match.application ? [{
831
+ operation,
832
+ match: result.match
833
+ }] : []);
834
+ const invalid = selected.flatMap(({ result }) => "problem" in result ? [result.problem] : []);
835
+ const problem = ownedRootProblem(root, matches) ?? invalid.find((value) => value.kind === "unreadable" || value.kind === "ambiguous") ?? (accepted.length ? null : invalid[0] ?? {
836
+ kind: "not-an-app",
837
+ message: `No project integration recognizes an application at ${root}.`,
838
+ remedy: "Run this from the directory of a supported app."
839
+ });
840
+ const providers = [...new Map(accepted.map(({ match }) => [match.id, match])).values()];
841
+ return {
842
+ problem,
843
+ platforms: problem ? [] : accepted.flatMap(({ operation }) => operation === "ios" || operation === "android" ? [operation] : []),
844
+ load: async () => problem ? [] : Promise.all(providers.flatMap((match) => match.doctor ? [match.doctor()] : []))
845
+ };
846
+ }
847
+ function keepsWorkspace(root) {
848
+ const matches = inspect(root);
849
+ return matches.some((match) => match.application) || [
850
+ "ios",
851
+ "android",
852
+ "dev-server"
853
+ ].some((operation) => operationProblem(root, matches, operation)?.kind === "unreadable");
854
+ }
855
+ function detectPlatforms(root, resolved) {
856
+ const platforms = new Set(inspect(root).flatMap((match) => match.platforms(resolved)));
857
+ return [
858
+ "ios",
859
+ "android",
860
+ "macos",
861
+ "web"
862
+ ].filter((platform) => platforms.has(platform));
863
+ }
864
+ return {
865
+ findProjectRoot,
866
+ projectProblem,
867
+ selectAndroid,
868
+ selectIos,
869
+ selectMacos,
870
+ selectWeb,
871
+ selectDoctor,
872
+ isMobileProject,
873
+ keepsWorkspace,
874
+ detectPlatforms
875
+ };
876
+ }
877
+ //#endregion
878
+ //#region src/integrations/projects.ts
879
+ const expo = {
880
+ mode: MODE_EXPO,
881
+ loadDevServer: async () => (await import("./server-expo-DV43vep4.mjs")).startExpoServer,
882
+ platforms(root) {
883
+ const appJson = readAppJson(root);
884
+ const config = appJson?.expo ?? appJson;
885
+ const text = readAppConfigText(root);
886
+ const explicit = (text ? literalPlatforms(text) : null) ?? config?.platforms;
887
+ if (Array.isArray(explicit)) return explicit.filter((platform) => platform === "ios" || platform === "android" || platform === "web");
888
+ const pkg = readPackageJson(root);
889
+ return [
890
+ "ios",
891
+ "android",
892
+ ..."react-native-web" in {
893
+ ...pkg?.dependencies,
894
+ ...pkg?.devDependencies
895
+ } || isPackageResolvable(root, "react-native-web") ? ["web"] : []
896
+ ];
897
+ }
898
+ };
899
+ const reactNative = {
900
+ mode: MODE_BARE,
901
+ loadDevServer: async () => (await import("./server-bare-dWjAarki.mjs")).startBareServer,
902
+ platforms(root, { context, settings }) {
903
+ if (!declaresAppDependency(readPackageJson(root))) return [];
904
+ const platforms = [];
905
+ let ios = [];
906
+ try {
907
+ ios = readdirSync(resolveIosProjectDir(settings, root).dir);
908
+ } catch {}
909
+ if (ios.some((name) => name.endsWith(".xcodeproj") || name.endsWith(".xcworkspace"))) platforms.push("ios");
910
+ const android = resolveAndroidLayout(settings, root, context.repoRoot ?? root);
911
+ if (android.custom ? existsSync(android.moduleDir) : [
912
+ "build.gradle",
913
+ "build.gradle.kts",
914
+ "settings.gradle",
915
+ "settings.gradle.kts"
916
+ ].some((name) => existsSync(join(android.gradleRoot, name)))) platforms.push("android");
917
+ return platforms;
918
+ }
919
+ };
920
+ const projectRegistry = createProjectRegistry([
921
+ {
922
+ id: "react-native",
923
+ inspect(root) {
924
+ const problem = appProjectProblem(root, loadPackageJson(root));
925
+ return {
926
+ root: existsSync(join(root, "package.json")) ? "explicit" : false,
927
+ application: problem === null,
928
+ ownedRoots: problem === null ? [join(root, "ios"), join(root, "android")] : [],
929
+ platforms: (resolved) => nativeProjectIntegration(root).platforms(root, resolved),
930
+ validate: (operation) => operation === "ios" || operation === "android" || operation === "dev-server" ? problem : void 0,
931
+ ios: async (settings) => (await import("./react-native-ios-SCTK_CT5.mjs")).reactNativeIosProject(root, settings),
932
+ android: async (resolved) => (await import("./react-native-android-chm1DMlA.mjs")).reactNativeAndroidProject(root, resolved),
933
+ doctor: async () => (await import("./react-native-doctor-DYXoh-6I.mjs")).reactNativeProjectDoctor(root)
934
+ };
935
+ }
936
+ },
937
+ nativeAndroidIntegration,
938
+ {
939
+ id: "swift-package",
940
+ inspect(root) {
941
+ if (!existsSync(join(root, "Package.swift"))) return null;
942
+ return {
943
+ root: "explicit",
944
+ application: true,
945
+ platforms: ({ settings }) => settingValueAt(settings, "macos.product") && settingValueAt(settings, "macos.infoPlist") ? ["macos"] : [],
946
+ validate: (operation) => operation === "macos" ? null : void 0,
947
+ macos: async () => (await import("./swiftpm-macos-afeNUExW.mjs")).swiftpmMacosProject(root),
948
+ doctor: async () => {
949
+ const { macosToolchain } = await import("./toolchain-BIupIro1.mjs");
950
+ return {
951
+ inspect: () => [],
952
+ offloadTargets: ({ options: { host = process.platform, platform } }) => host === "darwin" && platform === void 0 ? async () => [{
953
+ platform: "macos",
954
+ local: macosToolchain()
955
+ }] : null
956
+ };
957
+ }
958
+ };
959
+ }
960
+ },
961
+ {
962
+ id: "browser-web",
963
+ inspect(root) {
964
+ return {
965
+ root: false,
966
+ application: false,
967
+ platforms: ({ settings }) => webSettings(settings).url !== null ? ["web"] : [],
968
+ validate: (operation) => operation === "web" ? null : void 0,
969
+ web: async () => (await import("./browser-web-CzBmhmgs.mjs")).browserWebProject(root)
970
+ };
971
+ }
972
+ },
973
+ nativeXcodeProjectIntegration
974
+ ]);
975
+ const detectPlatforms = projectRegistry.detectPlatforms;
976
+ function nativeProjectIntegration(root, isExpo = detectIsExpo) {
977
+ return isExpo(root) ? expo : reactNative;
978
+ }
979
+ function literalPlatforms(text) {
980
+ const tokens = text.match(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|[A-Za-z_$][\w$]*|[^\s]/g)?.filter((token) => !token.startsWith("//") && !token.startsWith("/*")) ?? [];
981
+ let open = 0;
982
+ const index = tokens.findIndex((token, i) => {
983
+ if (token === "[") open++;
984
+ else if (token === "]") open--;
985
+ return open === 0 && (token === "platforms" || token === "\"platforms\"" || token === "'platforms'") && tokens[i + 1] === ":";
986
+ });
987
+ if (index < 0 || tokens[index + 2] !== "[") return null;
988
+ const platforms = [];
989
+ let i = index + 3;
990
+ while (tokens[i] !== "]") {
991
+ const value = tokens[i]?.match(/^(['"])([a-z]+)\1$/)?.[2];
992
+ if (value === void 0) return null;
993
+ platforms.push(value);
994
+ i++;
995
+ if (tokens[i] === ",") i++;
996
+ else if (tokens[i] !== "]") return null;
997
+ }
998
+ return tokens[i + 1] === "," || tokens[i + 1] === "}" ? platforms : null;
999
+ }
1000
+ //#endregion
1001
+ //#region src/workspace/project.ts
1002
+ const findProjectRoot = projectRegistry.findProjectRoot;
1003
+ const projectProblem = projectRegistry.projectProblem;
1004
+ const isMobileProject = projectRegistry.isMobileProject;
1005
+ function projectShortcut(path, proj) {
1006
+ if (proj?.label) return proj.label;
1007
+ const rootPath = findEnclosingWorktreeRoot(path);
1008
+ if (rootPath && rootPath !== path) return `${projectShortcut(rootPath, getProject(rootPath))}/${path.split("/").pop() || path}`;
1009
+ return path.split("/").pop() || path;
1010
+ }
1011
+ function ownedDeviceLabel(projectPath) {
1012
+ const app = realpathSync$1(projectPath);
1013
+ const root = repoRoot(app);
1014
+ const appLabel = basename$1(app);
1015
+ if (!root) return appLabel;
1016
+ const worktreeLabel = basename$1(realpathSync$1(root));
1017
+ return worktreeLabel === appLabel ? appLabel : `${worktreeLabel}-${appLabel}`;
1018
+ }
1019
+ const NO_PROJECT_REFUSAL = {
1020
+ code: "STIM_NO_PROJECT",
1021
+ message: "No supported app project was found from this directory.",
1022
+ remedy: "Run this from the directory of a supported app."
1023
+ };
1024
+ function findServerWorkspace(startDir, registry = projectRegistry) {
1025
+ const nearest = registry.findProjectRoot(startDir);
1026
+ if (!nearest) return null;
1027
+ if (registry.keepsWorkspace(nearest)) return {
1028
+ root: nearest,
1029
+ from: null
1030
+ };
1031
+ if (Object.keys(getProject(nearest)?.ports ?? {}).length) return {
1032
+ root: nearest,
1033
+ from: null
1034
+ };
1035
+ const top = repoRoot(nearest);
1036
+ if (!top) return {
1037
+ root: nearest,
1038
+ from: null
1039
+ };
1040
+ const worktree = expandedRealpath(top);
1041
+ const apps = Object.keys(loadConfig$1()?.projects ?? {}).filter((path) => {
1042
+ if (path === nearest || !isPathPrefix(worktree, expandedRealpath(path)) || !registry.isMobileProject(path)) return false;
1043
+ const owner = repoRoot(path);
1044
+ return owner !== null && expandedRealpath(owner) === worktree;
1045
+ });
1046
+ return apps.length === 1 ? {
1047
+ root: apps[0],
1048
+ from: nearest
1049
+ } : {
1050
+ root: nearest,
1051
+ from: null
1052
+ };
1053
+ }
1054
+ function findCommandWorkspace(startDir, note = (line) => console.error(line)) {
1055
+ const workspace = findServerWorkspace(startDir);
1056
+ if (workspace?.from) note(`Using the Stim workspace ${workspace.root}: ${workspace.from} is not a React Native or Expo app.`);
1057
+ return workspace?.root ?? null;
1058
+ }
1059
+ function expandedRealpath(path) {
1060
+ try {
1061
+ return realpathSync$1.native(path);
1062
+ } catch {
1063
+ return resolve$1(path);
1064
+ }
1065
+ }
1066
+ //#endregion
1067
+ export { clearWorkspaceSupervisor as A, MODE_BARE as C, clearManagedMetroTunnel as D, clearExpoMetroTunnel as E, readWorkspaceLaunches as F, workspaceDevServerMode as I, writePidFile as L, readPidFile as M, readRemoteSession as N, clearRemoteSession as O, readRemoteSessionId as P, writeWorkspaceLaunch as R, resolvePackageJson as S, MODE_EXPO as T, detectIsExpo as _, isMobileProject as a, readAppConfigText as b, projectShortcut as c, projectRegistry as d, NativeXcodeError as f, appProjectProblem as g, selectNativeXcodeProject as h, findServerWorkspace as i, readMetroTunnel as j, clearWorkspaceLaunches as k, detectPlatforms as l, pbxString as m, findCommandWorkspace as n, ownedDeviceLabel as o, pbxReferences as p, findProjectRoot as r, projectProblem as s, NO_PROJECT_REFUSAL as t, nativeProjectIntegration as u, detectTutorial as v, MODE_COMMAND as w, readAppJson as x, isPackageResolvable as y };