stim 1.17.2 → 1.18.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 (150) hide show
  1. package/README.md +50 -1
  2. package/dist/{activity-IRvJY_ru.mjs → activity-BxnkDZDi.mjs} +4 -4
  3. package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-CZ66YA4T.mjs} +8 -8
  4. package/dist/android-BKA16DTf.mjs +4 -0
  5. package/dist/{android-B9yaNKwa.mjs → android-BvwuMUJ9.mjs} +192 -98
  6. package/dist/{android-DE4v59Mb.mjs → android-CBwCNicO.mjs} +2 -2
  7. package/dist/{android-B_YNZy61.mjs → android-DC3ZLuw0.mjs} +9 -9
  8. package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-BBFyr9CA.mjs} +5 -5
  9. package/dist/android-cas-compiler.mjs +2 -2
  10. package/dist/api-D0VtFhqk.mjs +152 -0
  11. package/dist/api-run.d.mts +1 -0
  12. package/dist/api-run.mjs +145 -0
  13. package/dist/api.d.mts +212 -0
  14. package/dist/api.mjs +2 -0
  15. package/dist/{app-install-VPkI5WEg.mjs → app-install-D-PUENug.mjs} +3 -3
  16. package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
  17. package/dist/{budget-DfcgFdQB.mjs → budget-DIhL2BKx.mjs} +1578 -58
  18. package/dist/build-facts-DcLv2HSd.d.mts +111 -0
  19. package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-BI7ozfmH.mjs} +3 -3
  20. package/dist/cache-manifest.mjs +1 -1
  21. package/dist/{chrome-BrcVWQyD.mjs → chrome-DJnmnS5q.mjs} +1 -1
  22. package/dist/cli-DNJMwADx.mjs +68 -0
  23. package/dist/cli.mjs +1 -1
  24. package/dist/{client-SvBdgObD.mjs → client-DzhapoMB.mjs} +139 -32
  25. package/dist/collector-run.mjs +9 -9
  26. package/dist/{command-output-CDAIFD-8.mjs → command-output-CBtzyIgF.mjs} +2 -2
  27. package/dist/{config-D0DbpZtM.mjs → config-CgYMahz7.mjs} +5 -5
  28. package/dist/{created-devices-CpvzBL25.mjs → created-devices-DKbeWsiK.mjs} +2 -2
  29. package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-xKoOypBm.mjs} +2 -2
  30. package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-BLfcgc4O.mjs} +1 -1
  31. package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CLnIH02x.mjs} +2 -2
  32. package/dist/{device-C8IOoJPv.mjs → device-D7oCHGWi.mjs} +8 -8
  33. package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-EUOOYr8T.mjs} +13 -13
  34. package/dist/device-host-worker.mjs +19 -19
  35. package/dist/{device-ios-C2Um-XUR.mjs → device-ios-6MewKKnc.mjs} +13 -13
  36. package/dist/{device-lease-DpoS36_u.mjs → device-lease-Co-EwifR.mjs} +9 -9
  37. package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-w1xhCqIW.mjs} +4 -4
  38. package/dist/{device-pool-3o7s5lo4.mjs → device-pool-F5WvRPtD.mjs} +3 -3
  39. package/dist/{device-remote-hFSkpfQu.mjs → device-remote-tHbd1fKy.mjs} +15 -15
  40. package/dist/{doctor-CFv1fCrF.mjs → doctor-B9ckYUoU.mjs} +33 -30
  41. package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics-BRXOqDzG.mjs} +11 -11
  42. package/dist/{exec-CaErVv7t.mjs → exec-DWhCZDGm.mjs} +65 -2
  43. package/dist/{gc-DEmkg3Xh.mjs → gc-Dh6mXOKC.mjs} +23 -469
  44. package/dist/{gradle-dJpre3Yn.mjs → gradle-C6GKnunQ.mjs} +6 -6
  45. package/dist/{guide-status-Du-ZQ3FD.mjs → guide-status-Bhf9SJXc.mjs} +3 -3
  46. package/dist/{guide-CSmhMTf8.mjs → guide-tm6igAaa.mjs} +290 -53
  47. package/dist/{hosted-android-E1Heoqnv.mjs → hosted-android-Bv285t_t.mjs} +3 -3
  48. package/dist/{hosted-client-Bp4knSwu.mjs → hosted-client-C-rMtppF.mjs} +2 -2
  49. package/dist/{hosted-ios-BnbeYOUD.mjs → hosted-ios-D5mNmtj4.mjs} +1 -1
  50. package/dist/{hosted-logs-OcgIBvCT.mjs → hosted-logs-DN8Iz4mJ.mjs} +9 -9
  51. package/dist/{hosted-macos-DcxKjtfH.mjs → hosted-macos-B-wEjlwG.mjs} +5 -5
  52. package/dist/{hosted-native-1Ebbig4w.mjs → hosted-native-DeeOG8xZ.mjs} +6 -6
  53. package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-jjtxAn5C.mjs} +7 -8
  54. package/dist/{ios-DSfnPfse.mjs → ios-B3SbOWdw.mjs} +19 -7
  55. package/dist/{ios-Cr1h5gnO.mjs → ios-Bvbf0SGY.mjs} +1 -1
  56. package/dist/{ios-BJ4XUSe1.mjs → ios-CPdkoKaa.mjs} +181 -95
  57. package/dist/ios-DNpdygiv.mjs +7 -0
  58. package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-Bn7jWDB1.mjs} +1 -1
  59. package/dist/{ios-device-pAN4t54-.mjs → ios-device-vOaByMhw.mjs} +3 -3
  60. package/dist/{ios-state-8L3lgum6.mjs → ios-state-B2pES6Vs.mjs} +1 -1
  61. package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-hskNcWkZ.mjs} +5 -5
  62. package/dist/{logs-BAaS7gS2.mjs → logs-1Ylc1Ri6.mjs} +13 -13
  63. package/dist/macos-Ck7OBMNY.mjs +2 -0
  64. package/dist/{macos-BIRQS90f.mjs → macos-DF6C7wUU.mjs} +118 -34
  65. package/dist/macos-run.mjs +1 -1
  66. package/dist/maintenance-run.mjs +349 -47
  67. package/dist/{metro-g8NqiJp-.mjs → metro-Dh8T4Ytd.mjs} +5 -5
  68. package/dist/{metro-gateway-C-ijGI2F.mjs → metro-gateway-CEUw9RMH.mjs} +4 -4
  69. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
  70. package/dist/{named-ports-C8m8EQs2.mjs → named-ports-BFIz1Shz.mjs} +61 -6
  71. package/dist/{native-run-D1Q56rAD.mjs → native-run-B06FkmZx.mjs} +4 -4
  72. package/dist/{native-runtime-BEK171xQ.mjs → native-runtime-D_603JiC.mjs} +6 -6
  73. package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
  74. package/dist/offload-worker.d.mts +3 -17
  75. package/dist/offload-worker.mjs +10 -11
  76. package/dist/{ownership-DyRQru0q.mjs → ownership-D6Se7gg_.mjs} +3 -3
  77. package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
  78. package/dist/placement-log-BcPhRwR4.mjs +84 -0
  79. package/dist/{build-plan-BDsSi09V.mjs → plan-placement-CtxkCO54.mjs} +122 -48
  80. package/dist/{ports-4nYqxPWC.mjs → ports-BLTtfBq-.mjs} +3 -3
  81. package/dist/{prebuild-D7RUMkwN.mjs → prebuild-juCyreaR.mjs} +5 -5
  82. package/dist/preview-D5V07Nrl.mjs +987 -0
  83. package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
  84. package/dist/{project-D47Rh0ly.mjs → project-C3ChRLZy.mjs} +4 -68
  85. package/dist/projects-DD7dBTCj.mjs +92 -0
  86. package/dist/{pull-request-pxnDCQzn.mjs → pull-request-u4vp7zmK.mjs} +1 -1
  87. package/dist/pull-requests.mjs +1 -1
  88. package/dist/{recordings-BNgsD1Tr.mjs → recordings-BAWltsVt.mjs} +1 -1
  89. package/dist/{reload-DcRxj3qG.mjs → reload-DvyWzVjJ.mjs} +15 -15
  90. package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-BGg7aGIZ.mjs} +5 -5
  91. package/dist/{run-CkE9y0nc.mjs → run-vhtDgpHO.mjs} +5 -5
  92. package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-B9E7S1aR.mjs} +4 -5
  93. package/dist/server-expo-Db9HBUoa.mjs +2 -0
  94. package/dist/{server-expo-CHTyXD0G.mjs → server-expo-DoBHhuzK.mjs} +5 -6
  95. package/dist/{settings-SR9OmW9h.mjs → settings-Byp2AQ_E.mjs} +6 -5
  96. package/dist/{settings-Cfd-0r2d.mjs → settings-CQyMElEP.mjs} +24 -763
  97. package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
  98. package/dist/settings.schema.json +126 -3
  99. package/dist/{simslim-BWNrc3M3.mjs → simslim-EHBQ2R4G.mjs} +7 -7
  100. package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-C1nqh4mu.mjs} +6 -6
  101. package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
  102. package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
  103. package/dist/{stage-CzaiwFsI.mjs → stage-g_WqWeaE.mjs} +14 -3
  104. package/dist/{start-DUz3mYdb.mjs → start-D-IeOyoK.mjs} +19 -19
  105. package/dist/{start-AlfCPRVU.mjs → start-NTc1Ifqs.mjs} +1 -1
  106. package/dist/{state-PeQiJuIu.mjs → state-BG-aAk4k.mjs} +4 -4
  107. package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
  108. package/dist/{state-DyaNTgfR.mjs → state-xnqpx3AD.mjs} +2 -2
  109. package/dist/{stats-Bq4fJOdB.mjs → stats-DY6ekz8F.mjs} +5 -5
  110. package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
  111. package/dist/{status-BA2pehwf.mjs → status-DCjZeE-K.mjs} +33 -34
  112. package/dist/{status-CzS4bPO9.mjs → status-Dcr0Qexd.mjs} +5 -5
  113. package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-Behws_fi.mjs} +1 -1
  114. package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-aR_WsNOA.mjs} +1 -1
  115. package/dist/{stop-BIiM49EY.mjs → stop-BGhKMOuH.mjs} +5 -5
  116. package/dist/{stop-DQIasgKp.mjs → stop-BTjs3xbc.mjs} +17 -18
  117. package/dist/{stop-zF1y04f2.mjs → stop-DIJtD2GL.mjs} +2 -2
  118. package/dist/supervisor-run.d.mts +24 -22
  119. package/dist/supervisor-run.mjs +21 -22
  120. package/dist/{support-CsJNVfOc.mjs → support-BaayeYCf.mjs} +7 -7
  121. package/dist/{support-WvD__tSV.mjs → support-Bwvbrq5-.mjs} +5 -5
  122. package/dist/{toolchain-DNyR3NPq.mjs → toolchain-Bnyd_K3A.mjs} +471 -22
  123. package/dist/{trigger-Bemu8ytw.mjs → trigger-CJMhwXe8.mjs} +5 -5
  124. package/dist/trigger-DWAq9hkw.mjs +2 -0
  125. package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-CjB-MP6H.mjs} +7 -7
  126. package/dist/web-B6_bFDn7.mjs +2 -0
  127. package/dist/{web-Dl0KSRHf.mjs → web-DTU0uiWj.mjs} +22 -22
  128. package/dist/web-run.mjs +10 -10
  129. package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-C59dqG-Z.mjs} +3 -3
  130. package/dist/{ownership-BMLqyBgJ.mjs → workspaces-xXItD8Dj.mjs} +577 -23
  131. package/dist/{worktree-j3IcFAI5.mjs → worktree-BJfnU6r5.mjs} +1 -1
  132. package/dist/{worktree-Be_7NLKy.mjs → worktree-BSYlofkq.mjs} +234 -35
  133. package/dist/worktree-CsKYexJM.mjs +753 -0
  134. package/package.json +10 -5
  135. package/dist/build-progress-D0WkjCPd.mjs +0 -1036
  136. package/dist/build-slots-41v_d2x9.mjs +0 -142
  137. package/dist/cli-CYXb29oE.mjs +0 -51
  138. package/dist/deps-iKw0eD4P.mjs +0 -438
  139. package/dist/errors-B6W7FkxQ.mjs +0 -15
  140. package/dist/idle-BXG__1BA.mjs +0 -176
  141. package/dist/in-use-CvGOUn-x.mjs +0 -130
  142. package/dist/preview-B-g9d7kV.mjs +0 -318
  143. package/dist/process-identity-BmRm1qV0.mjs +0 -12
  144. package/dist/server-expo-CeakhI8P.mjs +0 -2
  145. package/dist/state-BKa8wNN5.mjs +0 -148
  146. package/dist/stop-cause-rhmGfwvt.mjs +0 -97
  147. package/dist/trigger-CXts6qt1.mjs +0 -2
  148. package/dist/watchman-APYjXSQa.mjs +0 -48
  149. package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
  150. package/dist/workspaces-Bcib97-6.mjs +0 -306
