stim 1.17.2 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +50 -1
  2. package/dist/{activity-IRvJY_ru.mjs → activity-BSw0x84R.mjs} +4 -4
  3. package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-Bjxu_VmX.mjs} +8 -8
  4. package/dist/{android-B9yaNKwa.mjs → android-BXc6BmEi.mjs} +658 -404
  5. package/dist/{android-B_YNZy61.mjs → android-BdQC6zs1.mjs} +9 -9
  6. package/dist/android-BvV1gGdN.mjs +4 -0
  7. package/dist/{android-DE4v59Mb.mjs → android-DE5g_5gv.mjs} +2 -2
  8. package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-C87geAg-.mjs} +5 -5
  9. package/dist/android-cas-compiler.mjs +2 -2
  10. package/dist/api-DIf_s86s.mjs +150 -0
  11. package/dist/api-run.d.mts +1 -0
  12. package/dist/api-run.mjs +145 -0
  13. package/dist/api.d.mts +218 -0
  14. package/dist/api.mjs +2 -0
  15. package/dist/{app-install-VPkI5WEg.mjs → app-install-CFI4ptUL.mjs} +33 -13
  16. package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
  17. package/dist/{web-Dl0KSRHf.mjs → browser-web-CDhqQxhv.mjs} +138 -176
  18. package/dist/{budget-DfcgFdQB.mjs → budget-Dz-kyXLv.mjs} +1391 -58
  19. package/dist/build-facts-B87vo-Im.d.mts +142 -0
  20. package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-DiwKSyv9.mjs} +3 -3
  21. package/dist/cache-manifest.mjs +1 -1
  22. package/dist/{chrome-BrcVWQyD.mjs → chrome-BTUOo8p2.mjs} +1 -1
  23. package/dist/cli-C9Zvgbdc.mjs +68 -0
  24. package/dist/cli.mjs +1 -1
  25. package/dist/{client-SvBdgObD.mjs → client-CuUo8Nvg.mjs} +381 -49
  26. package/dist/collector-run.d.mts +6 -0
  27. package/dist/collector-run.mjs +9 -9
  28. package/dist/{command-output-CDAIFD-8.mjs → command-output-Bt8ygHE7.mjs} +2 -2
  29. package/dist/{config-D0DbpZtM.mjs → config-DC5jEtd1.mjs} +9 -5
  30. package/dist/{created-devices-CpvzBL25.mjs → created-devices-D0s26Hx5.mjs} +2 -2
  31. package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-DKaS1OM9.mjs} +2 -2
  32. package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-pB_IPfV4.mjs} +1 -1
  33. package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CDY_zAmD.mjs} +2 -2
  34. package/dist/{device-C8IOoJPv.mjs → device-BHw0WlHl.mjs} +8 -8
  35. package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-BoN1jQkO.mjs} +25 -14
  36. package/dist/device-host-worker.mjs +19 -19
  37. package/dist/{gradle-dJpre3Yn.mjs → device-ios-B2ruohE-.mjs} +661 -107
  38. package/dist/{device-lease-DpoS36_u.mjs → device-lease-Cu7PtQpZ.mjs} +9 -9
  39. package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-D38SnZtt.mjs} +4 -4
  40. package/dist/{device-pool-3o7s5lo4.mjs → device-pool-CP4rAsHx.mjs} +3 -3
  41. package/dist/{device-remote-hFSkpfQu.mjs → device-remote-B5GiUifk.mjs} +15 -16
  42. package/dist/{doctor-CFv1fCrF.mjs → doctor-PRJH94v6.mjs} +61 -34
  43. package/dist/doctor-watchman-wZhyTdp4.mjs +259 -0
  44. package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics--v5HGOU0.mjs} +11 -11
  45. package/dist/{exec-CaErVv7t.mjs → exec-i8GDPwq9.mjs} +93 -6
  46. package/dist/{gc-DEmkg3Xh.mjs → gc-CyHMcKcn.mjs} +25 -471
  47. package/dist/{guide-CSmhMTf8.mjs → guide-dRaQJJk9.mjs} +681 -507
  48. package/dist/{hosted-android-E1Heoqnv.mjs → hosted-android-Bs7fytj7.mjs} +3 -3
  49. package/dist/{hosted-client-Bp4knSwu.mjs → hosted-client-C5G4h_OJ.mjs} +2 -2
  50. package/dist/{hosted-ios-BnbeYOUD.mjs → hosted-ios-Bik035eL.mjs} +1 -1
  51. package/dist/{hosted-logs-OcgIBvCT.mjs → hosted-logs-CI9Az9a4.mjs} +9 -9
  52. package/dist/{hosted-macos-DcxKjtfH.mjs → hosted-macos-DABm3ScO.mjs} +5 -5
  53. package/dist/{hosted-native-1Ebbig4w.mjs → hosted-native-DbV2aUVw.mjs} +13 -9
  54. package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-DYiMBn34.mjs} +7 -8
  55. package/dist/{ios-DSfnPfse.mjs → ios-BWbHFZVz.mjs} +19 -7
  56. package/dist/{ios-Cr1h5gnO.mjs → ios-DCrkgXEM.mjs} +1 -1
  57. package/dist/ios-DYUr9LGc.mjs +8 -0
  58. package/dist/ios-ITYno9rO.mjs +2241 -0
  59. package/dist/{ios-device-pAN4t54-.mjs → ios-device-CJ_ZOGky.mjs} +3 -3
  60. package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-CrW8aMb5.mjs} +1 -1
  61. package/dist/{ios-state-8L3lgum6.mjs → ios-state-By0XuLTQ.mjs} +1 -1
  62. package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-DCveSE6d.mjs} +5 -5
  63. package/dist/{logs-BAaS7gS2.mjs → logs-Bao1HOMy.mjs} +13 -13
  64. package/dist/{macos-BIRQS90f.mjs → macos-ciyBdTY5.mjs} +194 -132
  65. package/dist/macos-nIOATJmR.mjs +2 -0
  66. package/dist/macos-run.mjs +1 -1
  67. package/dist/maintenance-run.mjs +349 -47
  68. package/dist/manifest-CX0d1IkZ.mjs +148 -0
  69. package/dist/{metro-g8NqiJp-.mjs → metro-DI2AjPF_.mjs} +196 -43
  70. package/dist/{metro-gateway-C-ijGI2F.mjs → metro-gateway-BxpJLIwh.mjs} +4 -4
  71. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
  72. package/dist/{build-plan-BDsSi09V.mjs → miss-reason-CjQCOKqq.mjs} +207 -249
  73. package/dist/{named-ports-C8m8EQs2.mjs → named-ports-B4iWH4E-.mjs} +70 -8
  74. package/dist/{native-runtime-BEK171xQ.mjs → native-runtime-BGxsbfny.mjs} +6 -6
  75. package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
  76. package/dist/offload-worker.d.mts +3 -45
  77. package/dist/offload-worker.mjs +11 -11
  78. package/dist/{ownership-DyRQru0q.mjs → ownership-EB0ySdGK.mjs} +3 -3
  79. package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
  80. package/dist/placement-log-Dxe34rJm.mjs +88 -0
  81. package/dist/plan-placement-BVY59qa8.mjs +290 -0
  82. package/dist/{ports-4nYqxPWC.mjs → ports-ByPc0_97.mjs} +3 -3
  83. package/dist/{prebuild-D7RUMkwN.mjs → prebuild-BKJ3wJI1.mjs} +5 -5
  84. package/dist/preview-BbSottmO.mjs +986 -0
  85. package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
  86. package/dist/project-B5eQ1USs.mjs +612 -0
  87. package/dist/{pull-request-pxnDCQzn.mjs → pull-request-IHNdMnG8.mjs} +1 -1
  88. package/dist/pull-requests.mjs +1 -1
  89. package/dist/react-native-ios-BMh2i25I.mjs +2 -0
  90. package/dist/react-native-ios-CSpIxexZ.mjs +2083 -0
  91. package/dist/{recordings-BNgsD1Tr.mjs → recordings-Bv8McZN4.mjs} +1 -1
  92. package/dist/{reload-DcRxj3qG.mjs → reload-rAPdqeRR.mjs} +14 -15
  93. package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-N3hAEaAL.mjs} +5 -5
  94. package/dist/{run-CkE9y0nc.mjs → run-lAkle1-c.mjs} +5 -5
  95. package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-CkIjnegq.mjs} +5 -6
  96. package/dist/server-expo-DGsdUGX2.mjs +2 -0
  97. package/dist/{server-expo-CHTyXD0G.mjs → server-expo-n3tUi3Em.mjs} +5 -6
  98. package/dist/{settings-SR9OmW9h.mjs → settings-2am7TSxp.mjs} +5 -5
  99. package/dist/{settings-Cfd-0r2d.mjs → settings-CxgngUZk.mjs} +116 -103
  100. package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
  101. package/dist/settings.schema.json +198 -9
  102. package/dist/{simslim-BWNrc3M3.mjs → simslim-D9LkJxIO.mjs} +7 -7
  103. package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-HV_KtPIw.mjs} +6 -6
  104. package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
  105. package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
  106. package/dist/{stage-CzaiwFsI.mjs → stage-BgaZJVZY.mjs} +14 -3
  107. package/dist/{start-AlfCPRVU.mjs → start-D8NbxNBb.mjs} +1 -1
  108. package/dist/{start-DUz3mYdb.mjs → start-DtxthMfI.mjs} +34 -27
  109. package/dist/{state-PeQiJuIu.mjs → state-D2KtnALG.mjs} +4 -4
  110. package/dist/{state-DyaNTgfR.mjs → state-Tp4uImDS.mjs} +5 -3
  111. package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
  112. package/dist/{stats-Bq4fJOdB.mjs → stats-8EJ5ivFT.mjs} +6 -6
  113. package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
  114. package/dist/{status-BA2pehwf.mjs → status-BOyp0CUl.mjs} +31 -34
  115. package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-C65jk_q2.mjs} +1 -1
  116. package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-DbmUjsLv.mjs} +1 -1
  117. package/dist/stop-D0UIpZuh.mjs +72 -0
  118. package/dist/{stop-zF1y04f2.mjs → stop-DRipAg-e.mjs} +2 -2
  119. package/dist/{stop-DQIasgKp.mjs → stop-zDlknFoz.mjs} +79 -33
  120. package/dist/supervisor-run.d.mts +24 -22
  121. package/dist/supervisor-run.mjs +21 -22
  122. package/dist/{support-CsJNVfOc.mjs → support-CNqYkV0y.mjs} +7 -7
  123. package/dist/{support-WvD__tSV.mjs → support-DsNaFg6D.mjs} +5 -5
  124. package/dist/swiftpm-macos-aJzPAj1t.mjs +119 -0
  125. package/dist/{toolchain-DNyR3NPq.mjs → toolchain-C3xSoyUl.mjs} +475 -269
  126. package/dist/{trigger-Bemu8ytw.mjs → trigger-CAhmG-4s.mjs} +6 -6
  127. package/dist/trigger-CoalsMTV.mjs +2 -0
  128. package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-pFLB2e6P.mjs} +135 -38
  129. package/dist/{status-CzS4bPO9.mjs → watchman-BL1IdOtq.mjs} +75 -7
  130. package/dist/web-DFpFeZu5.mjs +2 -0
  131. package/dist/web-DbbrPUn0.mjs +127 -0
  132. package/dist/web-run.mjs +10 -10
  133. package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-CR9X86xJ.mjs} +3 -3
  134. package/dist/{ownership-BMLqyBgJ.mjs → workspaces-Djb2gfi9.mjs} +661 -23
  135. package/dist/{worktree-Be_7NLKy.mjs → worktree-BL4M1rhB.mjs} +252 -50
  136. package/dist/{worktree-j3IcFAI5.mjs → worktree-DV-A0LNL.mjs} +1 -1
  137. package/package.json +10 -5
  138. package/shim/bundle-response.cjs +2 -1
  139. package/shim/bundle-response.d.cts +1 -1
  140. package/shim/expo-metro-config.cjs +1 -1
  141. package/dist/build-progress-D0WkjCPd.mjs +0 -1036
  142. package/dist/build-slots-41v_d2x9.mjs +0 -142
  143. package/dist/cli-CYXb29oE.mjs +0 -51
  144. package/dist/deps-iKw0eD4P.mjs +0 -438
  145. package/dist/device-ios-C2Um-XUR.mjs +0 -412
  146. package/dist/errors-B6W7FkxQ.mjs +0 -15
  147. package/dist/guide-status-Du-ZQ3FD.mjs +0 -125
  148. package/dist/idle-BXG__1BA.mjs +0 -176
  149. package/dist/in-use-CvGOUn-x.mjs +0 -130
  150. package/dist/ios-BJ4XUSe1.mjs +0 -3867
  151. package/dist/native-run-D1Q56rAD.mjs +0 -158
  152. package/dist/preview-B-g9d7kV.mjs +0 -318
  153. package/dist/process-identity-BmRm1qV0.mjs +0 -12
  154. package/dist/project-D47Rh0ly.mjs +0 -276
  155. package/dist/server-expo-CeakhI8P.mjs +0 -2
  156. package/dist/state-BKa8wNN5.mjs +0 -148
  157. package/dist/stop-BIiM49EY.mjs +0 -48
  158. package/dist/stop-cause-rhmGfwvt.mjs +0 -97
  159. package/dist/trigger-CXts6qt1.mjs +0 -2
  160. package/dist/watchman-APYjXSQa.mjs +0 -48
  161. package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
  162. package/dist/workspaces-Bcib97-6.mjs +0 -306
