stim 1.8.0 → 1.10.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 (91) hide show
  1. package/README.md +23 -155
  2. package/dist/{android-DWssqHQ8.mjs → android-787HhP5w.mjs} +168 -39
  3. package/dist/{android-BRgBYzU0.mjs → android-DBkeHwpm.mjs} +250 -200
  4. package/dist/{android-DeYdjjBP.mjs → android-DPov-fv2.mjs} +2 -2
  5. package/dist/{android-cas-SWIjzeuL.mjs → android-cas-CITmIeWY.mjs} +22 -14
  6. package/dist/android-cas-compiler.mjs +2 -2
  7. package/dist/app-install-CrJ6t_bv.mjs +819 -0
  8. package/dist/budget-BP4jZZL4.mjs +620 -0
  9. package/dist/build-progress-BXx-ZBe4.mjs +512 -0
  10. package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Pubfp8wV.mjs} +4 -4
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +22 -19
  13. package/dist/collector-run.d.mts +7 -1
  14. package/dist/collector-run.mjs +10 -9
  15. package/dist/{command-output-B3ycpsYv.mjs → command-output-D5z2_bcF.mjs} +11 -2
  16. package/dist/{config-B7iyKWkL.mjs → config-BEq0Qw_a.mjs} +134 -121
  17. package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-r9UesA9a.mjs} +2 -2
  18. package/dist/{deps-oCXVNEqQ.mjs → deps-DP4eWqea.mjs} +8 -5
  19. package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-DJHoca49.mjs} +1 -1
  20. package/dist/{device-BUoZyMoR.mjs → dev-client-92U593KT.mjs} +1090 -660
  21. package/dist/{device-RdujjHLB.mjs → device-DhTZe7L4.mjs} +13 -20
  22. package/dist/{build-lock-CgtsB6TV.mjs → device-lease-CbEaIk7b.mjs} +481 -27
  23. package/dist/{device-pool-BxRwdufO.mjs → device-pool-Di8Q-9Ro.mjs} +7 -7
  24. package/dist/{device-remote-ByN8P6CB.mjs → device-remote-Dy1jGj3U.mjs} +645 -119
  25. package/dist/{doctor-BMt156qe.mjs → doctor-B-inAJmt.mjs} +137 -113
  26. package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-xxWKNO3-.mjs} +22 -17
  27. package/dist/exec-DK8KaVHK.mjs +133 -0
  28. package/dist/{gc-v2YcoJvm.mjs → gc-ChPFZMRC.mjs} +836 -813
  29. package/dist/{guide-BTAlD1_y.mjs → guide-DwmaD_Wa.mjs} +953 -209
  30. package/dist/guide-status-CWvti2Lg.mjs +125 -0
  31. package/dist/idle-DpRLp5lj.mjs +157 -0
  32. package/dist/{build-slots-C-oAHRT1.mjs → in-use-CjUluqIX.mjs} +102 -8
  33. package/dist/{ios-BcCiI4TS.mjs → ios-B2Fpqvlb.mjs} +60 -71
  34. package/dist/{ios-BP04pY5f.mjs → ios-Dd-hYJBh.mjs} +241 -234
  35. package/dist/{ios-device-CFXA46ez.mjs → ios-device-BiOsSVzd.mjs} +3 -2
  36. package/dist/{ios-device-CauFJiYx.mjs → ios-device-CtRdEVjx.mjs} +149 -52
  37. package/dist/launch-verify-CQS_Kg-C.mjs +365 -0
  38. package/dist/{logs-CakdBTtm.mjs → logs-MARIy6m9.mjs} +20 -15
  39. package/dist/metro-BWLOJ_Wr.mjs +409 -0
  40. package/dist/{named-ports-DzMOlmaX.mjs → named-ports-DiTfSz6p.mjs} +4 -4
  41. package/dist/{ndjson-BZwI1NBv.mjs → ndjson-CDY9IkVC.mjs} +23 -43
  42. package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
  43. package/dist/{ownership-Cp52QAK1.mjs → ownership-D2xbLVoz.mjs} +2 -3
  44. package/dist/{teardown-BJqEzPo_.mjs → ownership-DZjdx8Gq.mjs} +664 -612
  45. package/dist/{ports-BIbJaKNW.mjs → ports-CLSSFOuz.mjs} +3 -3
  46. package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
  47. package/dist/{project-DDZWf_16.mjs → project-BXZzOABt.mjs} +8 -3
  48. package/dist/{reload-BdypPbjM.mjs → reload-DoETJ3rl.mjs} +11 -13
  49. package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DVbb7kXH.mjs} +2 -2
  50. package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-BvOCeakp.mjs} +8 -7
  51. package/dist/server-expo-DBVTdult.mjs +2 -0
  52. package/dist/{settings-BXXorVM2.mjs → settings-DOugPlPz.mjs} +59 -159
  53. package/dist/settings-EWYNwe72.mjs +378 -0
  54. package/dist/settings.schema.json +1064 -0
  55. package/dist/simslim-Ca3487H6.mjs +436 -0
  56. package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-C5cg0fmk.mjs} +4 -4
  57. package/dist/spawn-claims-CUleC2cM.mjs +49 -0
  58. package/dist/{start-CmxtOKjU.mjs → start-F9gv5dcC.mjs} +159 -114
  59. package/dist/start-VoFsPSes.mjs +2 -0
  60. package/dist/{state-DZobzxLW.mjs → state-WGdIbi_d.mjs} +5 -3
  61. package/dist/{stats-BYcZ6n1h.mjs → stats-BAgJaLA4.mjs} +66 -5
  62. package/dist/{stats-CLg5RuDS.mjs → stats-DBYJ5bxL.mjs} +4 -4
  63. package/dist/status-DLCD-o3s.mjs +764 -0
  64. package/dist/status-Dr4fRkym.mjs +216 -0
  65. package/dist/{guide-status-Craq9z0t.mjs → stim-installations-CINXy_su.mjs} +3 -122
  66. package/dist/{stop-C8JW6kKp.mjs → stop-Dj4pfmyv.mjs} +111 -31
  67. package/dist/{stop-B1kOpYei.mjs → stop-ZvOeTmKb.mjs} +2 -2
  68. package/dist/supervisor-run.d.mts +11 -2
  69. package/dist/supervisor-run.mjs +228 -42
  70. package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-BXA96myD.mjs} +14 -9
  71. package/dist/workspaces-BTUyFfkv.mjs +1003 -0
  72. package/dist/worktree-4aZg2NnI.mjs +2 -0
  73. package/dist/{worktree-CFLA-6MX.mjs → worktree-BMS7GeqB.mjs} +523 -61
  74. package/dist/{worktree-FG_ZKKdb.mjs → worktree-BlizKDpZ.mjs} +200 -54
  75. package/dist/{xcode-B6YaWeg0.mjs → xcode-B2tBkf0m.mjs} +66 -57
  76. package/package.json +6 -6
  77. package/skill/SKILL.md +2 -0
  78. package/dist/app-install-BDbsSgCl.mjs +0 -1175
  79. package/dist/dev-client-BmT-ioFI.mjs +0 -797
  80. package/dist/device-lease-Ffz_M5t0.mjs +0 -359
  81. package/dist/exec-B29XNuhP.mjs +0 -77
  82. package/dist/logs-query-BGL-Pl12.mjs +0 -290
  83. package/dist/metro-C7bcJ0uo.mjs +0 -233
  84. package/dist/ownership-wolE-0Do.mjs +0 -470
  85. package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
  86. package/dist/reclaim-KJ-0uWEF.mjs +0 -361
  87. package/dist/server-expo-YDDG3eP7.mjs +0 -2
  88. package/dist/spawn-entry-nCE6m770.mjs +0 -14
  89. package/dist/status-DdkLki47.mjs +0 -426
  90. package/dist/workspace-state-BT5E3SA2.mjs +0 -146
  91. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
