stim 1.5.0 → 1.7.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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/{android-DJWiTBkz.mjs → android-DWssqHQ8.mjs} +165 -661
  3. package/dist/{android-sobvO9ia.mjs → android-Da-G_o3n.mjs} +646 -548
  4. package/dist/{android-BW8i_YxU.mjs → android-DeYdjjBP.mjs} +6 -2
  5. package/dist/{android-cas-CSS8gjR7.mjs → android-cas-SWIjzeuL.mjs} +54 -18
  6. package/dist/android-cas-compiler.mjs +1 -1
  7. package/dist/{app-install--PAocWvq.mjs → app-install-DJg7bVv-.mjs} +36 -10
  8. package/dist/build-lock-CgtsB6TV.mjs +604 -0
  9. package/dist/{build-slots-CuPszJDu.mjs → build-slots-C-oAHRT1.mjs} +2 -2
  10. package/dist/{cache-manifest-uoOKhgUD.mjs → cache-manifest-CYJi8-Hx.mjs} +2 -2
  11. package/dist/cache-manifest.d.mts +1 -1
  12. package/dist/cache-manifest.mjs +1 -1
  13. package/dist/cli.mjs +14 -14
  14. package/dist/collector-run.d.mts +2 -0
  15. package/dist/collector-run.mjs +7 -7
  16. package/dist/{command-output-BuIb_ZOI.mjs → command-output-B3ycpsYv.mjs} +2 -2
  17. package/dist/{config-DMP7L7H6.mjs → config-B7iyKWkL.mjs} +56 -16
  18. package/dist/{dependency-state-CTL8A9p3.mjs → dependency-state-D0M7oKA7.mjs} +2 -2
  19. package/dist/{deps-BAGOnrc7.mjs → deps-oCXVNEqQ.mjs} +6 -6
  20. package/dist/detached-entry-Bpo6ZY_I.mjs +65 -0
  21. package/dist/{dev-client-BoifdqkU.mjs → dev-client-DDUkWgJL.mjs} +12 -12
  22. package/dist/{device-BUw4JBuH.mjs → device-CBSaC6BU.mjs} +7 -7
  23. package/dist/{device-DvKJ454A.mjs → device-CLeDp-KH.mjs} +17 -15
  24. package/dist/{device-lease-r7yEQ1R8.mjs → device-lease-Ffz_M5t0.mjs} +3 -3
  25. package/dist/{device-pool-DU4FQKp5.mjs → device-pool-DNSHL5z8.mjs} +4 -4
  26. package/dist/{device-remote-BTUqbl0i.mjs → device-remote-BprxZp-m.mjs} +11 -11
  27. package/dist/{doctor-Di_B26uh.mjs → doctor-CTAx9gMu.mjs} +90 -46
  28. package/dist/{error-diagnostics-CTePWAzW.mjs → error-diagnostics-D1Kaf-eT.mjs} +23 -17
  29. package/dist/{exec-CyylIdq9.mjs → exec-B29XNuhP.mjs} +14 -2
  30. package/dist/{gc-C20dWw_8.mjs → gc-CX0KRfLG.mjs} +17 -16
  31. package/dist/{guide-sAnoty2y.mjs → guide-CPmAG9me.mjs} +61 -22
  32. package/dist/{guide-status-BF6RsJJp.mjs → guide-status-Craq9z0t.mjs} +3 -3
  33. package/dist/{ios-BdpkncL6.mjs → ios-BRXYSxSc.mjs} +34 -37
  34. package/dist/{ios-device-DJOi9FR5.mjs → ios-device-CDbvn5H2.mjs} +2 -2
  35. package/dist/{ios-device-D3pzTKQE.mjs → ios-device-CFXA46ez.mjs} +1 -1
  36. package/dist/{ios-Deh5js5V.mjs → ios-lEJ5pKsq.mjs} +3 -3
  37. package/dist/{logs-BfQdIhiF.mjs → logs-CakdBTtm.mjs} +6 -6
  38. package/dist/{logs-query-Cdp5aaCH.mjs → logs-query-BGL-Pl12.mjs} +1 -1
  39. package/dist/metro-C7bcJ0uo.mjs +233 -0
  40. package/dist/{named-ports-DH9SXd0f.mjs → named-ports-DzMOlmaX.mjs} +57 -42
  41. package/dist/{ownership-CD_XnpDu.mjs → ownership-Cp52QAK1.mjs} +3 -3
  42. package/dist/{ownership-CVY9cqCr.mjs → ownership-D5vkdm8H.mjs} +13 -13
  43. package/dist/{ports-A6uTJSPj.mjs → ports-BIbJaKNW.mjs} +3 -3
  44. package/dist/{process-output-C7IlIzd7.mjs → process-output-Dl5uKE03.mjs} +1 -1
  45. package/dist/{project-B_SDt4ip.mjs → project-DDZWf_16.mjs} +3 -3
  46. package/dist/{reclaim-C_rB25f4.mjs → reclaim-D614jI6_.mjs} +11 -11
  47. package/dist/{reload-CYBJbJfI.mjs → reload-ChW46VDb.mjs} +9 -9
  48. package/dist/{server-bare-H3pAhbMs.mjs → server-bare-BQkyxa_b.mjs} +1 -1
  49. package/dist/{server-expo-Ds8_mfng.mjs → server-expo-BVAA1OqJ.mjs} +23 -11
  50. package/dist/server-expo-YDDG3eP7.mjs +2 -0
  51. package/dist/{settings-kO2ZqIKy.mjs → settings-BXXorVM2.mjs} +8 -5
  52. package/dist/{slot-launch-BpHiHH6A.mjs → slot-launch-DbLvyrup.mjs} +4 -4
  53. package/dist/{start-DiEfqEWk.mjs → start-TTVILlUi.mjs} +111 -38
  54. package/dist/{state-CnWzvCWP.mjs → state-DZobzxLW.mjs} +2 -2
  55. package/dist/{stats-Fo9ACIv7.mjs → stats-BYcZ6n1h.mjs} +1 -1
  56. package/dist/{stats-DHrO0frH.mjs → stats-CLg5RuDS.mjs} +4 -4
  57. package/dist/{status-J5LAXpag.mjs → status-DUNy_HZs.mjs} +14 -15
  58. package/dist/{stop-CM1fejKW.mjs → stop-CnUpnFT5.mjs} +2 -2
  59. package/dist/{stop-0jJNahwN.mjs → stop-ZrfODZ51.mjs} +13 -13
  60. package/dist/supervisor-run.mjs +7 -5
  61. package/dist/{teardown-CAVcWeRD.mjs → teardown-5J5UXz2P.mjs} +14 -17
  62. package/dist/{workspace-process-lock-Bz19_R9R.mjs → workspace-process-lock-BYfZz_S-.mjs} +1 -1
  63. package/dist/{workspace-state-WVwWw_hB.mjs → workspace-state-BT5E3SA2.mjs} +3 -3
  64. package/dist/{worktree-CeUKDAQw.mjs → worktree-BfE4lzHK.mjs} +21 -16
  65. package/dist/{worktree-DJnBbQZ9.mjs → worktree-FG_ZKKdb.mjs} +13 -9
  66. package/dist/{xcode-BqDeRkpT.mjs → xcode-B6YaWeg0.mjs} +92 -17
  67. package/package.json +12 -8
  68. package/shim/android-optimizations.gradle +6 -0
  69. package/dist/metro-ato6ef3J.mjs +0 -99
  70. package/dist/ports-BZprTJRa.mjs +0 -90
  71. package/dist/server-expo-CSwUVYOa.mjs +0 -2