@@ -1,142 +0,0 @@
1
- import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
2
- import { a as formatElapsed } from "./command-output-CDAIFD-8.mjs";
3
- import { n as declareSpawnsOn, o as stopDeclaringSpawnsOn } from "./spawn-claims-D0XfSe8u.mjs";
4
- import { readdirSync, statSync } from "node:fs";
5
- import { join } from "node:path";
6
- import { buildSlotsDir, tryAcquireBuildSlotClaim } from "@stim-cli/core/state";
7
- //#region src/engine/build-slots.ts
8
- const SLOT_PREFIX = "slot-";
9
- const SLOT_POLL_MS = 1e3;
10
- const SLOT_PROGRESS_MS = 3e4;
11
- const SLOT_CEILING_MS = 54e5;
12
- function sleepAsync(ms) {
13
- return new Promise((resolve) => setTimeout(resolve, ms));
14
- }
15
- function toRecord(holder) {
16
- const details = holder.details;
17
- return {
18
- pid: holder.owner.pid,
19
- index: typeof details.index === "number" ? details.index : null,
20
- projectRoot: typeof details.projectRoot === "string" ? details.projectRoot : null,
21
- startedAt: holder.startedAt || null,
22
- logFile: typeof details.logFile === "string" ? details.logFile : null
23
- };
24
- }
25
- function tryAcquireBuildSlot({ max, root = null, logFile = null }) {
26
- if (!max || max <= 0) return {
27
- acquired: true,
28
- unlimited: true
29
- };
30
- const got = tryAcquireBuildSlotClaim({
31
- max,
32
- details: {
33
- projectRoot: root,
34
- logFile
35
- }
36
- });
37
- if (!got) return null;
38
- declareSpawnsOn(got.claim);
39
- return {
40
- acquired: true,
41
- path: got.path,
42
- index: got.index,
43
- slot: {
44
- pid: got.claim.owner.pid,
45
- index: got.index,
46
- projectRoot: root,
47
- startedAt: got.claim.startedAt,
48
- logFile
49
- },
50
- claim: got.claim
51
- };
52
- }
53
- function slotWaitingLine({ max, elapsedMs }) {
54
- return `${"build".padEnd(11)} waiting for a build slot (all ${max} in use, ${formatElapsed(elapsedMs)} elapsed) -- stim guide lifecycle concurrency`;
55
- }
56
- async function acquireBuildSlot({ max, root = null, logFile = null, now = Date.now, out = () => {}, waitingFor = () => {}, sleep = sleepAsync, intervalMs = SLOT_POLL_MS, progressMs = SLOT_PROGRESS_MS, ceilingMs = SLOT_CEILING_MS }) {
57
- if (!max || max <= 0) return {
58
- acquired: true,
59
- unlimited: true
60
- };
61
- const started = now();
62
- let lastProgress = started;
63
- let waited = false;
64
- try {
65
- for (;;) {
66
- const got = tryAcquireBuildSlot({
67
- max,
68
- root,
69
- logFile
70
- });
71
- if (got) return {
72
- ...got,
73
- slotWaitMs: waited ? Math.max(0, Math.round(now() - started)) : 0
74
- };
75
- waited = true;
76
- waitingFor({
77
- kind: "build-slot",
78
- inUse: max,
79
- max,
80
- since: new Date(started).toISOString()
81
- });
82
- const elapsed = now() - started;
83
- if (elapsed >= ceilingMs) {
84
- const err = /* @__PURE__ */ new Error(`Waited ${formatElapsed(elapsed)} for one of ${max} build slots, and every slot is held by a process that is still running, or by a holder Stim cannot identify. Slots live under ` + buildSlotsDir() + "; remove a slot directory whose builder is not really building, or raise concurrency.maxBuilds.");
85
- err.code = "STIM_BUILD_SLOT_TIMEOUT";
86
- throw err;
87
- }
88
- if (now() - lastProgress >= progressMs) {
89
- lastProgress = now();
90
- out(slotWaitingLine({
91
- max,
92
- elapsedMs: elapsed
93
- }));
94
- }
95
- await sleep(intervalMs);
96
- }
97
- } finally {
98
- waitingFor(null);
99
- }
100
- }
101
- function releaseBuildSlot(handle) {
102
- if (!handle || handle.unlimited) return false;
103
- stopDeclaringSpawnsOn(handle.claim);
104
- return (0, ownership_claim_exports.releaseClaim)(handle.claim);
105
- }
106
- function listBuildSlots() {
107
- const dir = buildSlotsDir();
108
- let names;
109
- try {
110
- names = readdirSync(dir);
111
- } catch {
112
- return [];
113
- }
114
- const slots = [];
115
- for (const name of names) {
116
- if (!name.startsWith(SLOT_PREFIX)) continue;
117
- const path = join(dir, name);
118
- try {
119
- if (!statSync(path).isDirectory()) continue;
120
- } catch {
121
- continue;
122
- }
123
- const survey = (0, ownership_claim_exports.readClaimSet)(path);
124
- const holder = survey.live[0] ?? survey.dead[0];
125
- const info = holder ? toRecord(holder) : null;
126
- const index = Number(name.slice(5));
127
- slots.push({
128
- path,
129
- name,
130
- index: Number.isFinite(index) ? index : null,
131
- pid: info?.pid ?? null,
132
- projectRoot: info?.projectRoot ?? null,
133
- startedAt: info?.startedAt ?? null,
134
- logFile: info?.logFile ?? null,
135
- alive: survey.live.length > 0,
136
- unresolved: survey.unresolved.length > 0
137
- });
138
- }
139
- return slots;
140
- }
141
- //#endregion
142
- export { listBuildSlots as n, releaseBuildSlot as r, acquireBuildSlot as t };
@@ -1,51 +0,0 @@
1
- import { y as refuseRelativeStimPaths } from "./config-D0DbpZtM.mjs";
2
- import { readFileSync } from "node:fs";
3
- import { Command } from "commander";
4
- //#region bin/cli.ts
5
- const commands = /* @__PURE__ */ new Map([
6
- ["doctor", () => import("./doctor-CFv1fCrF.mjs")],
7
- ["worktree", () => import("./worktree-j3IcFAI5.mjs")],
8
- ["start", () => import("./start-AlfCPRVU.mjs")],
9
- ["ports", () => import("./ports-4nYqxPWC.mjs")],
10
- ["stop", () => import("./stop-zF1y04f2.mjs")],
11
- ["ios", () => import("./ios-BJ4XUSe1.mjs")],
12
- ["android", () => import("./android-B9yaNKwa.mjs")],
13
- ["web", () => import("./web-Dl0KSRHf.mjs")],
14
- ["macos", () => import("./macos-BIRQS90f.mjs")],
15
- ["reload", () => import("./reload-DcRxj3qG.mjs")],
16
- ["device", () => import("./device-C8IOoJPv.mjs")],
17
- ["logs", () => import("./logs-BAaS7gS2.mjs")],
18
- ["settings", () => import("./settings-SR9OmW9h.mjs")],
19
- ["status", () => import("./status-BA2pehwf.mjs")],
20
- ["stats", () => import("./stats-Bq4fJOdB.mjs")],
21
- ["gc", () => import("./gc-DEmkg3Xh.mjs")],
22
- ["guide", () => import("./guide-CSmhMTf8.mjs")]
23
- ]);
24
- const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
25
- const program = new Command();
26
- program.name("stim").description("Isolated React Native dev environments per project/worktree").version(pkg.version);
27
- try {
28
- refuseRelativeStimPaths();
29
- const first = process.argv[2];
30
- const loadCommand = commands.get((first === "help" ? process.argv[3] : first) ?? "");
31
- if (loadCommand) (await loadCommand()).default(program, pkg.version);
32
- else if (first !== "--version" && first !== "-V") {
33
- const modules = await Promise.all([...commands.values()].map((load) => load()));
34
- for (const module of modules) module.default(program, pkg.version);
35
- }
36
- program.hook("preAction", async (_command, action) => {
37
- try {
38
- const { triggerMaintenance } = await import("./trigger-CXts6qt1.mjs");
39
- triggerMaintenance(action.name());
40
- } catch {}
41
- });
42
- await program.parseAsync();
43
- } catch (err) {
44
- const code = err?.code;
45
- const message = err instanceof Error ? err.message : String(err);
46
- if (typeof code === "string" && code.startsWith("STIM_")) console.error(`${code}: ${message}`);
47
- else console.error(`Unexpected error: ${message.replace(/\.$/, "")}. Report it at ${pkg.bugs.url}`);
48
- process.exit(1);
49
- }
50
- //#endregion
51
- export {};
@@ -1,438 +0,0 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
3
- import { a as spawnDeclared } from "./spawn-claims-D0XfSe8u.mjs";
4
- import { S as bundlerPin, u as HEARTBEAT_INTERVAL_MS, y as startBuildHeartbeat } from "./toolchain-DNyR3NPq.mjs";
5
- import { existsSync, readFileSync } from "node:fs";
6
- import { delimiter, dirname, join, relative, resolve, sep } from "node:path";
7
- import { homedir } from "node:os";
8
- //#region src/engine/deps.ts
9
- const DEPS_ERROR = "STIM_DEPS_FAILED";
10
- const TRANSCRIPT_LINES = 2e3;
11
- const MAX_POD_DIAGNOSTIC_LINES = 15;
12
- const POD_MARKER = /^\[!\]/;
13
- const POD_CONTINUATION = /^\s+\S/;
14
- const RUBY_HEAD = /^\S.*\.rb:\d+:in\s+(?:`[^']*'|'[^']*'):\s*\S/;
15
- const RUBY_FRAME = /^\s*from\s+\S/;
16
- const RUBY_CONTEXT_BEFORE = 4;
17
- const BUNDLER_FROZEN = /frozen mode|lockfile can't be updated|in deployment mode after changing/i;
18
- const BUNDLER_NO_COCOAPODS = /cocoapods is not currently included in the bundle|command not found: pod\b/i;
19
- function extractPodDiagnostics(transcript) {
20
- if (typeof transcript !== "string" || transcript === "") return null;
21
- const lines = transcript.split("\n").map((line) => line.replace(/\r$/, ""));
22
- return extractPodBangBlocks(lines) || extractRubyHead(lines);
23
- }
24
- function extractPodBangBlocks(lines) {
25
- const blocks = [];
26
- for (let i = 0; i < lines.length; i += 1) {
27
- const line = lines[i];
28
- if (line === void 0 || !POD_MARKER.test(line)) continue;
29
- const block = [line];
30
- let j = i + 1;
31
- for (; j < lines.length; j += 1) {
32
- const next = lines[j];
33
- if (next === void 0 || !POD_CONTINUATION.test(next)) break;
34
- block.push(next);
35
- }
36
- blocks.push(block);
37
- i = j - 1;
38
- }
39
- if (blocks.length === 0) return null;
40
- const out = [];
41
- let dropped = 0;
42
- for (const block of blocks) {
43
- const room = MAX_POD_DIAGNOSTIC_LINES - out.length;
44
- if (room <= 0) {
45
- dropped += block.length;
46
- continue;
47
- }
48
- out.push(...block.slice(0, room));
49
- dropped += Math.max(0, block.length - room);
50
- }
51
- if (dropped > 0) out.push(`(+${dropped} more [!] lines in the build log)`);
52
- return {
53
- source: "cocoapods",
54
- lines: out
55
- };
56
- }
57
- function extractRubyHead(lines) {
58
- for (let i = 0; i < lines.length; i += 1) {
59
- const line = lines[i];
60
- if (line === void 0 || !RUBY_HEAD.test(line) || RUBY_FRAME.test(line)) continue;
61
- let start = i;
62
- while (start > 0 && i - start < RUBY_CONTEXT_BEFORE) {
63
- const prev = lines[start - 1];
64
- if (prev === void 0 || prev.trim() === "" || RUBY_FRAME.test(prev) || POD_MARKER.test(prev)) break;
65
- start -= 1;
66
- }
67
- let end = i + 1;
68
- while (end < lines.length) {
69
- const next = lines[end];
70
- if (next === void 0 || next.trim() === "" || RUBY_FRAME.test(next)) break;
71
- end += 1;
72
- }
73
- return {
74
- source: "ruby",
75
- lines: lines.slice(start, end).filter((entry) => entry.trim() !== "").slice(0, MAX_POD_DIAGNOSTIC_LINES)
76
- };
77
- }
78
- return null;
79
- }
80
- function podsAreStale(lockText, manifestText) {
81
- const lock = normalize(lockText);
82
- const manifest = normalize(manifestText);
83
- if (lock === null && manifest === null) return {
84
- noPods: true,
85
- stale: false
86
- };
87
- if (lock === null) return {
88
- stale: true,
89
- reason: "ios/Podfile.lock is missing but ios/Pods exists"
90
- };
91
- if (manifest === null) return {
92
- stale: true,
93
- reason: "ios/Pods/Manifest.lock is missing (pods have never been installed here)"
94
- };
95
- if (lock !== manifest) return {
96
- stale: true,
97
- reason: "ios/Podfile.lock and ios/Pods/Manifest.lock differ"
98
- };
99
- return { stale: false };
100
- }
101
- function normalize(text) {
102
- if (typeof text !== "string") return null;
103
- return text.replace(/\r\n/g, "\n").trimEnd();
104
- }
105
- function podfilePath(root) {
106
- return join(root, "ios", "Podfile");
107
- }
108
- function readPodState(root) {
109
- return {
110
- hasPodfile: existsSync(podfilePath(root)),
111
- lockText: readOrNull(join(root, "ios", "Podfile.lock")),
112
- manifestText: readOrNull(join(root, "ios", "Pods", "Manifest.lock"))
113
- };
114
- }
115
- function readOrNull(file) {
116
- try {
117
- return readFileSync(file, "utf-8");
118
- } catch {
119
- return null;
120
- }
121
- }
122
- function podEnv(root, { env = process.env, home = homedir(), exists = existsSync } = {}) {
123
- const out = {
124
- ...env,
125
- FORCE_COLOR: "0",
126
- CLICOLOR: "0",
127
- LANG: env.LANG ?? "en_US.UTF-8",
128
- LC_ALL: env.LC_ALL ?? env.LANG ?? "en_US.UTF-8"
129
- };
130
- const version = readRubyVersion(root);
131
- if (!version) return out;
132
- const candidates = [
133
- { bin: join(home, ".rbenv", "versions", version, "bin") },
134
- {
135
- bin: join(home, ".rvm", "rubies", `ruby-${version}`, "bin"),
136
- gems: join(home, ".rvm", "gems", `ruby-${version}`)
137
- },
138
- { bin: join(home, ".asdf", "installs", "ruby", version, "bin") },
139
- { bin: join(home, ".local", "share", "mise", "installs", "ruby", version, "bin") }
140
- ];
141
- for (const c of candidates) {
142
- if (!exists(c.bin)) continue;
143
- out.PATH = `${c.bin}${delimiter}${out.PATH ?? ""}`;
144
- if (c.gems && exists(c.gems)) {
145
- out.GEM_HOME = c.gems;
146
- out.GEM_PATH = c.gems;
147
- }
148
- break;
149
- }
150
- return out;
151
- }
152
- function readRubyVersion(root, { read = readFileSync } = {}) {
153
- try {
154
- const first = String(read(join(root, ".ruby-version"), "utf-8")).split("\n").map((l) => l.trim()).find((l) => l && !l.startsWith("#"));
155
- if (!first) return null;
156
- return first.replace(/^ruby-/, "");
157
- } catch {
158
- return null;
159
- }
160
- }
161
- function bundlerEnv(root, gemfile) {
162
- return {
163
- ...podEnv(root),
164
- BUNDLE_GEMFILE: gemfile,
165
- BUNDLE_FROZEN: "true"
166
- };
167
- }
168
- const BUNDLE_PATH_SETTING = /^BUNDLE_PATH:[ \t]*["']?([^"'\r\n]+?)["']?[ \t]*$/m;
169
- function bundlePathInsideProject(root, env) {
170
- const fromEnv = env.BUNDLE_PATH;
171
- const configured = fromEnv || BUNDLE_PATH_SETTING.exec(readOrNull(join(root, ".bundle", "config")) ?? "")?.[1];
172
- if (!configured || configured.startsWith("~")) return null;
173
- const resolved = resolve(root, configured);
174
- if (resolved !== root && !resolved.startsWith(root + sep)) return null;
175
- return {
176
- path: relative(root, resolved) || ".",
177
- where: fromEnv ? "where the BUNDLE_PATH environment variable points" : "where its own .bundle/config points BUNDLE_PATH"
178
- };
179
- }
180
- async function runCaptured(ctx) {
181
- const startedAt = ctx.now();
182
- const transcript = [];
183
- const push = (line) => {
184
- const msg = stripAnsi(String(line)).trimEnd();
185
- if (!msg.trim()) return;
186
- transcript.push(msg);
187
- if (transcript.length > TRANSCRIPT_LINES) transcript.splice(TRANSCRIPT_LINES / 2, 1);
188
- ctx.logWriter?.write?.({
189
- src: "build",
190
- level: "debug",
191
- msg,
192
- raw: true,
193
- event: ctx.event
194
- });
195
- };
196
- let child;
197
- try {
198
- child = spawnDeclared(() => ctx.spawn(ctx.cmd, ctx.args, {
199
- cwd: ctx.cwd,
200
- stdio: [
201
- "ignore",
202
- "pipe",
203
- "pipe"
204
- ],
205
- env: ctx.env
206
- }));
207
- } catch (error) {
208
- return {
209
- transcript,
210
- code: null,
211
- signal: null,
212
- error,
213
- durationMs: ctx.now() - startedAt
214
- };
215
- }
216
- const reader = {
217
- out: createLineReader(push),
218
- err: createLineReader(push)
219
- };
220
- child.stdout?.setEncoding?.("utf-8");
221
- child.stderr?.setEncoding?.("utf-8");
222
- child.stdout?.on("data", (chunk) => reader.out.push(chunk));
223
- child.stderr?.on("data", (chunk) => reader.err.push(chunk));
224
- const stopHeartbeat = ctx.label ? startBuildHeartbeat({
225
- intervalMs: ctx.heartbeatMs,
226
- elapsed: () => ctx.now() - startedAt,
227
- emit: ctx.onHeartbeat,
228
- label: ctx.label,
229
- estimateMs: ctx.estimateMs ?? null
230
- }) : () => {};
231
- let result;
232
- try {
233
- result = await waitForChild(child);
234
- } finally {
235
- stopHeartbeat();
236
- }
237
- reader.out.flush();
238
- reader.err.flush();
239
- return {
240
- transcript,
241
- code: result.code ?? null,
242
- signal: result.signal ?? null,
243
- error: result.error ?? null,
244
- durationMs: ctx.now() - startedAt
245
- };
246
- }
247
- async function ensureBundledGems(root, pin, ctx) {
248
- const env = bundlerEnv(root, pin.gemfile);
249
- const cwd = dirname(pin.gemfile);
250
- const check = await runCaptured({
251
- ...ctx,
252
- cmd: "bundle",
253
- args: ["check", "--dry-run"],
254
- cwd,
255
- env,
256
- event: "bundle_check",
257
- label: "gems"
258
- });
259
- if (check.error) return bundlerSpawnFailure("bundle check", check, pin);
260
- if (check.code === 0) return { bundler: true };
261
- const install = await runCaptured({
262
- ...ctx,
263
- cmd: "bundle",
264
- args: ["install"],
265
- cwd,
266
- env,
267
- event: "bundle_install",
268
- label: "gems"
269
- });
270
- if (install.error) return bundlerSpawnFailure("bundle install", install, pin);
271
- const inProject = bundlePathInsideProject(cwd, env);
272
- const note = inProject ? `\`bundle install\` put this project's gems in ${inProject.path}${sep}, ${inProject.where}; Gemfile.lock itself is never written.` : void 0;
273
- if (install.code === 0) return {
274
- bundler: true,
275
- note
276
- };
277
- const how = install.signal ? `signal ${install.signal}` : `exit code ${install.code}`;
278
- const transcript = install.transcript.join("\n");
279
- const extracted = extractPodDiagnostics(transcript);
280
- return {
281
- bundler: false,
282
- failure: {
283
- failed: true,
284
- code: DEPS_ERROR,
285
- reason: `\`bundle install\` failed (${how}).`,
286
- remedy: frozenRemedy(root, pin, transcript) || gemInstallRemedy(root, pin),
287
- diagnosticSource: extracted ? extracted.source : "tail",
288
- diagnosticLines: extracted ? extracted.lines : [],
289
- lastLines: install.transcript.slice(-20),
290
- durationMs: install.durationMs
291
- }
292
- };
293
- }
294
- function bundlerSpawnFailure(command, run, pin) {
295
- if (isMissingBinary(run.error)) return {
296
- bundler: false,
297
- note: missingBundlerNote(pin)
298
- };
299
- return {
300
- bundler: false,
301
- failure: {
302
- failed: true,
303
- code: DEPS_ERROR,
304
- reason: `Could not run \`${command}\`: ${run.error?.message || run.error}`,
305
- remedy: `Bundler is how this project pins its CocoaPods (${pin.lockfile}). Check that \`bundle\` runs in ${dirname(pin.gemfile)}, then run again.`,
306
- lastLines: run.transcript.slice(-20),
307
- durationMs: run.durationMs
308
- }
309
- };
310
- }
311
- function missingBundlerNote(pin) {
312
- 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).`;
313
- }
314
- function frozenRemedy(root, pin, transcript) {
315
- if (!BUNDLER_FROZEN.test(transcript)) return void 0;
316
- 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.`;
317
- }
318
- function gemInstallRemedy(root, pin) {
319
- const version = readRubyVersion(root);
320
- 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.`;
321
- }
322
- async function runPodInstall(root, logWriter, { spawnFn = null, now = Date.now, heartbeatMs = HEARTBEAT_INTERVAL_MS, estimateMs = null, onHeartbeat = (line) => console.error(line) } = {}) {
323
- const iosDir = join(root, "ios");
324
- if (!existsSync(iosDir)) return {
325
- failed: true,
326
- code: DEPS_ERROR,
327
- reason: `No ios/ directory in ${root}, so there is nothing to pod install.`,
328
- remedy: "Run `stim ios` on a project with native iOS sources, or let prebuild generate them.",
329
- lastLines: []
330
- };
331
- const ctx = {
332
- logWriter,
333
- spawn: spawnFn || ((cmd, args, opts) => getExecutor().spawn(cmd, args, opts)),
334
- now,
335
- heartbeatMs,
336
- onHeartbeat
337
- };
338
- const notes = [];
339
- const pin = bundlerPin(root);
340
- let bundler = false;
341
- if (pin) {
342
- const gems = await ensureBundledGems(root, pin, ctx);
343
- if (gems.failure) return {
344
- ...gems.failure,
345
- notes
346
- };
347
- if (gems.note) notes.push(gems.note);
348
- bundler = gems.bundler;
349
- }
350
- const args = bundler ? [
351
- "bundle",
352
- "exec",
353
- "pod",
354
- "install"
355
- ] : ["pod", "install"];
356
- const command = args.join(" ");
357
- const run = await runCaptured({
358
- ...ctx,
359
- cmd: args[0],
360
- args: args.slice(1),
361
- cwd: iosDir,
362
- env: bundler && pin ? bundlerEnv(root, pin.gemfile) : podEnv(root),
363
- event: "pod_install",
364
- label: "pods",
365
- estimateMs
366
- });
367
- if (run.error) return {
368
- ...(bundler ? missingBundle(run.error, pin) : missingPod(run.error)) || {
369
- failed: true,
370
- code: "STIM_DEPS_FAILED",
371
- reason: `Could not run \`${command}\`: ${run.error?.message || run.error}`,
372
- lastLines: run.transcript.slice(-20),
373
- durationMs: run.durationMs
374
- },
375
- command,
376
- notes
377
- };
378
- if (run.code !== 0) {
379
- const how = run.signal ? `signal ${run.signal}` : `exit code ${run.code}`;
380
- const transcript = run.transcript.join("\n");
381
- const extracted = extractPodDiagnostics(transcript);
382
- return {
383
- failed: true,
384
- code: DEPS_ERROR,
385
- reason: `\`${command}\` failed (${how}).`,
386
- remedy: podFailureRemedy(root, pin, transcript),
387
- command,
388
- notes,
389
- diagnosticSource: extracted ? extracted.source : "tail",
390
- diagnosticLines: extracted ? extracted.lines : [],
391
- lastLines: run.transcript.slice(-20),
392
- durationMs: run.durationMs
393
- };
394
- }
395
- return {
396
- ok: true,
397
- command,
398
- notes,
399
- durationMs: run.durationMs
400
- };
401
- }
402
- function podFailureRemedy(root, pin, transcript) {
403
- if (pin) {
404
- const frozen = frozenRemedy(root, pin, transcript);
405
- if (frozen) return frozen;
406
- 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.`;
407
- }
408
- const pinned = /Could not find proper version of cocoapods/.test(transcript) ? readRubyVersion(root) : null;
409
- if (!pinned) return void 0;
410
- 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.`;
411
- }
412
- function isMissingBinary(err) {
413
- const nodeErr = err;
414
- const message = String(nodeErr?.message || err || "");
415
- return nodeErr?.code === "ENOENT" || /ENOENT|not found/i.test(message);
416
- }
417
- function missingBundle(err, pin) {
418
- if (!isMissingBinary(err)) return null;
419
- return {
420
- failed: true,
421
- code: DEPS_ERROR,
422
- reason: "Bundler is not installed: no `bundle` executable on PATH.",
423
- remedy: `${pin?.lockfile ?? "Gemfile.lock"} pins this project's CocoaPods, so pods run through bundler. Install bundler (\`gem install bundler\`) and run again.`,
424
- lastLines: []
425
- };
426
- }
427
- function missingPod(err) {
428
- if (!isMissingBinary(err)) return null;
429
- return {
430
- failed: true,
431
- code: DEPS_ERROR,
432
- reason: "CocoaPods is not installed: no `pod` executable on PATH.",
433
- remedy: "Install CocoaPods (`brew install cocoapods`, or `gem install cocoapods`) and run again.",
434
- lastLines: []
435
- };
436
- }
437
- //#endregion
438
- export { runPodInstall as a, runCaptured as i, podsAreStale as n, readPodState as r, DEPS_ERROR as t };
@@ -1,15 +0,0 @@
1
- //#region src/supervisor/errors.ts
2
- function supervisorError(code, message, remedy) {
3
- const err = new Error(message);
4
- err.code = code;
5
- if (remedy) err.remedy = remedy;
6
- return err;
7
- }
8
- function describeError(err) {
9
- if (!err) return "unknown error";
10
- const e = err;
11
- const message = e.message || String(err);
12
- return `${e.code ? `${e.code}: ` : ""}${message}${e.remedy ? ` Remedy: ${e.remedy}` : ""}`;
13
- }
14
- //#endregion
15
- export { supervisorError as n, describeError as t };