@@ -1,22 +1,23 @@
1
- import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
- import { g as loadConfig, u as getConfigDir } from "./config-B7iyKWkL.mjs";
1
+ import { g as loadConfig, u as getConfigDir } from "./config-BEq0Qw_a.mjs";
2
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
3
  import { accessSync, chmodSync, constants, lstatSync, mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
4
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
- import { chmodSync as chmodSync$1, existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, utimesSync } from "fs";
6
- import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
4
+ import { chmodSync as chmodSync$1, existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, statSync as statSync$1, utimesSync } from "fs";
5
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "path";
6
+ import { buildCacheRoot } from "@stim-cli/core";
7
+ import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "node:path";
7
8
  import { tmpdir } from "node:os";
8
9
  //#region src/temporary.ts
9
10
  function existingPath(path) {
10
11
  const missing = [];
11
- let current = resolve(path);
12
+ let current = resolve$1(path);
12
13
  while (!lstatSync(current, { throwIfNoEntry: false })) {
13
- missing.unshift(basename(current));
14
- current = dirname(current);
14
+ missing.unshift(basename$1(current));
15
+ current = dirname$1(current);
15
16
  }
16
17
  const existing = realpathSync(current);
17
18
  return {
18
19
  existing,
19
- resolved: join(existing, ...missing)
20
+ resolved: join$1(existing, ...missing)
20
21
  };
21
22
  }
22
23
  function filesystemDevice(path) {
@@ -25,21 +26,22 @@ function filesystemDevice(path) {
25
26
  function outsideWorkingTree(path) {
26
27
  let current = path;
27
28
  while (true) {
28
- if (lstatSync(join(current, ".git"), { throwIfNoEntry: false })) return false;
29
- const parent = dirname(current);
29
+ if (lstatSync(join$1(current, ".git"), { throwIfNoEntry: false })) return false;
30
+ const parent = dirname$1(current);
30
31
  if (parent === current) return true;
31
32
  current = parent;
32
33
  }
33
34
  }
35
+ function inside(parent, path) {
36
+ const rel = relative$1(parent, path);
37
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep$1}`) && !isAbsolute$1(rel);
38
+ }
34
39
  function temporaryRoot(near) {
35
40
  const reference = existingPath(near);
36
- const insideReference = (path) => {
37
- const rel = relative(reference.resolved, path);
38
- return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
39
- };
41
+ const insideReference = (path) => inside(reference.resolved, path);
40
42
  const override = process.env.STIM_TMPDIR ?? loadConfig()?.tempDir;
41
43
  if (override !== void 0) {
42
- if (typeof override !== "string" || !isAbsolute(override)) throw new Error("STIM_TMPDIR / tempDir must be an absolute directory path.");
44
+ if (typeof override !== "string" || !isAbsolute$1(override)) throw new Error("STIM_TMPDIR / tempDir must be an absolute directory path.");
43
45
  const { existing, resolved } = existingPath(override);
44
46
  if (!statSync(existing).isDirectory() || !outsideWorkingTree(existing)) throw new Error(`Temporary directory ${override} must be outside Git working trees.`);
45
47
  if (insideReference(resolved)) throw new Error(`Temporary directory ${override} must not be inside ${near}.`);
@@ -47,22 +49,23 @@ function temporaryRoot(near) {
47
49
  return resolved;
48
50
  }
49
51
  const { existing } = reference;
52
+ const cache = existingPath(buildCacheRoot()).resolved;
50
53
  const device = statSync(existing).dev;
51
54
  const candidates = [tmpdir()];
52
- let current = existing === reference.resolved || !statSync(existing).isDirectory() ? dirname(existing) : existing;
55
+ let current = existing === reference.resolved || !statSync(existing).isDirectory() ? dirname$1(existing) : existing;
53
56
  while (statSync(current).dev === device) {
54
57
  candidates.push(current);
55
- const parent = dirname(current);
58
+ const parent = dirname$1(current);
56
59
  if (parent === current) break;
57
60
  current = parent;
58
61
  }
59
62
  for (const candidate of candidates) try {
60
63
  const path = realpathSync(candidate);
61
- if (statSync(path).dev !== device || insideReference(path) || !outsideWorkingTree(path)) continue;
64
+ if (statSync(path).dev !== device || insideReference(path) || inside(cache, path) || !outsideWorkingTree(path)) continue;
62
65
  accessSync(path, constants.W_OK);
63
66
  return path;
64
67
  } catch {}
65
- throw new Error(`No writable temporary directory on the filesystem containing ${near}. Set STIM_TMPDIR or tempDir in ${join(getConfigDir(), "config.json")} to a directory outside Git working trees.`);
68
+ throw new Error(`No writable temporary directory on the filesystem containing ${near}. Set STIM_TMPDIR or tempDir in ${join$1(getConfigDir(), "config.json")} to a directory outside Git working trees.`);
66
69
  }
67
70
  function makeTemporaryDirectory(near, prefix) {
68
71
  const root = temporaryRoot(near);
@@ -70,13 +73,13 @@ function makeTemporaryDirectory(near, prefix) {
70
73
  recursive: true,
71
74
  mode: 448
72
75
  });
73
- return mkdtempSync(join(root, prefix));
76
+ return mkdtempSync(join$1(root, prefix));
74
77
  }
75
78
  function removeTemporaryEntry(path) {
76
79
  const stat = lstatSync(path, { throwIfNoEntry: false });
77
80
  if (stat?.isDirectory()) {
78
81
  chmodSync(path, stat.mode | 448);
79
- for (const entry of readdirSync(path, { withFileTypes: true })) if (entry.isDirectory()) removeTemporaryEntry(join(path, entry.name));
82
+ for (const entry of readdirSync(path, { withFileTypes: true })) if (entry.isDirectory()) removeTemporaryEntry(join$1(path, entry.name));
80
83
  }
81
84
  rmSync(path, {
82
85
  recursive: true,
@@ -94,7 +97,7 @@ function isCarrySkipped(rel) {
94
97
  return /(^|\/)android\/build\/generated\/autolinking(\/|$)/.test(rel) || String(rel).split("/").some((seg) => CARRY_SKIP_BASENAMES.has(seg));
95
98
  }
96
99
  function nativePath(path) {
97
- return sep$1 === "/" ? path : path.replaceAll("/", sep$1);
100
+ return sep === "/" ? path : path.replaceAll("/", sep);
98
101
  }
99
102
  function gitCommonDir(cwd) {
100
103
  const out = getExecutor().runFileQuiet("git", [
@@ -173,7 +176,7 @@ function matchesInclude(path, patterns) {
173
176
  return false;
174
177
  }
175
178
  function readWorktreeExclude(root) {
176
- return readPatternFile(join$1(root, ".worktreeexclude"));
179
+ return readPatternFile(join(root, ".worktreeexclude"));
177
180
  }
178
181
  function readPatternFile(p) {
179
182
  if (!existsSync$1(p)) return null;
@@ -183,7 +186,7 @@ function canonicalPath(path) {
183
186
  try {
184
187
  return realpathSync$1(path);
185
188
  } catch {
186
- return resolve$1(path);
189
+ return resolve(path);
187
190
  }
188
191
  }
189
192
  function nestedWorktreePaths(root) {
@@ -198,9 +201,9 @@ function nestedWorktreePaths(root) {
198
201
  ]);
199
202
  if (out === null) throw new Error("Could not list Git worktrees. Refusing to carry ignored files.");
200
203
  for (const entry of parseWorktrees(out)) {
201
- const rel = relative$1(source, canonicalPath(entry.path));
202
- if (!rel || rel === ".." || rel.startsWith(`..${sep$1}`) || isAbsolute$1(rel)) continue;
203
- paths.add(rel.split(sep$1).join("/"));
204
+ const rel = relative(source, canonicalPath(entry.path));
205
+ if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) continue;
206
+ paths.add(rel.split(sep).join("/"));
204
207
  }
205
208
  return [...paths];
206
209
  }
@@ -260,11 +263,11 @@ function missingDestinationReason(target, rel) {
260
263
  const parts = rel.split("/");
261
264
  for (let i = 1; i < parts.length; i++) {
262
265
  const parent = parts.slice(0, i).join("/");
263
- const stat = lstatSync$1(join$1(target, parent), { throwIfNoEntry: false });
266
+ const stat = lstatSync$1(join(target, parent), { throwIfNoEntry: false });
264
267
  if (stat?.isSymbolicLink()) return `symlink ancestor: ${parent}`;
265
268
  if (stat && !stat.isDirectory()) return `non-directory ancestor: ${parent}`;
266
269
  }
267
- return lstatSync$1(join$1(target, rel), { throwIfNoEntry: false }) ? "exists" : null;
270
+ return lstatSync$1(join(target, rel), { throwIfNoEntry: false }) ? "exists" : null;
268
271
  }
269
272
  function removeCopiedExclusions(path, rel) {
270
273
  const entries = readdirSync$1(path, { withFileTypes: true });
@@ -273,7 +276,7 @@ function removeCopiedExclusions(path, rel) {
273
276
  const stat = lstatSync$1(path);
274
277
  chmodSync$1(path, stat.mode | 448);
275
278
  try {
276
- for (const entry of excluded) removeTemporaryEntry(join$1(path, entry.name));
279
+ for (const entry of excluded) removeTemporaryEntry(join(path, entry.name));
277
280
  } finally {
278
281
  utimesSync(path, stat.atime, stat.mtime);
279
282
  chmodSync$1(path, stat.mode);
@@ -281,7 +284,7 @@ function removeCopiedExclusions(path, rel) {
281
284
  }
282
285
  for (const entry of entries) {
283
286
  const child = `${rel}/${entry.name}`;
284
- if (entry.isDirectory() && !isCarrySkipped(child)) removeCopiedExclusions(join$1(path, entry.name), child);
287
+ if (entry.isDirectory() && !isCarrySkipped(child)) removeCopiedExclusions(join(path, entry.name), child);
285
288
  }
286
289
  }
287
290
  function cloneIgnoredEntries({ root, target, patterns }) {
@@ -293,8 +296,8 @@ function cloneIgnoredEntries({ root, target, patterns }) {
293
296
  if (!guard.known) throw new Error("Could not list the destination's tracked files.");
294
297
  const nested = nestedWorktreePaths(target);
295
298
  for (const rel of listCarryableIgnoredEntries(root, patterns)) {
296
- const from = join$1(root, rel);
297
- const to = join$1(target, rel);
299
+ const from = join(root, rel);
300
+ const to = join(target, rel);
298
301
  try {
299
302
  const reason = guard.covers(rel) ? "tracked" : overlapsNestedWorktree(rel, nested) ? "nested worktree" : missingDestinationReason(target, rel);
300
303
  if (reason) {
@@ -304,7 +307,7 @@ function cloneIgnoredEntries({ root, target, patterns }) {
304
307
  });
305
308
  continue;
306
309
  }
307
- mkdirSync$1(dirname$1(to), { recursive: true });
310
+ mkdirSync$1(dirname(to), { recursive: true });
308
311
  try {
309
312
  getExecutor().runFile("cp", [
310
313
  "-Rc",
@@ -341,8 +344,8 @@ function podsOutOfSync(target, copiedEntries, { read = readFileSync$1 } = {}) {
341
344
  for (const rel of copiedEntries || []) {
342
345
  if (rel !== "Pods" && !rel.endsWith("/Pods")) continue;
343
346
  const iosDir = rel === "Pods" ? "" : rel.slice(0, -5);
344
- const manifest = join$1(target, rel, "Manifest.lock");
345
- const podfileLock = join$1(target, iosDir, "Podfile.lock");
347
+ const manifest = join(target, rel, "Manifest.lock");
348
+ const podfileLock = join(target, iosDir, "Podfile.lock");
346
349
  if (!existsSync$1(manifest)) continue;
347
350
  if (!existsSync$1(podfileLock)) {
348
351
  problems.push({
@@ -361,29 +364,99 @@ function podsOutOfSync(target, copiedEntries, { read = readFileSync$1 } = {}) {
361
364
  return problems;
362
365
  }
363
366
  const LOCKFILE_NAMES = [
364
- "package-lock.json",
365
- "yarn.lock",
366
367
  "pnpm-lock.yaml",
368
+ "yarn.lock",
367
369
  "bun.lock",
368
- "bun.lockb"
370
+ "bun.lockb",
371
+ "package-lock.json"
369
372
  ];
373
+ function npmInstallMatches(wanted, installed) {
374
+ if (!wanted.packages || !installed.packages) return null;
375
+ for (const [key, entry] of Object.entries(wanted.packages)) {
376
+ if (!key.includes("node_modules/") || entry.link) continue;
377
+ const present = installed.packages[key];
378
+ if (!present && (entry.optional || entry.devOptional)) continue;
379
+ if (present?.version !== entry.version) return false;
380
+ }
381
+ return true;
382
+ }
383
+ function lastYamlDocument(text) {
384
+ return (text.split(/^---$/m).at(-1) ?? "").trim();
385
+ }
386
+ function yarnClassicInstallMatches(lockfile, integrity) {
387
+ if (!integrity.lockfileEntries) return null;
388
+ const wanted = [...lockfile.matchAll(/^ {2}resolved "([^"]+)"$/gm)].flatMap((m) => m[1] ?? []);
389
+ if (wanted.length === 0) return null;
390
+ const installed = new Set(Object.values(integrity.lockfileEntries));
391
+ return wanted.every((url) => installed.has(url));
392
+ }
393
+ function yarnBerryInstallMatches(lockfile, state) {
394
+ const installed = new Set([...state.matchAll(/^"?([^\s"#][^"]*?)"?:$/gm)].flatMap((m) => m[1]?.replace(/@virtual:[^#]+#/, "@") ?? []));
395
+ let entries = 0;
396
+ for (const entry of lockfile.split(/\n(?=\S)/)) {
397
+ const resolution = /^ {2}resolution: "([^"]+)"$/m.exec(entry)?.[1];
398
+ if (!resolution) continue;
399
+ entries += 1;
400
+ if (installed.has(resolution) || /^ {2}conditions: /m.test(entry)) continue;
401
+ return false;
402
+ }
403
+ return entries === 0 ? null : true;
404
+ }
405
+ function installedMatchesLockfile(dir, lockfile, { read = readFileSync$1 } = {}) {
406
+ const text = (rel) => {
407
+ try {
408
+ return read(join(dir, rel), "utf-8").replaceAll("\r\n", "\n");
409
+ } catch {
410
+ return null;
411
+ }
412
+ };
413
+ const wanted = text(lockfile);
414
+ if (wanted === null) return null;
415
+ try {
416
+ if (lockfile === "package-lock.json") {
417
+ const installed = text("node_modules/.package-lock.json");
418
+ return installed === null ? null : npmInstallMatches(JSON.parse(wanted), JSON.parse(installed));
419
+ }
420
+ if (lockfile === "pnpm-lock.yaml") {
421
+ const installed = text("node_modules/.pnpm/lock.yaml");
422
+ return installed === null ? null : lastYamlDocument(wanted) === lastYamlDocument(installed);
423
+ }
424
+ if (lockfile === "yarn.lock") {
425
+ if (/^__metadata:$/m.test(wanted)) {
426
+ const berry = text("node_modules/.yarn-state.yml");
427
+ return berry === null ? null : yarnBerryInstallMatches(wanted, berry);
428
+ }
429
+ const classic = text("node_modules/.yarn-integrity");
430
+ return classic === null ? null : yarnClassicInstallMatches(wanted, JSON.parse(classic));
431
+ }
432
+ } catch {}
433
+ return null;
434
+ }
370
435
  function depsOutOfSync(root, target, copiedEntries, { read = readFileSync$1 } = {}) {
371
436
  const problems = [];
372
437
  for (const rel of copiedEntries || []) {
373
438
  if (rel !== "node_modules" && !rel.endsWith("/node_modules")) continue;
374
439
  const dir = rel === "node_modules" ? "" : rel.slice(0, -13);
375
- for (const name of LOCKFILE_NAMES) {
376
- const source = join$1(root, dir, name);
377
- const branch = join$1(target, dir, name);
378
- if (!existsSync$1(source) || !existsSync$1(branch)) continue;
379
- try {
380
- if (read(source, "utf-8") !== read(branch, "utf-8")) problems.push({
381
- dir: dir || ".",
382
- lockfile: name
383
- });
384
- } catch {}
385
- break;
440
+ const name = LOCKFILE_NAMES.find((candidate) => existsSync$1(join(target, dir, candidate)));
441
+ if (!name) continue;
442
+ const installed = installedMatchesLockfile(join(target, dir), name, { read });
443
+ if (installed === false) {
444
+ problems.push({
445
+ dir: dir || ".",
446
+ lockfile: name,
447
+ reason: "installed"
448
+ });
449
+ continue;
386
450
  }
451
+ const source = join(root, dir, name);
452
+ if (installed === true || !existsSync$1(source)) continue;
453
+ try {
454
+ if (read(source, "utf-8") !== read(join(target, dir, name), "utf-8")) problems.push({
455
+ dir: dir || ".",
456
+ lockfile: name,
457
+ reason: "lockfile"
458
+ });
459
+ } catch {}
387
460
  }
388
461
  return problems;
389
462
  }
@@ -546,10 +619,28 @@ function parseWorktrees(out) {
546
619
  current = { path: nativePath(line.slice(9)) };
547
620
  } else if (line.startsWith("branch ")) current.branch = line.slice(7).replace("refs/heads/", "");
548
621
  else if (line === "prunable" || line.startsWith("prunable ")) current.prunable = true;
622
+ else if (line === "locked" || line.startsWith("locked ")) current.locked = true;
549
623
  else if (line === "bare") current.bare = true;
550
624
  if (current.path) entries.push(current);
551
625
  return entries;
552
626
  }
627
+ function hasPopulatedSubmodules(worktree) {
628
+ const modules = getExecutor().runFileQuiet("git", [
629
+ "-C",
630
+ worktree,
631
+ "rev-parse",
632
+ "--git-path",
633
+ "modules"
634
+ ]);
635
+ if (modules && existsSync$1(resolve(worktree, nativePath(modules)))) return true;
636
+ return (getExecutor().runFileQuiet("git", [
637
+ "-C",
638
+ worktree,
639
+ "ls-files",
640
+ "--stage",
641
+ "-z"
642
+ ]) ?? "").split("\0").some((line) => line.startsWith("160000 ") && existsSync$1(join(worktree, line.slice(line.indexOf(" ") + 1), ".git")));
643
+ }
553
644
  function listWorktrees(cwd) {
554
645
  const out = getExecutor().runFileQuiet("git", [
555
646
  "-C",
@@ -560,6 +651,61 @@ function listWorktrees(cwd) {
560
651
  ]);
561
652
  return out ? parseWorktrees(out) : [];
562
653
  }
654
+ function branchOf(head) {
655
+ return head.startsWith("ref: refs/heads/") ? head.slice(16) : void 0;
656
+ }
657
+ /**
658
+ * The linked worktrees recorded under a git common dir, read from `worktrees/<id>/gitdir` and `HEAD` without running
659
+ * git or touching the worktree directories, sorted by path. The worktree with the branch the common dir's own `HEAD`
660
+ * names (the main checkout's branch, or a bare repository's source checkout) is left out.
661
+ */
662
+ function linkedWorktreesOnDisk(commonDir) {
663
+ const admin = join(commonDir, "worktrees");
664
+ let ids;
665
+ let sourceBranch;
666
+ try {
667
+ ids = readdirSync$1(admin);
668
+ sourceBranch = branchOf(readFileSync$1(join(commonDir, "HEAD"), "utf-8").trim());
669
+ } catch {
670
+ return [];
671
+ }
672
+ return ids.flatMap((id) => {
673
+ try {
674
+ const gitdir = readFileSync$1(join(admin, id, "gitdir"), "utf-8").trim();
675
+ const branch = branchOf(readFileSync$1(join(admin, id, "HEAD"), "utf-8").trim());
676
+ if (branch && branch === sourceBranch) return [];
677
+ const path = dirname(resolve(admin, id, nativePath(gitdir)));
678
+ return [branch ? {
679
+ path,
680
+ branch
681
+ } : { path }];
682
+ } catch {
683
+ return [];
684
+ }
685
+ }).toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
686
+ }
687
+ /**
688
+ * The git common dir of the repository containing `start`, read from `.git` and `commondir` files without running
689
+ * git. Returns null when `start` is missing, unreadable, or not inside a repository.
690
+ */
691
+ function gitCommonDirOnDisk(start) {
692
+ try {
693
+ for (let dir = realpathSync$1.native(start);; dir = dirname(dir)) {
694
+ const dotGit = join(dir, ".git");
695
+ if (existsSync$1(dotGit)) {
696
+ if (statSync$1(dotGit).isDirectory()) return realpathSync$1.native(dotGit);
697
+ const gitdir = /^gitdir: (.+)$/m.exec(readFileSync$1(dotGit, "utf-8"))?.[1]?.trim();
698
+ if (!gitdir) return null;
699
+ const linked = resolve(dir, gitdir);
700
+ const commondir = join(linked, "commondir");
701
+ return realpathSync$1.native(existsSync$1(commondir) ? resolve(linked, readFileSync$1(commondir, "utf-8").trim()) : linked);
702
+ }
703
+ if (dirname(dir) === dir) return null;
704
+ }
705
+ } catch {
706
+ return null;
707
+ }
708
+ }
563
709
  function selectSourceCheckout(entries, bareHead) {
564
710
  const first = entries[0];
565
711
  if (!first) return { refusal: "Git lists no worktrees for this repository." };
@@ -574,8 +720,8 @@ function selectSourceCheckout(entries, bareHead) {
574
720
  if (only && matches.length === 1) return { path: only.path };
575
721
  if (!only) {
576
722
  const stale = entries.find((entry) => entry.prunable && entry.branch === bareHead);
577
- const beside = checkouts[0] ? dirname$1(checkouts[0].path) : bare;
578
- return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, but no worktree has ${bareHead} checked out, so Stim has no source checkout to copy from. ${stale ? `Its worktree at ${stale.path} is missing. Recreate it, then retry:\n git -C ${bare} worktree prune\n git -C ${bare} worktree add ${stale.path} ${bareHead}\n` : `Create one, then retry:\n git -C ${bare} worktree add ${join$1(beside, bareHead)} ${bareHead}\n`}Or point HEAD at a branch that is checked out (${listed}):\n${pointHead}` };
723
+ const beside = checkouts[0] ? dirname(checkouts[0].path) : bare;
724
+ return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, but no worktree has ${bareHead} checked out, so Stim has no source checkout to copy from. ${stale ? `Its worktree at ${stale.path} is missing. Recreate it, then retry:\n git -C ${bare} worktree prune\n git -C ${bare} worktree add ${stale.path} ${bareHead}\n` : `Create one, then retry:\n git -C ${bare} worktree add ${join(beside, bareHead)} ${bareHead}\n`}Or point HEAD at a branch that is checked out (${listed}):\n${pointHead}` };
579
725
  }
580
726
  return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, which is checked out in more than one worktree: ${matches.map((entry) => entry.path).join(", ")}. Remove the extra worktrees, or point HEAD at a branch checked out once, then retry:\n${pointHead}` };
581
727
  }