@@ -1,10 +1,11 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { r as register } from "./cache-manifest-uoOKhgUD.mjs";
3
- import { d as sharedCompilationCache, m as workspaceDerivedData } from "./workspace-state-WVwWw_hB.mjs";
4
- import { t as createLineReader } from "./process-output-C7IlIzd7.mjs";
5
- import { a as formatElapsed, l as phaseLine } from "./command-output-BuIb_ZOI.mjs";
6
- import { U as resolveOptimizations } from "./settings-kO2ZqIKy.mjs";
7
- import { t as cleanLine } from "./server-expo-Ds8_mfng.mjs";
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";
8
9
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
9
10
  import { createHash } from "node:crypto";
10
11
  import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -332,18 +333,73 @@ function readPodfileProperties(root) {
332
333
  return null;
333
334
  }
334
335
  }
335
- function compilationCacheSettings({ workspaceRoot, derivedDataPath, casPath, xcodeMajor, ccache = false, optimizations = resolveOptimizations({}, {}).ios }) {
336
+ const SWIFT_PREFIX_MAPPING_MIN_SWIFT = {
337
+ major: 6,
338
+ minor: 4
339
+ };
340
+ function swiftPrefixMappingSupported(version) {
341
+ if (!version) return false;
342
+ const min = SWIFT_PREFIX_MAPPING_MIN_SWIFT;
343
+ return version.major > min.major || version.major === min.major && version.minor >= min.minor;
344
+ }
345
+ const SWIFT_CACHE_MIN_REACT_NATIVE = {
346
+ major: 0,
347
+ minor: 87
348
+ };
349
+ function reactNativeSupportsSwiftCache(version) {
350
+ if (!version) return false;
351
+ const min = SWIFT_CACHE_MIN_REACT_NATIVE;
352
+ return version.major > min.major || version.major === min.major && version.minor >= min.minor;
353
+ }
354
+ function parseReactNativeVersion(packageJson) {
355
+ if (!packageJson || typeof packageJson !== "object") return null;
356
+ const raw = packageJson.version;
357
+ const m = typeof raw === "string" ? /^(\d+)\.(\d+)\./.exec(raw) : null;
358
+ if (!m || m[1] === void 0 || m[2] === void 0) return null;
359
+ return {
360
+ major: parseInt(m[1], 10),
361
+ minor: parseInt(m[2], 10)
362
+ };
363
+ }
364
+ function detectReactNativeVersion(root) {
365
+ const file = resolvePackageJson(root, "react-native");
366
+ if (!file) return null;
367
+ try {
368
+ return parseReactNativeVersion(JSON.parse(readFileSync(file, "utf-8")));
369
+ } catch {
370
+ return null;
371
+ }
372
+ }
373
+ function compilationCacheSettings({ workspaceRoot, derivedDataPath, casPath, xcodeMajor, swiftVersion = null, reactNativeVersion = null, ccache = false, optimizations = resolveOptimizations({}, {}).ios }) {
336
374
  if (xcodeMajor === null || xcodeMajor === void 0) return [];
337
375
  if (xcodeMajor < 26) return [];
338
376
  if (ccache) return [];
377
+ const swiftMappable = swiftPrefixMappingSupported(swiftVersion);
378
+ const auto = swiftMappable && reactNativeSupportsSwiftCache(reactNativeVersion);
379
+ const swift = optimizations.compilationCache && (optimizations.swiftCompilationCache ?? auto);
380
+ const mappings = optimizations.prefixMapping ? compilationPrefixMappings(workspaceRoot, derivedDataPath) : "";
339
381
  return [
340
382
  `COMPILATION_CACHE_ENABLE_CACHING=${optimizations.compilationCache ? "YES" : "NO"}`,
341
383
  `COMPILATION_CACHE_CAS_PATH=${casPath}`,
342
- `SWIFT_ENABLE_COMPILE_CACHE=${optimizations.compilationCache && optimizations.swiftCompilationCache ? "YES" : "NO"}`,
384
+ `SWIFT_ENABLE_COMPILE_CACHE=${swift ? "YES" : "NO"}`,
343
385
  `CLANG_ENABLE_PREFIX_MAPPING=${optimizations.prefixMapping ? "YES" : "NO"}`,
344
- `CLANG_OTHER_PREFIX_MAPPINGS=${optimizations.prefixMapping ? compilationPrefixMappings(workspaceRoot, derivedDataPath) : ""}`
386
+ `CLANG_OTHER_PREFIX_MAPPINGS=${mappings}`,
387
+ ...swift && swiftMappable && optimizations.prefixMapping ? ["SWIFT_ENABLE_PREFIX_MAPPING=YES", `SWIFT_OTHER_PREFIX_MAPPINGS=${mappings}`] : ["SWIFT_ENABLE_PREFIX_MAPPING=NO"]
345
388
  ];
346
389
  }
390
+ function parseSwiftVersion(output) {
391
+ const m = /\bSwift version (\d+)\.(\d+)/.exec(String(output || ""));
392
+ if (!m || m[1] === void 0 || m[2] === void 0) return null;
393
+ const major = parseInt(m[1], 10);
394
+ const minor = parseInt(m[2], 10);
395
+ return Number.isFinite(major) && Number.isFinite(minor) ? {
396
+ major,
397
+ minor
398
+ } : null;
399
+ }
400
+ function detectSwiftVersion(exec = null) {
401
+ return parseSwiftVersion((exec || getExecutor()).runQuiet("xcrun swift --version", { timeoutMs: 1e4 }));
402
+ }
347
403
  function parseXcodeMajor(output) {
348
404
  const m = /^Xcode\s+(\d+)/m.exec(String(output || ""));
349
405
  if (!m) return null;
@@ -356,12 +412,19 @@ function detectXcodeMajor(exec = null) {
356
412
  return parseXcodeMajor((exec || getExecutor()).runQuiet("xcodebuild -version", { timeoutMs: 1e4 }));
357
413
  }
358
414
  function resolveCompilationCacheSettings({ root, derivedDataPath, exec = null, casPath = sharedCompilationCache(), optimizations = resolveOptimizations({}, {}).ios, onNote = (line) => console.error(line) }) {
415
+ const xcodeMajor = detectXcodeMajor(exec);
416
+ const ccache = ccacheEnabled(readPodfileProperties(root));
417
+ const probe = xcodeMajor !== null && xcodeMajor >= 26 && !ccache && optimizations.compilationCache && optimizations.swiftCompilationCache !== false;
418
+ const swiftVersion = probe ? detectSwiftVersion(exec) : null;
419
+ const reactNativeVersion = probe ? detectReactNativeVersion(root) : null;
359
420
  const settings = compilationCacheSettings({
360
421
  workspaceRoot: root,
361
422
  derivedDataPath,
362
423
  casPath,
363
- xcodeMajor: detectXcodeMajor(exec),
364
- ccache: ccacheEnabled(readPodfileProperties(root)),
424
+ xcodeMajor,
425
+ swiftVersion,
426
+ reactNativeVersion,
427
+ ccache,
365
428
  optimizations
366
429
  });
367
430
  if (settings.length > 0 && optimizations.compilationCache) {
@@ -371,7 +434,8 @@ function resolveCompilationCacheSettings({ root, derivedDataPath, exec = null, c
371
434
  prune: "atomic",
372
435
  note: "shared Xcode compilation cache"
373
436
  });
374
- onNote(chalk.dim(phaseLine("cache", `compilation cache on (CAS at ${casPath})`)));
437
+ const swift = settings.includes("SWIFT_ENABLE_COMPILE_CACHE=YES") ? settings.includes("SWIFT_ENABLE_PREFIX_MAPPING=YES") ? "Swift on" : "Swift on, unmapped" : swiftVersion && !swiftPrefixMappingSupported(swiftVersion) ? `Swift off, ${swiftVersion.major}.${swiftVersion.minor} < ${SWIFT_PREFIX_MAPPING_MIN_SWIFT.major}.${SWIFT_PREFIX_MAPPING_MIN_SWIFT.minor}` : swiftVersion && reactNativeVersion && !reactNativeSupportsSwiftCache(reactNativeVersion) ? `Swift off, react-native ${reactNativeVersion.major}.${reactNativeVersion.minor} < ${SWIFT_CACHE_MIN_REACT_NATIVE.major}.${SWIFT_CACHE_MIN_REACT_NATIVE.minor}` : "Swift off";
438
+ onNote(chalk.dim(phaseLine("cache", `compilation cache on (CAS at ${casPath}, ${swift})`)));
375
439
  }
376
440
  return settings;
377
441
  }
@@ -567,8 +631,12 @@ function parseCompilationCacheActivity(line) {
567
631
  hitRatePercent: Number(match[3])
568
632
  };
569
633
  }
634
+ const SWIFT_CACHING_NEEDS_EXPLICIT_MODULES = /warning: swift compiler caching requires explicit module build/;
570
635
  function compilationCacheActivityLine(activity) {
571
- if (activity.status === "reported") return `${activity.hits}/${activity.cacheableTasks} hits (${activity.hitRatePercent}%)`;
636
+ if (activity.status === "reported") {
637
+ const swift = activity.swiftTargetsWithoutExplicitModules ? `; Swift excluded: ${activity.swiftTargetsWithoutExplicitModules} targets build without explicit modules (SWIFT_ENABLE_EXPLICIT_MODULES=NO)` : "";
638
+ return `${activity.hits}/${activity.cacheableTasks} hits (${activity.hitRatePercent}%)${swift}`;
639
+ }
572
640
  if (activity.status === "not-run") return "not run; artifact cache supplied the app";
573
641
  return "unavailable; Xcode did not report reliable statistics";
574
642
  }
@@ -648,6 +716,7 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
648
716
  });