@@ -1,145 +1,18 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { r as register } from "./cache-manifest-DnUHBJaU.mjs";
3
- import { t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
4
- import { a as formatElapsed, l as phaseLine } from "./command-output-CDAIFD-8.mjs";
5
- import { Z as resolveOptimizations } from "./settings-Cfd-0r2d.mjs";
6
- import { m as resolvePackageJson } from "./project-D47Rh0ly.mjs";
7
- import { _ as workspaceDerivedData, f as sharedCcache$1, p as sharedCompilationCache$1, x as workspaceLogsDir$1 } from "./workspace-state-DqcCbpE7.mjs";
8
- import { a as spawnDeclared } from "./spawn-claims-D0XfSe8u.mjs";
9
- import { o as androidHome } from "./android-B_YNZy61.mjs";
10
- import { t as cleanLine } from "./server-expo-CHTyXD0G.mjs";
1
+ import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
2
+ import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
3
+ import { a as formatElapsed, l as phaseLine } from "./command-output-Bt8ygHE7.mjs";
4
+ import { _ as workspaceDerivedData } from "./workspace-state-CR9X86xJ.mjs";
5
+ import { Ot as resolveOptimizations } from "./settings-CxgngUZk.mjs";
6
+ import { a as spawnDeclared } from "./spawn-claims-Kqiifsk2.mjs";
7
+ import { o as androidHome } from "./android-BdQC6zs1.mjs";
8
+ import { t as cleanLine } from "./server-expo-n3tUi3Em.mjs";
11
9
  import { createRequire } from "node:module";
12
- import { createHash } from "node:crypto";
13
10
  import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
14
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
11
+ import { basename, delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
15
12
  import { stimBuildDigest } from "@stim-cli/core/state";
16
13
  import { fileURLToPath } from "node:url";
17
- import chalk from "chalk";
18
- const CCACHE_SLOPPINESS = "pch_defines,time_macros";
19
- const STATS_LOG_FILE = "ccache-stats.log";
20
- const CCACHE_UNAVAILABLE = {
21
- status: "unavailable",
22
- hits: null,
23
- misses: null,
24
- hitRatePercent: null
25
- };
26
- const CCACHE_NOT_RUN = {
27
- status: "not-run",
28
- hits: null,
29
- misses: null,
30
- hitRatePercent: null
31
- };
32
- function ccacheStatsLog(root) {
33
- return join(workspaceLogsDir$1(root), STATS_LOG_FILE);
34
- }
35
- function ccacheEnvironment({ binary, dir, workspaceRoot, statsLog }) {
36
- return {
37
- CMAKE_C_COMPILER_LAUNCHER: binary,
38
- CMAKE_CXX_COMPILER_LAUNCHER: binary,
39
- CCACHE_DIR: dir,
40
- CCACHE_BASEDIR: workspaceRoot,
41
- CCACHE_NOHASHDIR: "true",
42
- CCACHE_SLOPPINESS,
43
- CCACHE_MAXSIZE: "5G",
44
- CCACHE_STATSLOG: statsLog
45
- };
46
- }
47
- function parseCcacheBinary(output) {
48
- const first = String(output ?? "").split("\n")[0]?.trim();
49
- if (!first || !isAbsolute(first)) return null;
50
- return first;
51
- }
52
- const CMAKE_LAUNCHER = /CMAKE_(C|CXX)_COMPILER_LAUNCHER\b/;
53
- function projectCmakeLauncher(source) {
54
- if (typeof source !== "string") return null;
55
- const match = CMAKE_LAUNCHER.exec(source);
56
- return match ? `CMAKE_${match[1]}_COMPILER_LAUNCHER` : null;
57
- }
58
- const HIT_COUNTERS = /* @__PURE__ */ new Set(["direct_cache_hit", "preprocessed_cache_hit"]);
59
- function parseCcacheStatsLog(text) {
60
- if (typeof text !== "string") return null;
61
- let hits = 0;
62
- let misses = 0;
63
- for (const raw of text.split("\n")) {
64
- const line = raw.trim();
65
- if (HIT_COUNTERS.has(line)) hits += 1;
66
- else if (line === "cache_miss") misses += 1;
67
- }
68
- const total = hits + misses;
69
- if (total === 0) return null;
70
- return {
71
- status: "reported",
72
- hits,
73
- misses,
74
- hitRatePercent: Math.round(hits / total * 1e3) / 10
75
- };
76
- }
77
- function readCcacheActivity(statsLog) {
78
- let text;
79
- try {
80
- text = readFileSync(statsLog, "utf-8");
81
- } catch {
82
- return CCACHE_UNAVAILABLE;
83
- }
84
- return parseCcacheStatsLog(text) ?? CCACHE_UNAVAILABLE;
85
- }
86
- function ccacheActivityLine(activity) {
87
- if (activity.status === "reported") return `${activity.hits} hits / ${activity.misses} misses (${activity.hitRatePercent}%)`;
88
- if (activity.status === "not-run") return "not run; artifact cache supplied the app";
89
- return "unavailable; no C++ compile went through ccache";
90
- }
91
- function lookupCcache() {
92
- try {
93
- return parseCcacheBinary(getExecutor().findExecutable("ccache"));
94
- } catch {
95
- return null;
96
- }
97
- }
98
- function readOrNull(file) {
99
- try {
100
- return readFileSync(file, "utf-8");
101
- } catch {
102
- return null;
103
- }
104
- }
105
- function canonical(root) {
106
- try {
107
- return realpathSync(root);
108
- } catch {
109
- return root;
110
- }
111
- }
112
- function resolveCcache({ root, dir = sharedCcache$1(), lookup = lookupCcache, onNote = (line) => console.error(line) }) {
113
- const binary = lookup();
114
- if (!binary) {
115
- onNote(chalk.dim(phaseLine("cache", "ccache off (not installed; brew install ccache)")));
116
- return null;
117
- }
118
- const declared = projectCmakeLauncher(readOrNull(join(root, "android", "app", "build.gradle")));
119
- if (declared) {
120
- onNote(chalk.dim(phaseLine("cache", `ccache off (android/app/build.gradle sets ${declared} itself)`)));
121
- return null;
122
- }
123
- const statsLog = ccacheStatsLog(root);
124
- register({
125
- dir,
126
- name: "ccache",
127
- prune: "atomic",
128
- note: "ccache keeps itself under CCACHE_MAXSIZE, so it is emptied whole or not at all"
129
- });
130
- onNote(chalk.dim(phaseLine("cache", `ccache on (${dir})`)));
131
- return {
132
- dir,
133
- statsLog,
134
- env: ccacheEnvironment({
135
- binary,
136
- dir,
137
- workspaceRoot: canonical(root),
138
- statsLog
139
- })
140
- };
141
- }
142
- //#endregion
14
+ import { homedir } from "node:os";
15
+ import { createHash } from "node:crypto";
143
16
  //#region src/engine/bundler.ts
144
17
  const COCOAPODS_SPEC = /^ {4}cocoapods \(/m;
145
18
  function bundlerPin(root) {
@@ -300,15 +173,6 @@ function describeDiagnostic(diagnostic) {
300
173
  }
301
174
  //#endregion
302
175
  //#region src/engine/xcode.ts
303
- const IOS_DIR = "ios";
304
- const PREBUILD_REMEDY = "Generate it with `npx expo prebuild -p ios` (stim ios does this automatically for an Expo project with no ios/ directory), or commit the native project.";
305
- function buildFailure(message, remedy) {
306
- return { error: {
307
- code: "STIM_BUILD_FAILED",
308
- message,
309
- remedy
310
- } };
311
- }
312
176
  function pickXcodeProject(entries) {
313
177
  const names = (Array.isArray(entries) ? entries : []).filter((e) => typeof e === "string");
314
178
  const workspaces = names.filter((e) => e.endsWith(".xcworkspace")).toSorted();
@@ -336,22 +200,6 @@ function pickXcodeProject(entries) {
336
200
  }
337
201
  return null;
338
202
  }
339
- function discoverXcodeProject(root) {
340
- const dir = join(root, IOS_DIR);
341
- let entries;
342
- try {
343
- entries = readdirSync(dir);
344
- } catch {
345
- return buildFailure(`No ${IOS_DIR}/ directory in ${root}.`, PREBUILD_REMEDY);
346
- }
347
- const picked = pickXcodeProject(entries);
348
- if (!picked) return buildFailure(`${dir} contains no .xcworkspace and no .xcodeproj.`, PREBUILD_REMEDY);
349
- return {
350
- ...picked,
351
- dir,
352
- path: join(dir, picked.file)
353
- };
354
- }
355
203
  function parseSchemeList(text) {
356
204
  const empty = {
357
205
  name: null,
@@ -454,18 +302,6 @@ function compilationPrefixMappings(workspaceRoot, derivedDataPath) {
454
302
  if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) mappings.push(`${derived}=/^derived-data`);
455
303
  return mappings.map((mapping) => /[\s"\\]/.test(mapping) ? `"${mapping.replace(/["\\]/g, "\\$&")}"` : mapping).join(" ");
456
304
  }
457
- function ccacheEnabled(podfileProperties) {
458
- if (!podfileProperties || typeof podfileProperties !== "object") return false;
459
- return podfileProperties["apple.ccacheEnabled"] === "true";
460
- }
461
- function readPodfileProperties(root) {
462
- try {
463
- const parsed = JSON.parse(readFileSync(join(root, IOS_DIR, "Podfile.properties.json"), "utf-8"));
464
- return parsed && typeof parsed === "object" ? parsed : null;
465
- } catch {
466
- return null;
467
- }
468
- }
469
305
  const SWIFT_PREFIX_MAPPING_MIN_SWIFT = {
470
306
  major: 6,
471
307
  minor: 4
@@ -475,40 +311,12 @@ function swiftPrefixMappingSupported(version) {
475
311
  const min = SWIFT_PREFIX_MAPPING_MIN_SWIFT;
476
312
  return version.major > min.major || version.major === min.major && version.minor >= min.minor;
477
313
  }
478
- const SWIFT_CACHE_MIN_REACT_NATIVE = {
479
- major: 0,
480
- minor: 87
481
- };
482
- function reactNativeSupportsSwiftCache(version) {
483
- if (!version) return false;
484
- const min = SWIFT_CACHE_MIN_REACT_NATIVE;
485
- return version.major > min.major || version.major === min.major && version.minor >= min.minor;
486
- }
487
- function parseReactNativeVersion(packageJson) {
488
- if (!packageJson || typeof packageJson !== "object") return null;
489
- const raw = packageJson.version;
490
- const m = typeof raw === "string" ? /^(\d+)\.(\d+)\./.exec(raw) : null;
491
- if (!m || m[1] === void 0 || m[2] === void 0) return null;
492
- return {
493
- major: parseInt(m[1], 10),
494
- minor: parseInt(m[2], 10)
495
- };
496
- }
497
- function detectReactNativeVersion(root) {
498
- const file = resolvePackageJson(root, "react-native");
499
- if (!file) return null;
500
- try {
501
- return parseReactNativeVersion(JSON.parse(readFileSync(file, "utf-8")));
502
- } catch {
503
- return null;
504
- }
505
- }
506
- function compilationCacheSettings({ workspaceRoot, derivedDataPath, casPath, xcodeMajor, swiftVersion = null, reactNativeVersion = null, ccache = false, optimizations = resolveOptimizations({}, {}).ios }) {
314
+ function compilationCacheSettings({ workspaceRoot, derivedDataPath, casPath, xcodeMajor, swiftVersion = null, swiftCacheCompatible = false, ccache = false, optimizations = resolveOptimizations({}, {}).ios }) {
507
315
  if (xcodeMajor === null || xcodeMajor === void 0) return [];
508
316
  if (xcodeMajor < 26) return [];
509
317
  if (ccache) return [];
510
318
  const swiftMappable = swiftPrefixMappingSupported(swiftVersion);
511
- const auto = swiftMappable && reactNativeSupportsSwiftCache(reactNativeVersion);
319
+ const auto = swiftMappable && swiftCacheCompatible;
512
320
  const swift = optimizations.compilationCache && (optimizations.swiftCompilationCache ?? auto);
513
321
  const mappings = optimizations.prefixMapping ? compilationPrefixMappings(workspaceRoot, derivedDataPath) : "";
514
322
  return [
@@ -544,34 +352,6 @@ function parseXcodeMajor(output) {
544
352
  function detectXcodeMajor(exec = null) {
545
353
  return parseXcodeMajor((exec || getExecutor()).runQuiet("xcodebuild -version", { timeoutMs: 1e4 }));
546
354
  }
547
- function resolveCompilationCacheSettings({ root, derivedDataPath, exec = null, casPath = sharedCompilationCache$1(), optimizations = resolveOptimizations({}, {}).ios, onNote = (line) => console.error(line) }) {
548
- const xcodeMajor = detectXcodeMajor(exec);
549
- const ccache = ccacheEnabled(readPodfileProperties(root));
550
- const probe = xcodeMajor !== null && xcodeMajor >= 26 && !ccache && optimizations.compilationCache && optimizations.swiftCompilationCache !== false;
551
- const swiftVersion = probe ? detectSwiftVersion(exec) : null;
552
- const reactNativeVersion = probe ? detectReactNativeVersion(root) : null;
553
- const settings = compilationCacheSettings({
554
- workspaceRoot: root,
555
- derivedDataPath,
556
- casPath,
557
- xcodeMajor,
558
- swiftVersion,
559
- reactNativeVersion,
560
- ccache,
561
- optimizations
562
- });
563
- if (settings.length > 0 && optimizations.compilationCache) {
564
- register({
565
- dir: casPath,
566
- name: "Xcode compilation cache",
567
- prune: "atomic",
568
- note: "shared Xcode compilation cache"
569
- });
570
- const swift = settings.includes("SWIFT_ENABLE_COMPILE_CACHE=YES") ? settings.includes("SWIFT_ENABLE_PREFIX_MAPPING=YES") ? "Swift on" : "Swift on, unmapped" : swiftVersion && !swiftPrefixMappingSupported(swiftVersion) ? `Swift off, ${swiftVersion.major}.${swiftVersion.minor} < ${SWIFT_PREFIX_MAPPING_MIN_SWIFT.major}.${SWIFT_PREFIX_MAPPING_MIN_SWIFT.minor}` : swiftVersion && reactNativeVersion && !reactNativeSupportsSwiftCache(reactNativeVersion) ? `Swift off, react-native ${reactNativeVersion.major}.${reactNativeVersion.minor} < ${SWIFT_CACHE_MIN_REACT_NATIVE.major}.${SWIFT_CACHE_MIN_REACT_NATIVE.minor}` : "Swift off";
571
- onNote(chalk.dim(phaseLine("cache", `compilation cache on (CAS at ${casPath}, ${swift})`)));
572
- }
573
- return settings;
574
- }
575
355
  function xcodebuildArgs({ project, scheme, udid = null, destination = null, configuration = "Debug", sdk = "iphonesimulator", derivedDataPath, extraArgs = [], buildSettings = [] }) {
576
356
  return [
577
357
  project.flag,
@@ -774,13 +554,13 @@ function compilationCacheActivityLine(activity) {
774
554
  if (activity.status === "not-run") return "not run; artifact cache supplied the app";
775
555
  return "unavailable; Xcode did not report reliable statistics";
776
556
  }
777
- async function buildIos({ root, udid = null, logWriter, project = null, scheme = null, configuration = "Debug", sdk = "iphonesimulator", destination = null, arch = null, derivedDataPath = null, extraArgs = [], compilationCache = void 0, optimizations = resolveOptimizations({}, {}).ios, now = () => Date.now(), exec = null, heartbeatMs = HEARTBEAT_INTERVAL_MS, estimateMs = null, onHeartbeat = (line) => console.error(line), onNote = (line) => console.error(line) }) {
778
- if (!root || typeof root !== "string") throw new TypeError("buildIos requires {root}");
779
- if (!logWriter || typeof logWriter.write !== "function") throw new TypeError("buildIos requires {logWriter} with a write() method");
780
- if (!udid && !destination) throw new TypeError("buildIos requires {udid} (or an explicit {destination})");
557
+ async function buildXcode({ root, udid = null, logWriter, project, scheme = null, configuration = "Debug", sdk = "iphonesimulator", destination = null, arch = null, derivedDataPath = null, extraArgs = [], compilationCache, startedAt: clockStart, now = () => Date.now(), exec = null, heartbeatMs = HEARTBEAT_INTERVAL_MS, estimateMs = null, onHeartbeat = (line) => console.error(line) }) {
558
+ if (!root || typeof root !== "string") throw new TypeError("buildXcode requires {root}");
559
+ if (!logWriter || typeof logWriter.write !== "function") throw new TypeError("buildXcode requires {logWriter} with a write() method");
560
+ if (!udid && !destination) throw new TypeError("buildXcode requires {udid} (or an explicit {destination})");
781
561
  const executor = exec || getExecutor();
782
562
  const dd = derivedDataPath || (scheme ? join(workspaceDerivedData(root), `scheme-${createHash("sha256").update(scheme).digest("hex")}`) : workspaceDerivedData(root));
783
- const startedAt = now();
563
+ const startedAt = clockStart ?? now();
784
564
  const elapsed = () => now() - startedAt;
785
565
  const reportError = (message, remedy) => {
786
566
  logWriter.write({
@@ -791,23 +571,18 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
791
571
  ...remedy ? { remedy } : {}
792
572
  });
793
573
  };
794
- let target = project;
795
- if (!target) {
796
- const discovered = discoverXcodeProject(root);
797
- if (discovered.error) {
798
- reportError(discovered.error.message, discovered.error.remedy);
799
- return failedResult({
800
- code: discovered.error.code,
801
- diagnostics: [{
802
- message: discovered.error.message,
803
- remedy: discovered.error.remedy ?? void 0
804
- }],
805
- durationMs: elapsed()
806
- });
807
- }
808
- target = discovered;
574
+ if (project.error) {
575
+ reportError(project.error.message, project.error.remedy);
576
+ return failedResult({
577
+ code: project.error.code,
578
+ diagnostics: [{
579
+ message: project.error.message,
580
+ remedy: project.error.remedy ?? void 0
581
+ }],
582
+ durationMs: elapsed()
583
+ });
809
584
  }
810
- const resolvedTarget = target;
585
+ const resolvedTarget = project;
811
586
  const selected = resolveScheme(resolvedTarget, {
812
587
  exec: executor,
813
588
  scheme
@@ -824,12 +599,9 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
824
599
  });
825
600
  }
826
601
  const buildScheme = selected.scheme;
827
- const cacheSettings = compilationCache === void 0 ? resolveCompilationCacheSettings({
828
- root,
602
+ const cacheSettings = typeof compilationCache === "function" ? compilationCache({
829
603
  derivedDataPath: dd,
830
- exec: executor,
831
- onNote,
832
- optimizations
604
+ exec: executor
833
605
  }) : compilationCache || [];
834
606
  const buildSettings = arch ? [
835
607
  ...cacheSettings,
@@ -1075,11 +847,436 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
1075
847
  };
1076
848
  }
1077
849
  //#endregion
1078
- //#region src/offload/manifest.ts
1079
- function manifestDigest(entries) {
1080
- const hash = createHash("sha256");
1081
- for (const entry of entries.toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) hash.update(`${entry.path}\0${entry.kind}\0${entry.sha256}\n`);
1082
- return hash.digest("hex");
850
+ //#region src/engine/deps.ts
851
+ const DEPS_ERROR = "STIM_DEPS_FAILED";
852
+ const TRANSCRIPT_LINES = 2e3;
853
+ const MAX_POD_DIAGNOSTIC_LINES = 15;
854
+ const POD_MARKER = /^\[!\]/;
855
+ const POD_CONTINUATION = /^\s+\S/;
856
+ const RUBY_HEAD = /^\S.*\.rb:\d+:in\s+(?:`[^']*'|'[^']*'):\s*\S/;
857
+ const RUBY_FRAME = /^\s*from\s+\S/;
858
+ const RUBY_CONTEXT_BEFORE = 4;
859
+ const BUNDLER_FROZEN = /frozen mode|lockfile can't be updated|in deployment mode after changing/i;
860
+ const BUNDLER_NO_COCOAPODS = /cocoapods is not currently included in the bundle|command not found: pod\b/i;
861
+ function extractPodDiagnostics(transcript) {
862
+ if (typeof transcript !== "string" || transcript === "") return null;
863
+ const lines = transcript.split("\n").map((line) => line.replace(/\r$/, ""));
864
+ return extractPodBangBlocks(lines) || extractRubyHead(lines);
865
+ }
866
+ function extractPodBangBlocks(lines) {
867
+ const blocks = [];
868
+ for (let i = 0; i < lines.length; i += 1) {
869
+ const line = lines[i];
870
+ if (line === void 0 || !POD_MARKER.test(line)) continue;
871
+ const block = [line];
872
+ let j = i + 1;
873
+ for (; j < lines.length; j += 1) {
874
+ const next = lines[j];
875
+ if (next === void 0 || !POD_CONTINUATION.test(next)) break;
876
+ block.push(next);
877
+ }
878
+ blocks.push(block);
879
+ i = j - 1;
880
+ }
881
+ if (blocks.length === 0) return null;
882
+ const out = [];
883
+ let dropped = 0;
884
+ for (const block of blocks) {
885
+ const room = MAX_POD_DIAGNOSTIC_LINES - out.length;
886
+ if (room <= 0) {
887
+ dropped += block.length;
888
+ continue;
889
+ }
890
+ out.push(...block.slice(0, room));
891
+ dropped += Math.max(0, block.length - room);
892
+ }
893
+ if (dropped > 0) out.push(`(+${dropped} more [!] lines in the build log)`);
894
+ return {
895
+ source: "cocoapods",
896
+ lines: out
897
+ };
898
+ }
899
+ function extractRubyHead(lines) {
900
+ for (let i = 0; i < lines.length; i += 1) {
901
+ const line = lines[i];
902
+ if (line === void 0 || !RUBY_HEAD.test(line) || RUBY_FRAME.test(line)) continue;
903
+ let start = i;
904
+ while (start > 0 && i - start < RUBY_CONTEXT_BEFORE) {
905
+ const prev = lines[start - 1];
906
+ if (prev === void 0 || prev.trim() === "" || RUBY_FRAME.test(prev) || POD_MARKER.test(prev)) break;
907
+ start -= 1;
908
+ }
909
+ let end = i + 1;
910
+ while (end < lines.length) {
911
+ const next = lines[end];
912
+ if (next === void 0 || next.trim() === "" || RUBY_FRAME.test(next)) break;
913
+ end += 1;
914
+ }
915
+ return {
916
+ source: "ruby",
917
+ lines: lines.slice(start, end).filter((entry) => entry.trim() !== "").slice(0, MAX_POD_DIAGNOSTIC_LINES)
918
+ };
919
+ }
920
+ return null;
921
+ }
922
+ function podsAreStale(lockText, manifestText) {
923
+ const lock = normalize(lockText);
924
+ const manifest = normalize(manifestText);
925
+ if (lock === null && manifest === null) return {
926
+ noPods: true,
927
+ stale: false
928
+ };
929
+ if (lock === null) return {
930
+ stale: true,
931
+ reason: "ios/Podfile.lock is missing but ios/Pods exists"
932
+ };
933
+ if (manifest === null) return {
934
+ stale: true,
935
+ reason: "ios/Pods/Manifest.lock is missing (pods have never been installed here)"
936
+ };
937
+ if (lock !== manifest) return {
938
+ stale: true,
939
+ reason: "ios/Podfile.lock and ios/Pods/Manifest.lock differ"
940
+ };
941
+ return { stale: false };
942
+ }
943
+ function normalize(text) {
944
+ if (typeof text !== "string") return null;
945
+ return text.replace(/\r\n/g, "\n").trimEnd();
946
+ }
947
+ function podfilePath(root) {
948
+ return join(root, "ios", "Podfile");
949
+ }
950
+ function readPodState(root) {
951
+ return {
952
+ hasPodfile: existsSync(podfilePath(root)),
953
+ lockText: readOrNull(join(root, "ios", "Podfile.lock")),
954
+ manifestText: readOrNull(join(root, "ios", "Pods", "Manifest.lock"))
955
+ };
956
+ }
957
+ function readOrNull(file) {
958
+ try {
959
+ return readFileSync(file, "utf-8");
960
+ } catch {
961
+ return null;
962
+ }
963
+ }
964
+ function podEnv(root, options = {}) {
965
+ return podEnvForRuby(readRubyVersion(root), options);
966
+ }
967
+ function podEnvForRuby(version, { env = process.env, home = homedir(), exists = existsSync } = {}) {
968
+ const out = {
969
+ ...env,
970
+ FORCE_COLOR: "0",
971
+ CLICOLOR: "0",
972
+ LANG: env.LANG ?? "en_US.UTF-8",
973
+ LC_ALL: env.LC_ALL ?? env.LANG ?? "en_US.UTF-8"
974
+ };
975
+ if (!version) return out;
976
+ const candidates = [
977
+ { bin: join(home, ".rbenv", "versions", version, "bin") },
978
+ {
979
+ bin: join(home, ".rvm", "rubies", `ruby-${version}`, "bin"),
980
+ gems: join(home, ".rvm", "gems", `ruby-${version}`)
981
+ },
982
+ { bin: join(home, ".asdf", "installs", "ruby", version, "bin") },
983
+ { bin: join(home, ".local", "share", "mise", "installs", "ruby", version, "bin") }
984
+ ];
985
+ for (const c of candidates) {
986
+ if (!exists(c.bin)) continue;
987
+ out.PATH = `${c.bin}${delimiter}${out.PATH ?? ""}`;
988
+ if (c.gems && exists(c.gems)) {
989
+ out.GEM_HOME = c.gems;
990
+ out.GEM_PATH = c.gems;
991
+ }
992
+ break;
993
+ }
994
+ return out;
995
+ }
996
+ function readRubyVersion(root, { read = readFileSync } = {}) {
997
+ try {
998
+ const first = String(read(join(root, ".ruby-version"), "utf-8")).split("\n").map((l) => l.trim()).find((l) => l && !l.startsWith("#"));
999
+ if (!first) return null;
1000
+ return first.replace(/^ruby-/, "");
1001
+ } catch {
1002
+ return null;
1003
+ }
1004
+ }
1005
+ function bundlerEnv(root, gemfile) {
1006
+ return {
1007
+ ...podEnv(root),
1008
+ BUNDLE_GEMFILE: gemfile,
1009
+ BUNDLE_FROZEN: "true"
1010
+ };
1011
+ }
1012
+ const BUNDLE_PATH_SETTING = /^BUNDLE_PATH:[ \t]*["']?([^"'\r\n]+?)["']?[ \t]*$/m;
1013
+ function bundlePathInsideProject(root, env) {
1014
+ const fromEnv = env.BUNDLE_PATH;
1015
+ const configured = fromEnv || BUNDLE_PATH_SETTING.exec(readOrNull(join(root, ".bundle", "config")) ?? "")?.[1];
1016
+ if (!configured || configured.startsWith("~")) return null;
1017
+ const resolved = resolve(root, configured);
1018
+ if (resolved !== root && !resolved.startsWith(root + sep)) return null;
1019
+ return {
1020
+ path: relative(root, resolved) || ".",
1021
+ where: fromEnv ? "where the BUNDLE_PATH environment variable points" : "where its own .bundle/config points BUNDLE_PATH"
1022
+ };
1023
+ }
1024
+ async function runCaptured(ctx) {
1025
+ const startedAt = ctx.now();
1026
+ const transcript = [];
1027
+ const push = (line) => {
1028
+ const msg = stripAnsi(String(line)).trimEnd();
1029
+ if (!msg.trim()) return;
1030
+ transcript.push(msg);
1031
+ if (transcript.length > TRANSCRIPT_LINES) transcript.splice(TRANSCRIPT_LINES / 2, 1);
1032
+ ctx.logWriter?.write?.({
1033
+ src: "build",
1034
+ level: "debug",
1035
+ msg,
1036
+ raw: true,
1037
+ event: ctx.event
1038
+ });
1039
+ };
1040
+ let child;
1041
+ try {
1042
+ child = spawnDeclared(() => ctx.spawn(ctx.cmd, ctx.args, {
1043
+ cwd: ctx.cwd,
1044
+ stdio: [
1045
+ "ignore",
1046
+ "pipe",
1047
+ "pipe"
1048
+ ],
1049
+ env: ctx.env
1050
+ }));
1051
+ } catch (error) {
1052
+ return {
1053
+ transcript,
1054
+ code: null,
1055
+ signal: null,
1056
+ error,
1057
+ durationMs: ctx.now() - startedAt
1058
+ };
1059
+ }
1060
+ const reader = {
1061
+ out: createLineReader(push),
1062
+ err: createLineReader(push)
1063
+ };
1064
+ child.stdout?.setEncoding?.("utf-8");
1065
+ child.stderr?.setEncoding?.("utf-8");
1066
+ child.stdout?.on("data", (chunk) => reader.out.push(chunk));
1067
+ child.stderr?.on("data", (chunk) => reader.err.push(chunk));
1068
+ const stopHeartbeat = ctx.label ? startBuildHeartbeat({
1069
+ intervalMs: ctx.heartbeatMs,
1070
+ elapsed: () => ctx.now() - startedAt,
1071
+ emit: ctx.onHeartbeat,
1072
+ label: ctx.label,
1073
+ estimateMs: ctx.estimateMs ?? null
1074
+ }) : () => {};
1075
+ let result;
1076
+ try {
1077
+ result = await waitForChild(child);
1078
+ } finally {
1079
+ stopHeartbeat();
1080
+ }
1081
+ reader.out.flush();
1082
+ reader.err.flush();
1083
+ return {
1084
+ transcript,
1085
+ code: result.code ?? null,
1086
+ signal: result.signal ?? null,
1087
+ error: result.error ?? null,
1088
+ durationMs: ctx.now() - startedAt
1089
+ };
1090
+ }
1091
+ async function ensureBundledGems(root, pin, ctx) {
1092
+ const env = bundlerEnv(root, pin.gemfile);
1093
+ const cwd = dirname(pin.gemfile);
1094
+ const check = await runCaptured({
1095
+ ...ctx,
1096
+ cmd: "bundle",
1097
+ args: ["check", "--dry-run"],
1098
+ cwd,
1099
+ env,
1100
+ event: "bundle_check",
1101
+ label: "gems"
1102
+ });
1103
+ if (check.error) return bundlerSpawnFailure("bundle check", check, pin);
1104
+ if (check.code === 0) return { bundler: true };
1105
+ const install = await runCaptured({
1106
+ ...ctx,
1107
+ cmd: "bundle",
1108
+ args: ["install"],
1109
+ cwd,
1110
+ env,
1111
+ event: "bundle_install",
1112
+ label: "gems"
1113
+ });
1114
+ if (install.error) return bundlerSpawnFailure("bundle install", install, pin);
1115
+ const inProject = bundlePathInsideProject(cwd, env);
1116
+ const note = inProject ? `\`bundle install\` put this project's gems in ${inProject.path}${sep}, ${inProject.where}; Gemfile.lock itself is never written.` : void 0;
1117
+ if (install.code === 0) return {
1118
+ bundler: true,
1119
+ note
1120
+ };
1121
+ const how = install.signal ? `signal ${install.signal}` : `exit code ${install.code}`;
1122
+ const transcript = install.transcript.join("\n");
1123
+ const extracted = extractPodDiagnostics(transcript);
1124
+ return {
1125
+ bundler: false,
1126
+ failure: {
1127
+ failed: true,
1128
+ code: DEPS_ERROR,
1129
+ reason: `\`bundle install\` failed (${how}).`,
1130
+ remedy: frozenRemedy(root, pin, transcript) || gemInstallRemedy(root, pin),
1131
+ diagnosticSource: extracted ? extracted.source : "tail",
1132
+ diagnosticLines: extracted ? extracted.lines : [],
1133
+ lastLines: install.transcript.slice(-20),
1134
+ durationMs: install.durationMs
1135
+ }
1136
+ };
1137
+ }
1138
+ function bundlerSpawnFailure(command, run, pin) {
1139
+ if (isMissingBinary(run.error)) return {
1140
+ bundler: false,
1141
+ note: missingBundlerNote(pin)
1142
+ };
1143
+ return {
1144
+ bundler: false,
1145
+ failure: {
1146
+ failed: true,
1147
+ code: DEPS_ERROR,
1148
+ reason: `Could not run \`${command}\`: ${run.error?.message || run.error}`,
1149
+ remedy: `Bundler is how this project pins its CocoaPods (${pin.lockfile}). Check that \`bundle\` runs in ${dirname(pin.gemfile)}, then run again.`,
1150
+ lastLines: run.transcript.slice(-20),
1151
+ durationMs: run.durationMs
1152
+ }
1153
+ };
1154
+ }
1155
+ function missingBundlerNote(pin) {
1156
+ return `${pin.lockfile} pins this project's CocoaPods, but \`bundle\` is not on PATH, so pods run as plain \`pod install\` with whatever CocoaPods PATH resolves to (\`gem install bundler\` to use the pin).`;
1157
+ }
1158
+ function frozenRemedy(root, pin, transcript) {
1159
+ if (!BUNDLER_FROZEN.test(transcript)) return void 0;
1160
+ return `Stim runs bundler with BUNDLE_FROZEN so it can never write into your checkout, and ${pin.gemfile} no longer matches ${pin.lockfile}. Run \`cd ${root} && bundle install\` yourself, keep the updated Gemfile.lock, then run again.`;
1161
+ }
1162
+ function gemInstallRemedy(root, pin) {
1163
+ const version = readRubyVersion(root);
1164
+ return `${version ? `This repo pins ruby ${version} (.ruby-version), so put that ruby on PATH (rbenv/rvm/asdf/mise) first. ` : ""}Bundler could not install the gems ${pin.lockfile} pins, so \`bundle exec pod install\` cannot run. Run \`cd ${root} && bundle install\` and fix what it reports, then run again.`;
1165
+ }
1166
+ async function runPodInstall(root, logWriter, { spawnFn = null, now = Date.now, heartbeatMs = HEARTBEAT_INTERVAL_MS, estimateMs = null, onHeartbeat = (line) => console.error(line) } = {}) {
1167
+ const iosDir = join(root, "ios");
1168
+ if (!existsSync(iosDir)) return {
1169
+ failed: true,
1170
+ code: DEPS_ERROR,
1171
+ reason: `No ios/ directory in ${root}, so there is nothing to pod install.`,
1172
+ remedy: "Run `stim ios` on a project with native iOS sources, or let prebuild generate them.",
1173
+ lastLines: []
1174
+ };
1175
+ const ctx = {
1176
+ logWriter,
1177
+ spawn: spawnFn || ((cmd, args, opts) => getExecutor().spawn(cmd, args, opts)),
1178
+ now,
1179
+ heartbeatMs,
1180
+ onHeartbeat
1181
+ };
1182
+ const notes = [];
1183
+ const pin = bundlerPin(root);
1184
+ let bundler = false;
1185
+ if (pin) {
1186
+ const gems = await ensureBundledGems(root, pin, ctx);
1187
+ if (gems.failure) return {
1188
+ ...gems.failure,
1189
+ notes
1190
+ };
1191
+ if (gems.note) notes.push(gems.note);
1192
+ bundler = gems.bundler;
1193
+ }
1194
+ const args = bundler ? [
1195
+ "bundle",
1196
+ "exec",
1197
+ "pod",
1198
+ "install"
1199
+ ] : ["pod", "install"];
1200
+ const command = args.join(" ");
1201
+ const run = await runCaptured({
1202
+ ...ctx,
1203
+ cmd: args[0],
1204
+ args: args.slice(1),
1205
+ cwd: iosDir,
1206
+ env: bundler && pin ? bundlerEnv(root, pin.gemfile) : podEnv(root),
1207
+ event: "pod_install",
1208
+ label: "pods",
1209
+ estimateMs
1210
+ });
1211
+ if (run.error) return {
1212
+ ...(bundler ? missingBundle(run.error, pin) : missingPod(run.error)) || {
1213
+ failed: true,
1214
+ code: "STIM_DEPS_FAILED",
1215
+ reason: `Could not run \`${command}\`: ${run.error?.message || run.error}`,
1216
+ lastLines: run.transcript.slice(-20),
1217
+ durationMs: run.durationMs
1218
+ },
1219
+ command,
1220
+ notes
1221
+ };
1222
+ if (run.code !== 0) {
1223
+ const how = run.signal ? `signal ${run.signal}` : `exit code ${run.code}`;
1224
+ const transcript = run.transcript.join("\n");
1225
+ const extracted = extractPodDiagnostics(transcript);
1226
+ return {
1227
+ failed: true,
1228
+ code: DEPS_ERROR,
1229
+ reason: `\`${command}\` failed (${how}).`,
1230
+ remedy: podFailureRemedy(root, pin, transcript),
1231
+ command,
1232
+ notes,
1233
+ diagnosticSource: extracted ? extracted.source : "tail",
1234
+ diagnosticLines: extracted ? extracted.lines : [],
1235
+ lastLines: run.transcript.slice(-20),
1236
+ durationMs: run.durationMs
1237
+ };
1238
+ }
1239
+ return {
1240
+ ok: true,
1241
+ command,
1242
+ notes,
1243
+ durationMs: run.durationMs
1244
+ };
1245
+ }
1246
+ function podFailureRemedy(root, pin, transcript) {
1247
+ if (pin) {
1248
+ const frozen = frozenRemedy(root, pin, transcript);
1249
+ if (frozen) return frozen;
1250
+ if (BUNDLER_NO_COCOAPODS.test(transcript)) return `${pin.lockfile} resolves cocoapods, but bundler found no \`pod\` executable in the bundle it loaded: the installed gems belong to a different Gemfile than ${pin.gemfile}, or that Gemfile's bundle was never installed. Run \`cd ${root} && bundle install && bundle exec pod --version\` and make that work, then run again.`;
1251
+ }
1252
+ const pinned = /Could not find proper version of cocoapods/.test(transcript) ? readRubyVersion(root) : null;
1253
+ if (!pinned) return void 0;
1254
+ return `This repo pins ruby ${pinned} (.ruby-version) and its Gemfile's cocoapods was installed under it; the shell's ruby is likely a different version, so bundler looks in the wrong gem home. Put ruby ${pinned} on PATH (rbenv/rvm/asdf/mise) -- \`bundle install\` will NOT fix this.`;
1255
+ }
1256
+ function isMissingBinary(err) {
1257
+ const nodeErr = err;
1258
+ const message = String(nodeErr?.message || err || "");
1259
+ return nodeErr?.code === "ENOENT" || /ENOENT|not found/i.test(message);
1260
+ }
1261
+ function missingBundle(err, pin) {
1262
+ if (!isMissingBinary(err)) return null;
1263
+ return {
1264
+ failed: true,
1265
+ code: DEPS_ERROR,
1266
+ reason: "Bundler is not installed: no `bundle` executable on PATH.",
1267
+ remedy: `${pin?.lockfile ?? "Gemfile.lock"} pins this project's CocoaPods, so pods run through bundler. Install bundler (\`gem install bundler\`) and run again.`,
1268
+ lastLines: []
1269
+ };
1270
+ }
1271
+ function missingPod(err) {
1272
+ if (!isMissingBinary(err)) return null;
1273
+ return {
1274
+ failed: true,
1275
+ code: DEPS_ERROR,
1276
+ reason: "CocoaPods is not installed: no `pod` executable on PATH.",
1277
+ remedy: "Install CocoaPods (`brew install cocoapods`, or `gem install cocoapods`) and run again.",
1278
+ lastLines: []
1279
+ };
1083
1280
  }
1084
1281
  //#endregion
1085
1282
  //#region src/offload/toolchain.ts
@@ -1087,7 +1284,10 @@ const distDir = dirname(fileURLToPath(import.meta.url));
1087
1284
  function quiet(file, args) {
1088
1285
  return getExecutor().runFileQuiet(file, args, { timeoutMs: 2e4 });
1089
1286
  }
1090
- function iosToolchain() {
1287
+ function iosToolchain(root) {
1288
+ return iosToolchainForRuby(readRubyVersion(root));
1289
+ }
1290
+ function iosToolchainForRuby(rubyVersion) {
1091
1291
  const xcode = quiet("xcodebuild", ["-version"]);
1092
1292
  return {
1093
1293
  stimBuild: stimBuildDigest(distDir),
@@ -1098,9 +1298,15 @@ function iosToolchain() {
1098
1298
  "iphonesimulator",
1099
1299
  "--show-sdk-version"
1100
1300
  ])?.trim() ?? null,
1101
- cocoapods: quiet("pod", ["--version"])?.trim().split("\n").pop()?.trim() ?? null
1301
+ cocoapods: cocoapodsVersion(rubyVersion)
1102
1302
  };
1103
1303
  }
1304
+ function cocoapodsVersion(rubyVersion) {
1305
+ return getExecutor().runFileQuiet("pod", ["--version"], {
1306
+ timeoutMs: 2e4,
1307
+ env: Object.fromEntries(Object.entries(podEnvForRuby(rubyVersion)).filter((entry) => entry[1] !== void 0))
1308
+ })?.trim().split("\n").pop()?.trim() ?? null;
1309
+ }
1104
1310
  function macosToolchain() {
1105
1311
  const xcode = quiet("xcodebuild", ["-version"]);
1106
1312
  return {
@@ -1190,7 +1396,7 @@ function sdkPackages() {
1190
1396
  platforms
1191
1397
  };
1192
1398
  }
1193
- function workerToolchain() {
1399
+ function workerToolchain(rubyVersion = null) {
1194
1400
  let listed = null;
1195
1401
  try {
1196
1402
  listed = JSON.parse(quiet("xcrun", [
@@ -1202,7 +1408,7 @@ function workerToolchain() {
1202
1408
  ]) ?? "null");
1203
1409
  } catch {}
1204
1410
  return {
1205
- ...iosToolchain(),
1411
+ ...iosToolchainForRuby(rubyVersion),
1206
1412
  macosSdk: quiet("xcrun", [
1207
1413
  "--sdk",
1208
1414
  "macosx",
@@ -1295,4 +1501,4 @@ function toolchainMismatches(target, worker) {
1295
1501
  return out;
1296
1502
  }
1297
1503
  //#endregion
1298
- export { podInstallCommand as C, projectCmakeLauncher as D, ccacheActivityLine as E, readCcacheActivity as O, bundlerPin as S, CCACHE_UNAVAILABLE as T, readBundleId as _, toolchainMismatches as a, tailLines as b, COMPILATION_CACHE_NOT_RUN as c, buildIos as d, ccacheEnabled as f, readBundleExecutable as g, discoverXcodeProject as h, macosToolchain as i, resolveCcache as k, COMPILATION_CACHE_UNAVAILABLE as l, detectXcodeMajor as m, androidToolchain as n, workerToolchain as o, compilationCacheActivityLine as p, iosToolchain as r, manifestDigest as s, androidRequirements as t, HEARTBEAT_INTERVAL_MS as u, resolveScheme as v, CCACHE_NOT_RUN as w, describeDiagnostic as x, startBuildHeartbeat as y };
1504
+ export { bundlerPin as A, readBundleExecutable as C, swiftPrefixMappingSupported as D, startBuildHeartbeat as E, tailLines as O, pickXcodeProject as S, resolveScheme as T, buildXcode as _, toolchainMismatches as a, detectSwiftVersion as b, podsAreStale as c, runCaptured as d, runPodInstall as f, SWIFT_PREFIX_MAPPING_MIN_SWIFT as g, HEARTBEAT_INTERVAL_MS as h, macosToolchain as i, podInstallCommand as j, describeDiagnostic as k, readPodState as l, COMPILATION_CACHE_UNAVAILABLE as m, androidToolchain as n, workerToolchain as o, COMPILATION_CACHE_NOT_RUN as p, iosToolchain as r, DEPS_ERROR as s, androidRequirements as t, readRubyVersion as u, compilationCacheActivityLine as v, readBundleId as w, detectXcodeMajor as x, compilationCacheSettings as y };