stim 1.13.0 → 1.15.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 (133) hide show
  1. package/README.md +2 -0
  2. package/dist/{activity-DfVKZTEB.mjs → activity-B2-kb7k0.mjs} +281 -41
  3. package/dist/{android-BHfAu2mf.mjs → android-CYmq0jNi.mjs} +2 -2
  4. package/dist/{android-CD8pr8Q2.mjs → android-Cu_L4RpL.mjs} +524 -412
  5. package/dist/{android-qx_31wJo.mjs → android-Db2eEMaf.mjs} +152 -65
  6. package/dist/android-cas-PnKfAdB2.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{app-install-CDFY2nUL.mjs → app-install-CAAplXAt.mjs} +162 -173
  9. package/dist/{budget-E5o91NR1.mjs → budget-CMiqghig.mjs} +1048 -77
  10. package/dist/{build-plan-BTa93o_r.mjs → build-plan-aeGMFX7e.mjs} +193 -650
  11. package/dist/build-progress-C2Jv1AtA.mjs +872 -0
  12. package/dist/build-slots-Df1ApSuH.mjs +127 -0
  13. package/dist/{cache-manifest-DWPF4RKJ.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  14. package/dist/cache-manifest.mjs +1 -1
  15. package/dist/{chrome-B5hWgQJS.mjs → chrome-BrcVWQyD.mjs} +4 -4
  16. package/dist/cli-0cX5s4NM.mjs +45 -0
  17. package/dist/cli.mjs +6 -42
  18. package/dist/client-C2IS36Qf.mjs +1333 -0
  19. package/dist/collector-run.mjs +8 -7
  20. package/dist/{command-output-BIL7rlX7.mjs → command-output-CliHHn7a.mjs} +1 -1
  21. package/dist/{config-0jCcMM0E.mjs → config-D0DbpZtM.mjs} +29 -29
  22. package/dist/{created-devices-Cd558h31.mjs → created-devices-BwVb3liH.mjs} +2 -2
  23. package/dist/{dependency-state-DNN0yfx6.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
  24. package/dist/{deps-2Wo81np9.mjs → deps-BFlKbOIp.mjs} +3 -3
  25. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  26. package/dist/dev-client-CjzLoNs0.mjs +80 -0
  27. package/dist/{device-Cf7vl-so.mjs → device-BZ1IyS5A.mjs} +7 -7
  28. package/dist/device-capacity-DMuKC7nx.mjs +136 -0
  29. package/dist/device-host-worker.d.mts +1 -0
  30. package/dist/device-host-worker.mjs +769 -0
  31. package/dist/device-ios-DMQBUPus.mjs +381 -0
  32. package/dist/{device-lease-CL9dZf73.mjs → device-lease-W5Pi1q38.mjs} +43 -36
  33. package/dist/{device-pool-MuoiyRcu.mjs → device-pool-B2r38YjS.mjs} +4 -4
  34. package/dist/{device-remote-DT9ngZiD.mjs → device-remote-9ulPsdhY.mjs} +68 -162
  35. package/dist/{doctor-D2iysNNH.mjs → doctor-BcE5xsBn.mjs} +191 -99
  36. package/dist/{error-diagnostics-BoiOhck8.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
  37. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  38. package/dist/{gc-eA-PRBX8.mjs → gc-BuL1Genr.mjs} +430 -102
  39. package/dist/{android-cas-B9Kv9aIy.mjs → gradle-CmdOBq1d.mjs} +8 -294
  40. package/dist/{guide-BfZ0OmiP.mjs → guide-cCSGRFd3.mjs} +2052 -240
  41. package/dist/{guide-status-BaMYu55M.mjs → guide-status-CIoWK6mG.mjs} +3 -3
  42. package/dist/hosted-macos-DwowNU7W.mjs +447 -0
  43. package/dist/idle-shutdown-B5LPaNyY.mjs +122 -0
  44. package/dist/in-use-CndPMilN.mjs +110 -0
  45. package/dist/{ios-CmEzZiOj.mjs → ios-3CtCRhwr.mjs} +201 -10
  46. package/dist/{ios-CcfCeZ19.mjs → ios-Chob8ny7.mjs} +560 -259
  47. package/dist/{ios-device-BW-t_Lkv.mjs → ios-device-DERmMmk-.mjs} +12 -7
  48. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  49. package/dist/{launch-verify-CuJaBpEa.mjs → launch-verify-CLlofxRt.mjs} +42 -11
  50. package/dist/{logs-DcSRbC2t.mjs → logs-h9h6MUvE.mjs} +95 -13
  51. package/dist/machine-usage-D_YYMzfR.mjs +283 -0
  52. package/dist/machines-CYtOCqcj.mjs +259 -0
  53. package/dist/macos-BwtbUtTN.mjs +468 -0
  54. package/dist/macos-run.d.mts +5 -0
  55. package/dist/macos-run.mjs +2 -0
  56. package/dist/{merge-state-ytFQCeh4.mjs → merge-state-BnxLNQiM.mjs} +22 -12
  57. package/dist/{metro-BPeY1YMh.mjs → metro-Di8EN2Tb.mjs} +20 -120
  58. package/dist/{named-ports-n7tnJsI8.mjs → named-ports-Dj_XBNaL.mjs} +8 -64
  59. package/dist/native-run-DFDoIRbY.mjs +147 -0
  60. package/dist/{native-runtime-CiiFHt0e.mjs → native-runtime-i6OkVg-w.mjs} +11 -6
  61. package/dist/offload-worker.d.mts +121 -0
  62. package/dist/offload-worker.mjs +601 -0
  63. package/dist/{ownership-Bz2kETAS.mjs → ownership-BkYxUtuj.mjs} +3 -2
  64. package/dist/{page-BTu7I60K.mjs → page-QF_Ke10s.mjs} +7 -2
  65. package/dist/{ports-C5UutqpJ.mjs → ports-DddwttKM.mjs} +3 -3
  66. package/dist/prebuild-BvxDlD6C.mjs +213 -0
  67. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  68. package/dist/{project-DGeclhvO.mjs → project-CV3tDFHl.mjs} +9 -18
  69. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  70. package/dist/pull-requests.d.mts +120 -0
  71. package/dist/pull-requests.mjs +2 -0
  72. package/dist/recordings-GEXp4iBT.mjs +37 -0
  73. package/dist/{reload-C-cV5yy_.mjs → reload-ChPiwvot.mjs} +70 -22
  74. package/dist/{remote-cache--aehr4Pr.mjs → remote-cache-CO2elm_b.mjs} +4 -4
  75. package/dist/run-B-xqRrsg.mjs +119 -0
  76. package/dist/{server-bare-Bq8BpSIo.mjs → server-bare-X7PUgizL.mjs} +60 -13
  77. package/dist/{server-expo-DUCg52-K.mjs → server-expo-DhtX6bom.mjs} +6 -10
  78. package/dist/server-expo-KJeD109I.mjs +2 -0
  79. package/dist/{settings-xB0Frkg6.mjs → settings-CduVcoxj.mjs} +77 -26
  80. package/dist/{settings-ssVBToDR.mjs → settings-DbL6uhH8.mjs} +16 -10
  81. package/dist/settings.schema.json +357 -2
  82. package/dist/{simslim-Bbg9UWrt.mjs → simslim-Ciluxlq4.mjs} +8 -132
  83. package/dist/slot-launch-BY0BrOLh.mjs +65 -0
  84. package/dist/{spawn-claims-PT8aQpCa.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  85. package/dist/spawn-entry-DncpJvWL.mjs +16 -0
  86. package/dist/stage-CzaiwFsI.mjs +143 -0
  87. package/dist/start-CPO4wRg7.mjs +2 -0
  88. package/dist/{start-BZhItkiL.mjs → start-CUD9nasA.mjs} +108 -61
  89. package/dist/state-B4PQFXMW.d.mts +12 -0
  90. package/dist/state-BxCgeTQu.mjs +41 -0
  91. package/dist/{state-DwAU2_-D.mjs → state-CaeaHbDA.mjs} +7 -4
  92. package/dist/{state-l1pYwOre.mjs → state-DFm77wRn.mjs} +67 -5
  93. package/dist/stats-DBLLFmKd.mjs +66 -0
  94. package/dist/{status-TJlL0HWo.mjs → status-DYXxNOT0.mjs} +425 -260
  95. package/dist/{status-sszP4XKU.mjs → status-l84Gbvp2.mjs} +122 -20
  96. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  97. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  98. package/dist/stim-footprint.swift +32 -0
  99. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  100. package/dist/{stop-BZ2qMiBP.mjs → stop-Bad61z_2.mjs} +128 -19
  101. package/dist/stop-Bvf1MflS.mjs +48 -0
  102. package/dist/{stop-DskDKNTU.mjs → stop-CceYh1yG.mjs} +2 -2
  103. package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
  104. package/dist/supervisor-run.d.mts +44 -12
  105. package/dist/supervisor-run.mjs +276 -33
  106. package/dist/support-CDQbDkL5.mjs +315 -0
  107. package/dist/support-WLOzVKUv.mjs +176 -0
  108. package/dist/teardown-BYuQafiA.mjs +718 -0
  109. package/dist/{xcode-LM6A8cNZ.mjs → toolchain-DmeYjXD9.mjs} +363 -13
  110. package/dist/warm-progress-DVHLN4uC.mjs +463 -0
  111. package/dist/{idle-DxLi5OkN.mjs → watchman-Bx0wceoF.mjs} +55 -8
  112. package/dist/{web-sWXd5iek.mjs → web-k6w8-HnE.mjs} +39 -22
  113. package/dist/web-run.mjs +326 -13
  114. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  115. package/dist/workspace-state-CL2V2Ons.mjs +371 -0
  116. package/dist/worktree-BT_uULFx.mjs +2 -0
  117. package/dist/{worktree-DeBQSVtR.mjs → worktree-C0c5Udwa.mjs} +55 -54
  118. package/dist/{worktree-D4Do_XiQ.mjs → worktree-qXKzADSC.mjs} +161 -255
  119. package/package.json +10 -5
  120. package/shim/bundle-response.cjs +55 -10
  121. package/shim/bundle-response.d.cts +1 -0
  122. package/shim/expo-metro-config.cjs +10 -2
  123. package/dist/build-progress-l7d-X-ZP.mjs +0 -247
  124. package/dist/in-use-wYEaKQM9.mjs +0 -249
  125. package/dist/ownership-PUP4Dwai.mjs +0 -1138
  126. package/dist/server-expo-BeYwP_Xy.mjs +0 -2
  127. package/dist/slot-launch-BIWEW-wI.mjs +0 -19
  128. package/dist/start-Rw2GD3nV.mjs +0 -2
  129. package/dist/stats-CaSwmsSZ.mjs +0 -62
  130. package/dist/stats-D8rUTPHd.mjs +0 -368
  131. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  132. package/dist/worktree-3cBa-bQt.mjs +0 -2
  133. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -1,116 +1,11 @@
1
- import { N as isOnMountedVolume, O as process_identity_exports, P as listMountedVolumes, _ as loadConfig$1, b as releaseMetroPort, n as allMetroPorts, r as claimMetroPort } from "./config-0jCcMM0E.mjs";
2
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
- import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
- import { existsSync as existsSync$1, readlinkSync as readlinkSync$1, realpathSync as realpathSync$1 } from "fs";
5
- import { join, resolve, sep } from "path";
6
- import { buildCacheRoot as sharedBuildCache, withDirLock, workspaceId, workspaceName as workspaceName$1, workspaceStateDir as workspaceDir } from "@stim-cli/core";
7
- import { emulatorLogFile, readJsonObject, readWorkspaceState, readWorkspaceState as readWorkspaceState$1, sharedCcache, sharedCompilationCache, supervisorLogFile, supervisorPidFile, workspaceDerivedData, workspaceLastUsed, workspaceLogErrorIndex, workspaceLogsDir, workspaceMetadataFile, workspaceStateFile, workspaceStateLock } from "@stim-cli/core/state";
8
- import { dirname as dirname$1 } from "node:path";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { M as isOnMountedVolume, N as listMountedVolumes, _ as loadConfig, n as allMetroPorts, r as claimMetroPort, x as releaseMetroPort } from "./config-D0DbpZtM.mjs";
3
+ import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
+ import { r as readWorkspaceState } from "./workspace-state-CL2V2Ons.mjs";
5
+ import { existsSync, readlinkSync, realpathSync } from "fs";
6
+ import { sep } from "path";
9
7
  import { request } from "http";
10
8
  import { connect } from "net";
11
- //#region src/workspace/paths.ts
12
- function ensureWorkspaceStorage(projectRoot) {
13
- const canonicalRoot = resolve(projectRoot);
14
- const dir = workspaceDir(canonicalRoot);
15
- const file = workspaceMetadataFile(canonicalRoot);
16
- mkdirSync(dir, { recursive: true });
17
- return withDirLock(join(dir, "metadata.lock"), () => {
18
- if (!existsSync(file)) {
19
- const metadata = {
20
- projectRoot: canonicalRoot,
21
- workspace: workspaceName$1(canonicalRoot),
22
- version: 1
23
- };
24
- const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
25
- writeFileSync(tmp, `${JSON.stringify(metadata, null, 2)}\n`);
26
- try {
27
- renameSync(tmp, file);
28
- } catch (error) {
29
- rmSync(tmp, { force: true });
30
- throw error;
31
- }
32
- return dir;
33
- }
34
- if (readJsonObject(file)?.projectRoot === canonicalRoot) return dir;
35
- const error = /* @__PURE__ */ new Error(`Stim workspace collision at ${dir}: its workspace.json does not belong to ${canonicalRoot}.`);
36
- error.code = "STIM_WORKSPACE_COLLISION";
37
- throw error;
38
- });
39
- }
40
- //#endregion
41
- //#region src/workspace/workspace-state.ts
42
- function withWorkspaceStateLock(root, fn) {
43
- const file = workspaceStateFile(root);
44
- return withDirLock(workspaceStateLock(root), fn, { ensureParent: () => {
45
- ensureWorkspaceStorage(root);
46
- mkdirSync(dirname$1(file), { recursive: true });
47
- } });
48
- }
49
- function writeWorkspaceState(root, patch) {
50
- return updateWorkspaceState(root, (state) => ({
51
- ...state,
52
- ...patch
53
- }));
54
- }
55
- function updateWorkspaceState(root, update) {
56
- return withWorkspaceStateLock(root, () => replaceWorkspaceState(root, update(readWorkspaceState(root) ?? {})));
57
- }
58
- function replaceWorkspaceState(root, state) {
59
- const file = workspaceStateFile(root);
60
- mkdirSync(dirname$1(file), { recursive: true });
61
- const tmp = `${file}.tmp-${process.pid}`;
62
- writeFileSync(tmp, `${JSON.stringify(state, null, 2)}\n`);
63
- renameSync(tmp, file);
64
- return state;
65
- }
66
- function clearWorkspaceStateKeys(root, keys) {
67
- if (!existsSync(workspaceStateFile(root))) return;
68
- withWorkspaceStateLock(root, () => {
69
- const state = readWorkspaceState(root);
70
- const file = workspaceStateFile(root);
71
- if (!state) {
72
- try {
73
- rmSync(file, { force: true });
74
- } catch {}
75
- return;
76
- }
77
- let changed = false;
78
- for (const key of keys) {
79
- if (!(key in state)) continue;
80
- delete state[key];
81
- changed = true;
82
- }
83
- if (!changed) return;
84
- if (Object.keys(state).length === 0) {
85
- rmSync(file, { force: true });
86
- return;
87
- }
88
- replaceWorkspaceState(root, state);
89
- });
90
- }
91
- function clearWorkspaceStateKey(root, key, shouldClear) {
92
- return withWorkspaceStateLock(root, () => {
93
- const state = readWorkspaceState(root);
94
- if (!state || !(key in state)) return true;
95
- if (!shouldClear(state[key])) return false;
96
- delete state[key];
97
- const file = workspaceStateFile(root);
98
- if (Object.keys(state).length === 0) {
99
- try {
100
- rmSync(file, { force: true });
101
- } catch {}
102
- return true;
103
- }
104
- replaceWorkspaceState(root, state);
105
- return true;
106
- });
107
- }
108
- function recordWorkspaceUse(root, now = /* @__PURE__ */ new Date()) {
109
- try {
110
- writeWorkspaceState(root, { lastUsedAt: now.toISOString() });
111
- } catch {}
112
- }
113
- //#endregion
114
9
  //#region src/ports.ts
115
10
  function isMetroRunning(port) {
116
11
  return new Promise((resolve) => {
@@ -161,14 +56,14 @@ async function computeNextPort(isFree = isPortFree) {
161
56
  throw new Error(`Found no free Metro port between ${FIRST_PORT} and 8281. Free one up, or stop a stale bundler (\`stim status\`, \`stim stop <port>\`).`);
162
57
  }
163
58
  async function findReclaimablePort(excludeProjectPath, probe = isMetroRunning, { isMounted = isOnMountedVolume, mountedVolumes } = {}) {
164
- const cfg = loadConfig$1();
59
+ const cfg = loadConfig();
165
60
  if (!cfg?.projects) return null;
166
61
  const mounted = mountedVolumes || listMountedVolumes();
167
62
  const candidates = [];
168
63
  for (const [path, proj] of Object.entries(cfg.projects)) {
169
64
  if (path === excludeProjectPath) continue;
170
65
  if (Object.keys(proj.ports ?? {}).length) continue;
171
- if (existsSync$1(path)) continue;
66
+ if (existsSync(path)) continue;
172
67
  if (!isMounted(path, mounted)) continue;
173
68
  if (typeof proj.metroPort === "number") candidates.push({
174
69
  port: proj.metroPort,
@@ -187,7 +82,11 @@ async function allocatePort(projectPath, probe = isMetroRunning, isFree = isPort
187
82
  return computeNextPort(isFree);
188
83
  }
189
84
  const RESERVE_ATTEMPTS = 5;
190
- async function reserveMetroPort(projectPath, probe = isMetroRunning, isFree = isPortFree) {
85
+ async function reserveMetroPort(projectPath, probe = isMetroRunning, isFree = isPortFree, pinned = null) {
86
+ if (pinned !== null) {
87
+ if (claimMetroPort(projectPath, pinned) !== null) return pinned;
88
+ throw new Error(`metro.port ${pinned} is already reserved by another workspace's Metro or named port.`);
89
+ }
191
90
  for (let attempt = 0; attempt < RESERVE_ATTEMPTS; attempt++) {
192
91
  const port = await allocatePort(projectPath, probe, isFree);
193
92
  const claimed = claimMetroPort(projectPath, port);
@@ -296,7 +195,7 @@ async function processCwds(pids) {
296
195
  for (const pid of new Set(pids)) {
297
196
  let cwd = null;
298
197
  if (process.platform === "linux") try {
299
- cwd = readlinkSync$1(`/proc/${pid}/cwd`);
198
+ cwd = readlinkSync(`/proc/${pid}/cwd`);
300
199
  } catch {}
301
200
  if (cwd) cwds.set(pid, cwd);
302
201
  else viaLsof.push(pid);
@@ -317,13 +216,13 @@ async function processCwds(pids) {
317
216
  }
318
217
  function processCwd(pid) {
319
218
  if (process.platform === "linux") try {
320
- return readlinkSync$1(`/proc/${pid}/cwd`);
219
+ return readlinkSync(`/proc/${pid}/cwd`);
321
220
  } catch {}
322
221
  return parseLsofCwd(getExecutor().runQuiet(`lsof -a -p ${pid} -d cwd -Fn`));
323
222
  }
324
223
  function canonicalPath(path) {
325
224
  try {
326
- return realpathSync$1(path);
225
+ return realpathSync(path);
327
226
  } catch {
328
227
  return path;
329
228
  }
@@ -338,7 +237,7 @@ const NOT_OURS_UNRESPONSIVE = "unresponsive";
338
237
  const NOT_OURS_UNREADABLE_CWD = "unreadable-cwd";
339
238
  const NOT_OURS_FOREIGN_CWD = "foreign-cwd";
340
239
  function ownedSupervisor(projectPath, port) {
341
- const supervisor = readWorkspaceState$1(canonicalPath(projectPath))?.supervisor;
240
+ const supervisor = readWorkspaceState(canonicalPath(projectPath))?.supervisor;
342
241
  if (supervisor?.port !== port || (0, process_identity_exports.inspectProcessIdentity)(supervisor) !== "same") return null;
343
242
  return supervisor;
344
243
  }
@@ -368,7 +267,8 @@ async function resolveProjectMetro(port, projectPath, { probe = isMetroRunning,
368
267
  if (!isInsideProject(cwd, projectPath)) return {
369
268
  notOurs: `pid ${pid} on port ${port} runs from ${cwd}, outside ${projectPath}`,
370
269
  kind: NOT_OURS_FOREIGN_CWD,
371
- pid
270
+ pid,
271
+ cwd
372
272
  };
373
273
  const owner = ownedSupervisor(projectPath, port);
374
274
  return { metro: {
@@ -406,4 +306,4 @@ function killMetroTree(leader, processToken) {
406
306
  }
407
307
  }
408
308
  //#endregion
409
- export { workspaceId as A, sharedBuildCache as C, supervisorPidFile as D, supervisorLogFile as E, withDirLock as F, workspaceLogsDir as M, workspaceName$1 as N, workspaceDerivedData as O, workspaceStateFile as P, ensureWorkspaceStorage as S, sharedCompilationCache as T, updateWorkspaceState as _, pidExists as a, writeWorkspaceState as b, resolveProjectMetro as c, isPortFree as d, reserveMetroPort as f, recordWorkspaceUse as g, readWorkspaceState$1 as h, listeningPidsByPort as i, workspaceLogErrorIndex as j, workspaceDir as k, signalProcessTree as l, clearWorkspaceStateKeys as m, killMetroTree as n, processCwd as o, clearWorkspaceStateKey as p, listeningPids as r, processCwds as s, NOT_OURS_FOREIGN_CWD as t, isMetroRunning as u, withWorkspaceStateLock as v, sharedCcache as w, emulatorLogFile as x, workspaceLastUsed as y };
309
+ export { listeningPidsByPort as a, processCwds as c, isMetroRunning as d, isPortFree as f, listeningPids as i, resolveProjectMetro as l, isInsideProject as n, pidExists as o, reserveMetroPort as p, killMetroTree as r, processCwd as s, NOT_OURS_FOREIGN_CWD as t, signalProcessTree as u };
@@ -1,62 +1,9 @@
1
- import { A as ownership_claim_exports, E as withConfigLock, O as process_identity_exports, S as saveConfig, _ as loadConfig, c as ensureConfig, d as getConfigDir, p as getProject } from "./config-0jCcMM0E.mjs";
2
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
- import { d as isPortFree, l as signalProcessTree, r as listeningPids } from "./metro-BPeY1YMh.mjs";
4
- import { n as declareSpawnsOn, o as stopDeclaringSpawnsOn } from "./spawn-claims-PT8aQpCa.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { C as saveConfig, D as withConfigLock, _ as loadConfig, c as ensureConfig, d as getConfigDir, p as getProject } from "./config-D0DbpZtM.mjs";
3
+ import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
5
+ import { f as isPortFree, i as listeningPids, u as signalProcessTree } from "./metro-Di8EN2Tb.mjs";
5
6
  import { realpathSync } from "node:fs";
6
- import { join } from "node:path";
7
- //#region src/engine/workspace-process-lock.ts
8
- const DEFAULT_WAIT_MS = 6e4;
9
- const POLL_MS = 25;
10
- const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
11
- function workspaceProcessLockError(err) {
12
- const code = err?.code;
13
- if (code === "STIM_LOCK_REFUSED") return "refused";
14
- if (code === "STIM_LOCK_TIMEOUT") return "timeout";
15
- return null;
16
- }
17
- function workspaceProcessLockPath(root, name, external) {
18
- return external ? join(root, `${name}.lock`) : join(root, ".stim", `${name}.lock`);
19
- }
20
- async function withWorkspaceProcessLock(root, name, fn, { now = Date.now, sleep = defaultSleep, waitMs = DEFAULT_WAIT_MS, ownerPurpose, details = {}, onHeld, rejectOwnerPurposes = [], external = false, declareSpawns = false } = {}) {
21
- const path = workspaceProcessLockPath(root, name, external);
22
- const deadline = now() + waitMs;
23
- for (;;) {
24
- const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
25
- root: path,
26
- mode: "exclusive",
27
- label: `${name} lock`,
28
- details: ownerPurpose ? {
29
- ...details,
30
- purpose: ownerPurpose
31
- } : details
32
- });
33
- if (attempt.pending) (0, ownership_claim_exports.releaseClaim)(attempt.pending);
34
- if (attempt.acquired) {
35
- if (declareSpawns) declareSpawnsOn(attempt.acquired);
36
- try {
37
- return await fn(attempt.acquired);
38
- } finally {
39
- stopDeclaringSpawnsOn(attempt.acquired);
40
- (0, ownership_claim_exports.releaseClaim)(attempt.acquired);
41
- }
42
- }
43
- const holder = attempt.held ?? attempt.waitingFor?.[0];
44
- const purpose = holder?.details.purpose;
45
- if (typeof purpose === "string" && rejectOwnerPurposes.includes(purpose)) {
46
- const error = /* @__PURE__ */ new Error(`The ${name} lock at ${path} is held for ${purpose}.`);
47
- error.code = "STIM_LOCK_REFUSED";
48
- throw error;
49
- }
50
- if (holder && onHeld) await onHeld(holder);
51
- if (now() >= deadline) {
52
- const error = /* @__PURE__ */ new Error(`Timed out waiting for the ${name} lock at ${path}.`);
53
- error.code = "STIM_LOCK_TIMEOUT";
54
- throw error;
55
- }
56
- await sleep(POLL_MS);
57
- }
58
- }
59
- //#endregion
60
7
  //#region src/named-ports.ts
61
8
  const FIRST_PORT = 8900;
62
9
  const LAST_PORT = 8999;
@@ -124,17 +71,14 @@ function releaseBrowserPort(projectPath, port) {
124
71
  saveConfig(cfg);
125
72
  }));
126
73
  }
127
- async function allocateNamedPort(projectPath, label, { isFree = async (port) => await isPortFree(port) && portListeners(port, process.platform).length === 0, log = console.error }) {
74
+ async function allocateNamedPort(projectPath, label, { isFree = async (port) => await isPortFree(port) && portListeners(port, process.platform).length === 0 }) {
128
75
  const root = realpathSync(projectPath);
129
76
  return withPortsLock(async () => {
130
77
  const existing = getProject(root)?.ports;
131
78
  if (existing && Object.hasOwn(existing, label)) return existing[label];
132
79
  for (let port = FIRST_PORT; port <= LAST_PORT; port++) {
133
80
  if (Object.values(loadConfig()?.projects ?? {}).some((project) => project.metroPort === port || Object.values(project.ports ?? {}).includes(port))) continue;
134
- if (!await isFree(port)) {
135
- log(`Port ${port} already in use, trying next...`);
136
- continue;
137
- }
81
+ if (!await isFree(port)) continue;
138
82
  if (withConfigLock(() => {
139
83
  const cfg = ensureConfig();
140
84
  if (Object.values(cfg.projects).some((project) => project.metroPort === port || Object.values(project.ports ?? {}).includes(port))) return false;
@@ -221,4 +165,4 @@ async function clearNamedPorts(projectPath, { label, stop = false, dryRun = fals
221
165
  });
222
166
  }
223
167
  //#endregion
224
- export { withWorkspaceProcessLock as a, reserveBrowserPort as i, getNamedPort as n, workspaceProcessLockError as o, releaseBrowserPort as r, workspaceProcessLockPath as s, clearNamedPorts as t };
168
+ export { reserveBrowserPort as i, getNamedPort as n, releaseBrowserPort as r, clearNamedPorts as t };
@@ -0,0 +1,147 @@
1
+ import { a as updateWorkspaceState, b as workspaceDir, r as readWorkspaceState, t as clearWorkspaceStateKey } from "./workspace-state-CL2V2Ons.mjs";
2
+ import { i as signalDeclaredSpawns, r as refuseDeclaredSpawns, t as allowDeclaredSpawns } from "./spawn-claims-D0XfSe8u.mjs";
3
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
4
+ import { a as formatElapsed } from "./command-output-CliHHn7a.mjs";
5
+ //#region src/engine/native-run.ts
6
+ const NATIVE_RUN_LOCK = "native-run";
7
+ const NATIVE_RUN_WAIT_MS = 18e5;
8
+ const WAIT_HEARTBEAT_MS = 3e4;
9
+ const CANCEL_REQUEST_KEY = "nativeRunCancel";
10
+ function nativeRunHolder(holder) {
11
+ const { command, platform, slot } = holder.details;
12
+ return {
13
+ command: typeof command === "string" ? command : null,
14
+ platform: typeof platform === "string" ? platform : null,
15
+ slot: typeof slot === "string" ? slot : "default"
16
+ };
17
+ }
18
+ function describeNativeRunHolder(holder, now) {
19
+ const { command, slot } = nativeRunHolder(holder);
20
+ const started = Date.parse(holder.startedAt);
21
+ const facts = [`pid ${holder.owner.pid}`];
22
+ if (Number.isFinite(started)) facts.push(`running for ${formatElapsed(now - started)}`);
23
+ if (command === null) return `another Stim run (${facts.join(", ")})`;
24
+ return `\`stim ${command}${slot === "default" ? "" : ` --slot ${slot}`}\` (${facts.join(", ")})`;
25
+ }
26
+ /**
27
+ * Report a wait on the native-run lock: one line as soon as a holder is seen, a line for each new
28
+ * holder, and a heartbeat every WAIT_HEARTBEAT_MS while the same holder keeps it.
29
+ */
30
+ function nativeRunWaitNotice({ write, now = Date.now, describe = describeNativeRunHolder }) {
31
+ let claimId = null;
32
+ let lastAt = 0;
33
+ return (holder, firstLine) => {
34
+ const at = now();
35
+ if (holder.claimId !== claimId) {
36
+ claimId = holder.claimId;
37
+ lastAt = at;
38
+ write(firstLine ?? `waiting for ${describe(holder, at)} in this workspace to finish`);
39
+ return;
40
+ }
41
+ if (at - lastAt < WAIT_HEARTBEAT_MS) return;
42
+ lastAt = at;
43
+ write(`still waiting for ${describe(holder, at)}`);
44
+ };
45
+ }
46
+ /**
47
+ * What `stop` does about the run holding the native-run lock. A build is interrupted only when the stop
48
+ * leaves it nothing to deploy to: the whole workspace stops, the build targets the stopped slot, or the
49
+ * stopped slot is the workspace's only device. A build for another slot that stays is left running and
50
+ * the stop proceeds without the lock. Only an owner whose identity is proven live is signalled.
51
+ */
52
+ function decideStopAction({ stopSlot, holder, ownerIdentity, deviceSlots }) {
53
+ if (holder.command === "macos") {
54
+ if (stopSlot !== void 0) return { action: "proceed" };
55
+ return ownerIdentity === "same" ? { action: "interrupt" } : { action: "refuse" };
56
+ }
57
+ if (holder.command !== "ios" && holder.command !== "android") return { action: "wait" };
58
+ if (!(stopSlot === void 0 || holder.slot === stopSlot || deviceSlots.includes(stopSlot) && deviceSlots.every((slot) => slot === stopSlot))) return { action: "proceed" };
59
+ return ownerIdentity === "same" ? { action: "interrupt" } : { action: "refuse" };
60
+ }
61
+ function requestNativeRunCancel(root, claimId, pid = process.pid) {
62
+ updateWorkspaceState(root, (state) => ({
63
+ ...state,
64
+ [CANCEL_REQUEST_KEY]: {
65
+ claimId,
66
+ pid,
67
+ at: (/* @__PURE__ */ new Date()).toISOString()
68
+ }
69
+ }));
70
+ }
71
+ function cancelRequestFor(root, claimId) {
72
+ const request = readWorkspaceState(root)?.[CANCEL_REQUEST_KEY];
73
+ if (request?.claimId !== claimId || typeof request.pid !== "number") return null;
74
+ return { pid: request.pid };
75
+ }
76
+ function clearNativeRunCancel(root, claimId) {
77
+ try {
78
+ clearWorkspaceStateKey(root, CANCEL_REQUEST_KEY, (value) => value?.claimId === claimId);
79
+ } catch {}
80
+ }
81
+ let cancelled = null;
82
+ /** Why this process's native run was cancelled, or null while it was not. */
83
+ function runCancellation() {
84
+ return cancelled;
85
+ }
86
+ function cancelledFailure(platform, { code, message }) {
87
+ if (cancelled === null) return null;
88
+ return {
89
+ code: "STIM_CANCELLED",
90
+ message: `The ${platform} run was ${cancelled} before it finished.`,
91
+ remedy: `Run \`stim ${platform}\` again when you want the app on a device.`,
92
+ lines: [`it stopped at ${code ?? "an unfinished step"}${message ? `: ${message}` : ""}`]
93
+ };
94
+ }
95
+ function cancelOnInterrupt({ root, claim, write, exit }) {
96
+ let signals = 0;
97
+ const onInterrupt = () => {
98
+ signals += 1;
99
+ if (signals > 1) {
100
+ exit(130);
101
+ return;
102
+ }
103
+ const request = cancelRequestFor(root, claim.claimId);
104
+ cancelled = request ? `cancelled by \`stim stop\` (pid ${request.pid})` : "cancelled by an interrupt";
105
+ refuseDeclaredSpawns(`the run was ${cancelled}`);
106
+ const signalled = signalDeclaredSpawns("SIGINT");
107
+ write(signalled > 0 ? `${cancelled}; stopping the running build tool` : request ? `${cancelled}; stopping before the next build tool or install` : cancelled);
108
+ if (signalled === 0 && !request) exit(130);
109
+ };
110
+ process.on("SIGINT", onInterrupt);
111
+ return () => {
112
+ process.off("SIGINT", onInterrupt);
113
+ allowDeclaredSpawns();
114
+ clearNativeRunCancel(root, claim.claimId);
115
+ };
116
+ }
117
+ /**
118
+ * Run an `ios`, `android` or `macos` invocation under the workspace's native-run lock: the claim records the
119
+ * command, platform and slot, and a wait for another holder is reported on `write`. The first SIGINT
120
+ * forwards the interrupt to the running declared build tool; a run `stim stop` cancelled between build
121
+ * tools starts no further one; a terminal interrupt with no build tool running, or a second SIGINT, exits
122
+ * with 130 at once.
123
+ */
124
+ function withNativeBuildRun(root, details, fn, { write, exit = (code) => process.exit(code) }) {
125
+ const notice = nativeRunWaitNotice({ write });
126
+ return withWorkspaceProcessLock(workspaceDir(root), NATIVE_RUN_LOCK, async (claim) => {
127
+ const dispose = cancelOnInterrupt({
128
+ root,
129
+ claim,
130
+ write,
131
+ exit
132
+ });
133
+ try {
134
+ return await fn(claim);
135
+ } finally {
136
+ dispose();
137
+ }
138
+ }, {
139
+ external: true,
140
+ waitMs: NATIVE_RUN_WAIT_MS,
141
+ declareSpawns: true,
142
+ details: { ...details },
143
+ onHeld: (holder) => notice(holder)
144
+ });
145
+ }
146
+ //#endregion
147
+ export { decideStopAction as a, nativeRunWaitNotice as c, withNativeBuildRun as d, clearNativeRunCancel as i, requestNativeRunCancel as l, NATIVE_RUN_WAIT_MS as n, describeNativeRunHolder as o, cancelledFailure as r, nativeRunHolder as s, NATIVE_RUN_LOCK as t, runCancellation as u };
@@ -1,13 +1,17 @@
1
- import { S as ensureWorkspaceStorage, c as resolveProjectMetro, h as readWorkspaceState$1 } from "./metro-BPeY1YMh.mjs";
2
- import { l as phaseLine } from "./command-output-BIL7rlX7.mjs";
3
- import { r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING } from "./launch-verify-CuJaBpEa.mjs";
4
- import { s as startDevServer } from "./start-BZhItkiL.mjs";
1
+ import { p as ensureWorkspaceStorage, r as readWorkspaceState$1 } from "./workspace-state-CL2V2Ons.mjs";
2
+ import { l as phaseLine } from "./command-output-CliHHn7a.mjs";
3
+ import { l as resolveProjectMetro } from "./metro-Di8EN2Tb.mjs";
4
+ import { S as metroPortSetting } from "./settings-CduVcoxj.mjs";
5
+ import { r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING } from "./launch-verify-CLlofxRt.mjs";
6
+ import { c as startDevServer } from "./start-CUD9nasA.mjs";
5
7
  import { readIdleStop } from "@stim-cli/core/state";
6
8
  import chalk from "chalk";
7
9
  //#region src/commands/native-runtime.ts
8
10
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
9
11
  async function ensureDevServer({ root, port, settings, remote, note, resolve = resolveProjectMetro, start = startDevServer, readState = readWorkspaceState$1 }) {
10
- const held = port === null ? null : (await resolve(port, root)).metro;
12
+ const pin = metroPortSetting(root);
13
+ const canReuse = !pin.error && (pin.port === null || pin.port === port);
14
+ const held = port === null || !canReuse ? null : (await resolve(port, root)).metro;
11
15
  if (port !== null && held) return {
12
16
  ok: true,
13
17
  port,
@@ -52,11 +56,12 @@ async function ensureWorkspaceStorageSafely(root, { note = (_line) => {} } = {})
52
56
  throw err;
53
57
  }
54
58
  }
55
- function launchOutcomeRecord({ launchState, release, bundleId, configuration, metroPort }) {
59
+ function launchOutcomeRecord({ launchState, release, bundleId, configuration, metroPort, unattributed = false }) {
56
60
  const unverified = launchState === LAUNCH_UNVERIFIED;
57
61
  const bundling = launchState === LAUNCH_BUNDLING;
58
62
  let msg;
59
63
  if (release) msg = unverified ? `${bundleId} could not be verified as running after its ${configuration} launch` : `${bundleId} is running its embedded ${configuration} bundle`;
64
+ else if (unverified && unattributed) msg = `a bundle delivered on this workspace's Metro port ${metroPort} could not be attributed to ${bundleId} on this device: another slot of the same platform shares that Metro`;
60
65
  else if (unverified) msg = `no bundle request from ${bundleId} reached this workspace's Metro on port ${metroPort}`;
61
66
  else if (bundling) msg = `${bundleId} requested a bundle from this workspace's Metro on port ${metroPort}; it was still being built when the launch check ended`;
62
67
  else msg = `${bundleId} fetched a bundle from this workspace's Metro on port ${metroPort}`;
@@ -0,0 +1,121 @@
1
+ import "./ndjson-Du3j0AQ9.mjs";
2
+ import "./state-B4PQFXMW.mjs";
3
+ import { readFileSync } from "fs";
4
+ import "@stim-cli/core/state";
5
+ import "@expo/fingerprint";
6
+ //#region src/engine/build-facts.d.ts
7
+ interface CompilationCacheActivity {
8
+ status: "reported" | "unavailable" | "not-run";
9
+ hits: number | null;
10
+ cacheableTasks: number | null;
11
+ hitRatePercent: number | null;
12
+ swiftTargetsWithoutExplicitModules?: number;
13
+ }
14
+ interface CcacheActivity {
15
+ status: "reported" | "unavailable" | "not-run";
16
+ hits: number | null;
17
+ misses: number | null;
18
+ hitRatePercent: number | null;
19
+ }
20
+ //#endregion
21
+ //#region src/optimizations.d.ts
22
+ interface Optimizations {
23
+ buildCache: boolean;
24
+ remoteBuildCache: boolean;
25
+ releaseBundleSwap: boolean;
26
+ metroSharedCache: boolean;
27
+ metroWarmup: boolean;
28
+ ios: {
29
+ compilationCache: boolean;
30
+ swiftCompilationCache: boolean | null;
31
+ prefixMapping: boolean;
32
+ };
33
+ android: {
34
+ compilerCache: "ccache" | "cas" | "none";
35
+ casToolchain: string | null;
36
+ compilerCacheFallback: CompilerCacheFallback | null;
37
+ pch: "auto" | "on" | "off";
38
+ gradleBuildCache: boolean;
39
+ targetAbiOnly: boolean;
40
+ };
41
+ }
42
+ interface CompilerCacheFallback {
43
+ key: string;
44
+ reason: string;
45
+ fromEnvironment: boolean;
46
+ manifest: string | null;
47
+ }
48
+ //#endregion
49
+ //#region src/offload/client.d.ts
50
+ /** The Gradle choices that shape the APK, so the machine builds what this Mac's cache key describes. */
51
+ interface AndroidBuildOptions {
52
+ variant: string | null;
53
+ abi: string | null;
54
+ gradleBuildCache: boolean;
55
+ pch: "auto" | "on" | "off";
56
+ compilerCache: "ccache" | "none";
57
+ }
58
+ //#endregion
59
+ //#region src/offload/worker.d.ts
60
+ /** One file of the client's checkout, as `git ls-files -co --exclude-standard` lists it. */
61
+ interface ManifestEntry {
62
+ path: string;
63
+ kind: "file" | "exec" | "link";
64
+ size: number;
65
+ sha256: string;
66
+ }
67
+ /** What stim-server hands this process on stdin; it has already checked every path and blob. */
68
+ interface WorkerJob {
69
+ job: string;
70
+ area: string;
71
+ blobs: string;
72
+ manifest: ManifestEntry[];
73
+ platform: "ios" | "android" | "macos";
74
+ macos: {
75
+ product: string;
76
+ infoPlist: string;
77
+ bundleId: string;
78
+ resources?: Record<string, string>;
79
+ assetCatalog?: string | null;
80
+ } | null;
81
+ swiftpmCache: string;
82
+ project: string;
83
+ packageName: string | null;
84
+ isExpo: boolean;
85
+ configuration: string | null;
86
+ scheme: string | null;
87
+ runtime: string | null;
88
+ android: AndroidBuildOptions | null;
89
+ expectedFingerprint: string;
90
+ optimizations: Optimizations["ios"] | null;
91
+ /** How long the client's Gradle daemon stays warm after an Android build; 0 or absent stops it when the build ends. */
92
+ gradleDaemonIdleMs?: number;
93
+ }
94
+ interface WorkerTimings {
95
+ syncMs: number;
96
+ depsMs: number;
97
+ prebuildMs: number;
98
+ podsMs: number;
99
+ fingerprintMs: number;
100
+ buildMs: number;
101
+ packageMs: number;
102
+ }
103
+ type WorkerResult = {
104
+ ok: true;
105
+ artifact: {
106
+ path: string;
107
+ name: string;
108
+ size: number;
109
+ sha256: string;
110
+ };
111
+ fingerprint: string;
112
+ compilationCache: CompilationCacheActivity | CcacheActivity | Record<string, never>;
113
+ timings: WorkerTimings;
114
+ } | {
115
+ ok: false;
116
+ code: string;
117
+ message: string;
118
+ timings: WorkerTimings;
119
+ };
120
+ //#endregion
121
+ export { ManifestEntry, WorkerJob, WorkerResult, WorkerTimings };