649
717
  const transcript = [];
650
718
  let compilationCacheActivity = COMPILATION_CACHE_UNAVAILABLE;
719
+ let swiftTargetsWithoutExplicitModules = 0;
651
720
  const onLine = (line) => {
652
721
  const msg = cleanLine(line);
653
722
  transcript.push(msg);
@@ -657,8 +726,13 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
657
726
  level: "debug",
658
727
  msg
659
728
  });
660
- const activity = parseCompilationCacheActivity(msg);
661
- if (activity) {
729
+ if (SWIFT_CACHING_NEEDS_EXPLICIT_MODULES.test(msg)) swiftTargetsWithoutExplicitModules += 1;
730
+ const parsed = parseCompilationCacheActivity(msg);
731
+ if (parsed) {
732
+ const activity = swiftTargetsWithoutExplicitModules ? {
733
+ ...parsed,
734
+ swiftTargetsWithoutExplicitModules
735
+ } : parsed;
662
736
  compilationCacheActivity = activity;
663
737
  logWriter.write({
664
738
  src: "build",
@@ -667,7 +741,8 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
667
741
  event: "compilation_cache",
668
742
  hits: activity.hits,
669
743
  cacheableTasks: activity.cacheableTasks,
670
- hitRatePercent: activity.hitRatePercent
744
+ hitRatePercent: activity.hitRatePercent,
745
+ ...swiftTargetsWithoutExplicitModules ? { swiftTargetsWithoutExplicitModules } : {}
671
746
  });
672
747
  }
673
748
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stim",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Stim: fast, isolated React Native environments for coding agents",
5
5
  "keywords": [
6
6
  "agent",
@@ -42,20 +42,24 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@expo/fingerprint": "^0.20.10",
45
- "@stim-cli/cache": "^1.5.0",
46
- "@stim-cli/core": "^1.5.0",
47
- "@stim-cli/metro": "^1.5.0",
48
- "chalk": "^5.4.1",
49
- "commander": "^13.1.0",
45
+ "@stim-cli/cache": "^1.7.0",
46
+ "@stim-cli/core": "^1.7.0",
47
+ "@stim-cli/metro": "^1.7.0",
48
+ "chalk": "^6.0.0",
49
+ "commander": "^15.0.0",
50
+ "cross-spawn": "^7.0.6",
51
+ "which": "^5.0.0",
50
52
  "ws": "^8.21.3"
51
53
  },
52
54
  "devDependencies": {
53
- "@stim-cli/expo-build-cache": "1.5.0",
55
+ "@stim-cli/expo-build-cache": "1.7.0",
56
+ "@types/cross-spawn": "^6.0.6",
57
+ "@types/which": "^3.0.4",
54
58
  "@types/ws": "^8.18.1",
55
59
  "unique-pid": "0.1.1"
56
60
  },
57
61
  "engines": {
58
- "node": "^20.19.4 || >=22.12.0"
62
+ "node": ">=22.12.0"
59
63
  },
60
64
  "scripts": {
61
65
  "test": "node --test test/*.test.js",
@@ -7,6 +7,12 @@ gradle.beforeProject { project ->
7
7
  def profile = System.getenv("STIM_ANDROID_NATIVE_PROFILE")
8
8
  def staging = android.externalNativeBuild.cmake.buildStagingDirectory ?: new File(project.projectDir, ".cxx")
9
9
  android.externalNativeBuild.cmake.buildStagingDirectory = new File(staging, "stim-" + profile)
10
+ // Windows caps a path at 260 characters and the SDK's ninja is not long-path aware, so the
11
+ // staging directory added above has to buy its room back: CMake replaces the mangled absolute
12
+ // source directory in an over-long object path with a hash of it.
13
+ if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
14
+ android.defaultConfig.externalNativeBuild.cmake.arguments.add("-DCMAKE_OBJECT_PATH_MAX=240")
15
+ }
10
16
  def configs = [android.defaultConfig] + android.buildTypes.toList() + android.productFlavors.toList()
11
17
  def pch = System.getenv("STIM_ANDROID_PCH")
12
18
  def ccache = System.getenv("STIM_ANDROID_CCACHE")
@@ -1,99 +0,0 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { w as process_identity_exports } from "./config-DMP7L7H6.mjs";
3
- import { r as readWorkspaceState } from "./workspace-state-WVwWw_hB.mjs";
4
- import { t as isMetroRunning } from "./ports-BZprTJRa.mjs";
5
- import { readlinkSync, realpathSync } from "fs";
6
- import { sep } from "path";
7
- //#region src/metro.ts
8
- /**
9
- * True when some process with this pid exists right now. It cannot tell a recycled pid from the
10
- * process that first held it, so it is only valid for a pid this run spawned and still holds a
11
- * handle on. For a pid read from a record on disk, decide with `inspectProcessIdentity` on that
12
- * record's process token, or take an ownership claim (`ownership-claim.ts`): this answer would keep
13
- * a dead holder's record looking live until some unrelated process exits.
14
- */
15
- function pidExists(pid) {
16
- if (!Number.isSafeInteger(pid) || pid <= 0) return false;
17
- try {
18
- process.kill(pid, 0);
19
- return true;
20
- } catch (error) {
21
- return error.code !== "ESRCH";
22
- }
23
- }
24
- function parseLsofPids(out) {
25
- if (!out) return [];
26
- return String(out).split("\n").map((s) => parseInt(s.trim(), 10)).filter((n) => Number.isFinite(n));
27
- }
28
- function parseLsofCwd(out) {
29
- if (!out) return null;
30
- const lines = String(out).split("\n");
31
- const idx = lines.findIndex((l) => l === "fcwd");
32
- if (idx === -1) return null;
33
- const nLine = lines.slice(idx + 1).find((l) => l.startsWith("n"));
34
- return nLine ? nLine.slice(1) : null;
35
- }
36
- function processCwd(pid) {
37
- if (process.platform === "linux") try {
38
- return readlinkSync(`/proc/${pid}/cwd`);
39
- } catch {}
40
- return parseLsofCwd(getExecutor().runQuiet(`lsof -a -p ${pid} -d cwd -Fn`));
41
- }
42
- function canonicalPath(path) {
43
- try {
44
- return realpathSync(path);
45
- } catch {
46
- return path;
47
- }
48
- }
49
- function isInsideProject(cwd, projectPath) {
50
- if (!cwd || !projectPath) return false;
51
- const a = canonicalPath(cwd);
52
- const b = canonicalPath(projectPath);
53
- return a === b || a.startsWith(b.endsWith(sep) ? b : b + sep);
54
- }
55
- const NOT_OURS_UNRESPONSIVE = "unresponsive";
56
- const NOT_OURS_UNREADABLE_CWD = "unreadable-cwd";
57
- const NOT_OURS_FOREIGN_CWD = "foreign-cwd";
58
- async function resolveProjectMetro(port, projectPath, { probe = isMetroRunning } = {}) {
59
- const pid = parseLsofPids(getExecutor().runQuiet(`lsof -nP -iTCP:${port} -sTCP:LISTEN -t`))[0];
60
- if (pid === void 0) return { missing: true };
61
- if (!await probe(port)) return {
62
- notOurs: `pid ${pid} on port ${port} does not answer Metro's /status`,
63
- kind: NOT_OURS_UNRESPONSIVE,
64
- pid
65
- };
66
- const cwd = processCwd(pid);
67
- if (!cwd) return {
68
- notOurs: `pid ${pid} on port ${port}: working directory could not be read`,
69
- kind: NOT_OURS_UNREADABLE_CWD,
70
- pid
71
- };
72
- if (!isInsideProject(cwd, projectPath)) return {
73
- notOurs: `pid ${pid} on port ${port} runs from ${cwd}, outside ${projectPath}`,
74
- kind: NOT_OURS_FOREIGN_CWD,
75
- pid
76
- };
77
- const supervisor = readWorkspaceState(canonicalPath(projectPath))?.supervisor;
78
- const owned = supervisor?.port === port && (0, process_identity_exports.inspectProcessIdentity)(supervisor) === "same";
79
- return { metro: {
80
- pid,
81
- leader: owned ? supervisor.pid : pid,
82
- cwd,
83
- ...owned ? { processToken: supervisor.processToken } : {}
84
- } };
85
- }
86
- function killMetroTree(leader, processToken) {
87
- if (!leader || leader === process.pid || (0, process_identity_exports.inspectProcessIdentity)({
88
- pid: leader,
89
- processToken
90
- }) !== "same") return false;
91
- try {
92
- process.kill(-leader, "SIGTERM");
93
- return true;
94
- } catch {
95
- return false;
96
- }
97
- }
98
- //#endregion
99
- export { resolveProjectMetro as i, killMetroTree as n, pidExists as r, NOT_OURS_FOREIGN_CWD as t };
@@ -1,90 +0,0 @@
1
- import { A as isOnMountedVolume, g as loadConfig, j as listMountedVolumes, n as allMetroPorts, r as claimMetroPort, v as removeProject } from "./config-DMP7L7H6.mjs";
2
- import { existsSync } from "fs";
3
- import { request } from "http";
4
- import { connect } from "net";
5
- //#region src/ports.ts
6
- function isMetroRunning(port) {
7
- return new Promise((resolve) => {
8
- const req = request({
9
- hostname: "localhost",
10
- port,
11
- path: "/status",
12
- timeout: 2e3
13
- }, (res) => {
14
- let data = "";
15
- res.on("data", (chunk) => {
16
- data += chunk;
17
- });
18
- res.on("end", () => resolve(data.includes("packager-status:running")));
19
- });
20
- req.on("error", () => resolve(false));
21
- req.on("timeout", () => {
22
- req.destroy();
23
- resolve(false);
24
- });
25
- req.end();
26
- });
27
- }
28
- function isPortFree(port, { timeoutMs = 400 } = {}) {
29
- return new Promise((resolve) => {
30
- const sock = connect({
31
- port,
32
- host: "127.0.0.1"
33
- });
34
- const done = (free) => {
35
- sock.removeAllListeners();
36
- sock.destroy();
37
- resolve(free);
38
- };
39
- sock.setTimeout(timeoutMs);
40
- sock.once("connect", () => done(false));
41
- sock.once("error", () => done(true));
42
- sock.once("timeout", () => done(false));
43
- });
44
- }
45
- const FIRST_PORT = 8082;
46
- async function computeNextPort(isFree = isPortFree) {
47
- const taken = new Set(allMetroPorts());
48
- for (let port = FIRST_PORT; port < 8282; port++) {
49
- if (taken.has(port)) continue;
50
- if (await isFree(port)) return port;
51
- }
52
- 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>\`).`);
53
- }
54
- async function findReclaimablePort(excludeProjectPath, probe = isMetroRunning, { isMounted = isOnMountedVolume, mountedVolumes } = {}) {
55
- const cfg = loadConfig();
56
- if (!cfg?.projects) return null;
57
- const mounted = mountedVolumes || listMountedVolumes();
58
- const candidates = [];
59
- for (const [path, proj] of Object.entries(cfg.projects)) {
60
- if (path === excludeProjectPath) continue;
61
- if (Object.keys(proj.ports ?? {}).length) continue;
62
- if (existsSync(path)) continue;
63
- if (!isMounted(path, mounted)) continue;
64
- if (typeof proj.metroPort === "number") candidates.push({
65
- port: proj.metroPort,
66
- ownerPath: path
67
- });
68
- }
69
- for (const c of candidates) if (!await probe(c.port)) return c;
70
- return null;
71
- }
72
- async function allocatePort(projectPath, probe = isMetroRunning, isFree = isPortFree) {
73
- const reclaim = await findReclaimablePort(projectPath, probe);
74
- if (reclaim && await isFree(reclaim.port)) {
75
- removeProject(reclaim.ownerPath);
76
- return reclaim.port;
77
- }
78
- return computeNextPort(isFree);
79
- }
80
- const RESERVE_ATTEMPTS = 5;
81
- async function reserveMetroPort(projectPath, probe = isMetroRunning, isFree = isPortFree) {
82
- for (let attempt = 0; attempt < RESERVE_ATTEMPTS; attempt++) {
83
- const port = await allocatePort(projectPath, probe, isFree);
84
- const claimed = claimMetroPort(projectPath, port);
85
- if (claimed !== null) return claimed;
86
- }
87
- throw new Error(`Could not reserve a Metro port after ${RESERVE_ATTEMPTS} attempts: another Stim run claimed each one first. Retry.`);
88
- }
89
- //#endregion
90
- export { isPortFree as n, reserveMetroPort as r, isMetroRunning as t };
@@ -1,2 +0,0 @@
1
- import { p as startExpoServer } from "./server-expo-Ds8_mfng.mjs";
2
- export { startExpoServer };