@@ -601,4 +747,4 @@ function resolveSourceCheckout(cwd) {
601
747
  };
602
748
  }
603
749
  //#endregion
604
- export { filesystemDevice as C, temporaryRoot as E, warmWorktreePaths as S, removeTemporaryEntry as T, resolveFullRef as _, dirtyFingerprintFiles as a, sourceCheckoutOf as b, hasRemote as c, listWorktrees as d, locallyKnownUpstream as f, repoRoot as g, removeWorktree as h, depsOutOfSync as i, hasUncommittedWork as l, readWorktreeExclude as m, cloneIgnoredEntries as n, dirtyPaths as o, podsOutOfSync as p, deleteBranch as r, gitCommonDir as s, branchExists as t, isPodInstallChurn as u, resolveSourceCheckout as v, makeTemporaryDirectory as w, unpushedCommits as x, restoreFile as y };
750
+ export { sourceCheckoutOf as C, makeTemporaryDirectory as D, filesystemDevice as E, removeTemporaryEntry as O, restoreFile as S, warmWorktreePaths as T, readWorktreeExclude as _, dirtyFingerprintFiles as a, resolveFullRef as b, gitCommonDirOnDisk as c, hasUncommittedWork as d, isPodInstallChurn as f, podsOutOfSync as g, locallyKnownUpstream as h, depsOutOfSync as i, temporaryRoot as k, hasPopulatedSubmodules as l, listWorktrees as m, cloneIgnoredEntries as n, dirtyPaths as o, linkedWorktreesOnDisk as p, deleteBranch as r, gitCommonDir as s, branchExists as t, hasRemote as u, removeWorktree as v, unpushedCommits as w, resolveSourceCheckout as x, repoRoot as y };
@@ -1,14 +1,15 @@
1
- import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
- import { r as register } from "./cache-manifest-CYJi8-Hx.mjs";
3
- import { d as sharedCompilationCache, m as workspaceDerivedData } from "./workspace-state-BT5E3SA2.mjs";
4
- import { l as resolvePackageJson } from "./project-DDZWf_16.mjs";
5
- import { t as createLineReader } from "./process-output-Dl5uKE03.mjs";
6
- import { U as resolveOptimizations } from "./settings-BXXorVM2.mjs";
7
- import { a as formatElapsed, l as phaseLine } from "./command-output-B3ycpsYv.mjs";
8
- import { t as cleanLine } from "./server-expo-BVAA1OqJ.mjs";
1
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
2
+ import { r as register } from "./cache-manifest-Pubfp8wV.mjs";
3
+ import { D as workspaceDerivedData, w as sharedCompilationCache } from "./metro-BWLOJ_Wr.mjs";
4
+ import { u as resolvePackageJson } from "./project-BXZzOABt.mjs";
5
+ import { n as spawnDeclared } from "./spawn-claims-CUleC2cM.mjs";
6
+ import { t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
7
+ import { a as formatElapsed, l as phaseLine } from "./command-output-D5z2_bcF.mjs";
8
+ import { W as resolveOptimizations } from "./settings-DOugPlPz.mjs";
9
+ import { t as cleanLine } from "./server-expo-BvOCeakp.mjs";
9
10
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
10
- import { createHash } from "node:crypto";
11
11
  import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
12
+ import { createHash } from "node:crypto";
12
13
  import chalk from "chalk";
13
14
  //#region src/engine/bundler.ts
14
15
  const COCOAPODS_SPEC = /^ {4}cocoapods \(/m;
@@ -87,66 +88,69 @@ function makeDiagnostic({ file = null, line = null, column = null, message }, ro
87
88
  function dedupeKey(d) {
88
89
  return `${d.file || ""}|${d.line || ""}|${d.column || ""}|${d.message}`;
89
90
  }
90
- function extractXcodeDiagnostics(transcript, root = null, sdk = "iphonesimulator") {
91
- if (typeof transcript !== "string" || transcript === "") return [];
92
- const lines = transcript.split("\n");
91
+ function createXcodeDiagnosticCollector(root = null, sdk = "iphonesimulator") {
93
92
  const out = [];
94
93
  const seen = /* @__PURE__ */ new Set();
95
- const push = (d) => {
94
+ let finished = false;
95
+ let inUndefinedSymbols = false;
96
+ const add = (d) => {
96
97
  const key = dedupeKey(d);
97
98
  if (seen.has(key)) return;
98
99
  seen.add(key);
99
100
  out.push(d);
100
101
  };
101
- for (let i = 0; i < lines.length; i += 1) {
102
- const rawLine = lines[i];
103
- if (rawLine === void 0) continue;
104
- const raw = rawLine.replace(/\r$/, "");
105
- if (raw.includes(BUILD_FAILED)) break;
106
- if (UNDEFINED_HEADER.test(raw)) {
107
- for (let j = i + 1; j < lines.length; j += 1) {
108
- const symLine = lines[j];
109
- if (symLine === void 0) continue;
110
- const sym = UNDEFINED_SYMBOL.exec(symLine.replace(/\r$/, ""));
111
- if (sym) {
112
- const symbol = sym[1];
113
- if (symbol !== void 0) push(makeDiagnostic({ message: undefinedSymbolMessage(symbol) }, root, sdk));
114
- continue;
115
- }
116
- if (/^\s+\S/.test(symLine) && !/^\S/.test(symLine)) continue;
117
- i = j - 1;
118
- break;
102
+ const push = (line) => {
103
+ if (finished) return;
104
+ const raw = line.replace(/\r$/, "");
105
+ if (inUndefinedSymbols) {
106
+ const sym = UNDEFINED_SYMBOL.exec(raw);
107
+ if (sym) {
108
+ const symbol = sym[1];
109
+ if (symbol !== void 0) add(makeDiagnostic({ message: undefinedSymbolMessage(symbol) }, root, sdk));
110
+ return;
119
111
  }
120
- continue;
112
+ if (/^\s+\S/.test(line) && !/^\S/.test(line)) return;
113
+ inUndefinedSymbols = false;
114
+ }
115
+ if (raw.includes(BUILD_FAILED)) {
116
+ finished = true;
117
+ return;
118
+ }
119
+ if (UNDEFINED_HEADER.test(raw)) {
120
+ inUndefinedSymbols = true;
121
+ return;
121
122
  }
122
123
  const ld = LD_ERROR.exec(raw);
123
124
  if (ld) {
124
- push(makeDiagnostic({ message: `ld: ${ld[1]}` }, root, sdk));
125
- continue;
125
+ add(makeDiagnostic({ message: `ld: ${ld[1]}` }, root, sdk));
126
+ return;
126
127
  }
127
128
  const positioned = POSITIONED.exec(raw);
128
129
  if (positioned) {
129
130
  const posMsg = positioned[4];
130
- if (posMsg === void 0) continue;
131
- push(makeDiagnostic({
131
+ if (posMsg === void 0) return;
132
+ add(makeDiagnostic({
132
133
  file: positioned[1],
133
134
  line: Number(positioned[2]),
134
135
  column: positioned[3] === void 0 ? null : Number(positioned[3]),
135
136
  message: posMsg
136
137
  }, root, sdk));
137
- continue;
138
+ return;
138
139
  }
139
140
  const plain = UNPOSITIONED.exec(raw);
140
141
  if (plain) {
141
142
  const plainMsg = plain[2];
142
- if (plainMsg === void 0) continue;
143
- push(makeDiagnostic({
143
+ if (plainMsg === void 0) return;
144
+ add(makeDiagnostic({
144
145
  file: fileFromPrefix(plain[1] || ""),
145
146
  message: plainMsg
146
147
  }, root, sdk));
147
148
  }
148
- }
149
- return out;
149
+ };
150
+ return {
151
+ push,
152
+ diagnostics: out
153
+ };
150
154
  }
151
155
  function capDiagnostics(diagnostics, max = 10) {
152
156
  const list = Array.isArray(diagnostics) ? diagnostics : [];
@@ -553,7 +557,8 @@ function readBundleExecutable(appPath, { exec = null } = {}) {
553
557
  return null;
554
558
  }
555
559
  }
556
- function tailLines(lines, count = 5) {
560
+ const TAIL_LINES = 5;
561
+ function tailLines(lines, count = TAIL_LINES) {
557
562
  return (Array.isArray(lines) ? lines : []).filter((l) => typeof l === "string" && l.trim() !== "").slice(-count);
558
563
  }
559
564
  const HEARTBEAT_INTERVAL_MS = 3e4;
@@ -714,13 +719,18 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
714
719
  msg: `xcodebuild ${args.join(" ")}`,
715
720
  event: "build_start"
716
721
  });
717
- const transcript = [];
722
+ const diagnosticCollector = createXcodeDiagnosticCollector(root, sdk);
723
+ const tail = [];
724
+ let transcriptLines = 0;
718
725
  let compilationCacheActivity = COMPILATION_CACHE_UNAVAILABLE;
719
726
  let swiftTargetsWithoutExplicitModules = 0;
720
727
  const onLine = (line) => {
721
728
  const msg = cleanLine(line);
722
- transcript.push(msg);
729
+ transcriptLines += 1;
730
+ diagnosticCollector.push(msg);
723
731
  if (msg.trim() === "") return;
732
+ tail.push(msg);
733
+ if (tail.length > TAIL_LINES) tail.shift();
724
734
  logWriter.write({
725
735
  src: "build",
726
736
  level: "debug",
@@ -748,7 +758,7 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
748
758
  };
749
759
  let child;
750
760
  try {
751
- child = executor.spawn("xcodebuild", args, {
761
+ child = spawnDeclared(() => executor.spawn("xcodebuild", args, {
752
762
  cwd: resolvedTarget.dir || root,
753
763
  stdio: [
754
764
  "ignore",
@@ -761,7 +771,7 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
761
771
  NSUnbufferedIO: "YES",
762
772
  FORCE_COLOR: "0"
763
773
  }
764
- });
774
+ }));
765
775
  } catch (err) {
766
776
  const message = `Could not run xcodebuild: ${err.message}`;
767
777
  const remedy = "Install Xcode and select it with `sudo xcode-select -s /Applications/Xcode.app`.";
@@ -808,7 +818,6 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
808
818
  });
809
819
  stopHeartbeat();
810
820
  const durationMs = elapsed();
811
- const text = transcript.join("\n");
812
821
  if (outcome.error) {
813
822
  const message = `Could not run xcodebuild: ${outcome.error.message}`;
814
823
  const remedy = "Install Xcode and select it with `sudo xcode-select -s /Applications/Xcode.app`.";
@@ -820,13 +829,13 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
820
829
  remedy
821
830
  }],
822
831
  durationMs,
823
- transcriptLines: transcript.length,
824
- tail: tailLines(transcript),
832
+ transcriptLines,
833
+ tail,
825
834
  compilationCache: compilationCacheActivity
826
835
  });
827
836
  }
828
837
  if (outcome.code !== 0) {
829
- const diagnostics = extractXcodeDiagnostics(text, root, sdk);
838
+ const diagnostics = diagnosticCollector.diagnostics;
830
839
  const capped = capDiagnostics(diagnostics);
831
840
  for (const d of capped.diagnostics) reportError(describeDiagnostic(d), d.remedy);
832
841
  if (capped.diagnostics.length === 0) reportError(`xcodebuild exited ${outcome.code ?? `on ${outcome.signal}`} with no recognizable diagnostic; see the transcript above.`, null);
@@ -835,8 +844,8 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
835
844
  diagnostics,
836
845
  durationMs,
837
846
  exitCode: outcome.code,
838
- transcriptLines: transcript.length,
839
- tail: tailLines(transcript),
847
+ transcriptLines,
848
+ tail,
840
849
  compilationCache: compilationCacheActivity
841
850
  });
842
851
  }
@@ -889,8 +898,8 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
889
898
  }],
890
899
  durationMs,
891
900
  exitCode: 0,
892
- transcriptLines: transcript.length,
893
- tail: tailLines(transcript),
901
+ transcriptLines,
902
+ tail,
894
903
  compilationCache: compilationCacheActivity
895
904
  });
896
905
  }
@@ -907,8 +916,8 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
907
916
  }],
908
917
  durationMs,
909
918
  exitCode: 0,
910
- transcriptLines: transcript.length,
911
- tail: tailLines(transcript),
919
+ transcriptLines,
920
+ tail,
912
921
  compilationCache: compilationCacheActivity
913
922
  });
914
923
  }
@@ -927,7 +936,7 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
927
936
  project: resolvedTarget,
928
937
  derivedDataPath: dd,
929
938
  productsDir: products,
930
- transcriptLines: transcript.length,
939
+ transcriptLines,
931
940
  compilationCache: compilationCacheActivity
932
941
  };
933
942
  }