stim 1.14.0 → 1.16.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 (152) hide show
  1. package/README.md +2 -0
  2. package/dist/activity-BxwdD81S.mjs +609 -0
  3. package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
  4. package/dist/{android-d6rGq1pA.mjs → android-B_NumVyQ.mjs} +169 -81
  5. package/dist/{android-LbPFRWVL.mjs → android-Ift4kVvt.mjs} +517 -413
  6. package/dist/android-cas-8U4DPqeR.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{android-XpvCrV7q.mjs → android-r2rBL8kV.mjs} +2 -2
  9. package/dist/{app-install-BpgSgkJj.mjs → app-install-CKWdbXIX.mjs} +128 -151
  10. package/dist/attempt-Bk2ouQVL.mjs +39 -0
  11. package/dist/budget-DjrURfQn.mjs +2700 -0
  12. package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-DjeSb2Xl.mjs} +192 -653
  13. package/dist/build-slots-rUk77xqh.mjs +132 -0
  14. package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  15. package/dist/cache-manifest.mjs +1 -1
  16. package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
  17. package/dist/cli-BkYZ_6hy.mjs +51 -0
  18. package/dist/cli.mjs +6 -42
  19. package/dist/client-BTY686R6.mjs +1368 -0
  20. package/dist/collector-run.mjs +9 -195
  21. package/dist/{command-output-DVcpw6eE.mjs → command-output-DnJMdEY-.mjs} +1 -1
  22. package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
  23. package/dist/{created-devices-CllH_8eb.mjs → created-devices-D5owjN4C.mjs} +2 -2
  24. package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-B6cMXZab.mjs} +2 -2
  25. package/dist/{deps-D9IcKqXS.mjs → deps-urbmfXig.mjs} +3 -3
  26. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  27. package/dist/dev-client-CroAIjjj.mjs +43 -0
  28. package/dist/{device-CjuuAJjQ.mjs → device-DZQjF7H5.mjs} +8 -7
  29. package/dist/device-capacity-BHwxNBlu.mjs +136 -0
  30. package/dist/device-host-worker.d.mts +1 -0
  31. package/dist/device-host-worker.mjs +964 -0
  32. package/dist/device-ios-BadmeL2V.mjs +381 -0
  33. package/dist/{device-lease-5c3WrVRm.mjs → device-lease-D-hLqx1b.mjs} +43 -36
  34. package/dist/{device-pool-BiyDyPP-.mjs → device-lease-run-CqjShqWW.mjs} +6 -126
  35. package/dist/device-pool-CUaEWMMm.mjs +125 -0
  36. package/dist/{device-remote-D87zEzaq.mjs → device-remote-D5WVWqOq.mjs} +69 -163
  37. package/dist/{doctor-x9pnU9fq.mjs → doctor-Cimf-gGB.mjs} +241 -103
  38. package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-B9L4Ae0x.mjs} +15 -18
  39. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  40. package/dist/{gc-ko6mUAYD.mjs → gc-Bqy4bjEu.mjs} +599 -108
  41. package/dist/{android-cas-iZK3DOCr.mjs → gradle-BsO1MdsH.mjs} +8 -294
  42. package/dist/{guide-6R0VNFqI.mjs → guide-B_uyKTxW.mjs} +2511 -239
  43. package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CjEZMAD4.mjs} +3 -3
  44. package/dist/hosted-client-a-WmqE2L.mjs +197 -0
  45. package/dist/hosted-ios-CIWelIqf.mjs +220 -0
  46. package/dist/hosted-logs-D5g041Q3.mjs +187 -0
  47. package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
  48. package/dist/idle-DYVymG92.mjs +1099 -0
  49. package/dist/idle-shutdown-CTsDnAde.mjs +122 -0
  50. package/dist/in-use-DvBw0Kxo.mjs +130 -0
  51. package/dist/{ios-BwDPrnYQ.mjs → ios-BaInn3sW.mjs} +201 -10
  52. package/dist/ios-Cr1h5gnO.mjs +189 -0
  53. package/dist/{ios-P1f5nm2n.mjs → ios-aq3QoGI3.mjs} +1159 -633
  54. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  55. package/dist/{ios-device-DGglU3E_.mjs → ios-device-FfCN_C8p.mjs} +12 -7
  56. package/dist/ios-state-DS5UpYTH.mjs +48 -0
  57. package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-2DBRizjo.mjs} +5 -5
  58. package/dist/logs-CHEVK8zD.mjs +371 -0
  59. package/dist/machines-BIBUdhpj.mjs +265 -0
  60. package/dist/macos-CSYEAFmF.mjs +473 -0
  61. package/dist/macos-run.d.mts +5 -0
  62. package/dist/macos-run.mjs +2 -0
  63. package/dist/maintenance-run.d.mts +4 -0
  64. package/dist/maintenance-run.mjs +254 -0
  65. package/dist/{metro-CcEo8Fl8.mjs → metro-BeK1ZPz1.mjs} +20 -120
  66. package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
  67. package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Bk_YXEmE.mjs} +6 -59
  68. package/dist/native-run-NaBrRFtQ.mjs +147 -0
  69. package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-DBzRnEKS.mjs} +9 -5
  70. package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
  71. package/dist/offload-worker.d.mts +122 -0
  72. package/dist/offload-worker.mjs +606 -0
  73. package/dist/{ownership-DyYDF5ww.mjs → ownership-DiA16AsN.mjs} +3 -2
  74. package/dist/ownership-hCkPzGmr.mjs +571 -0
  75. package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
  76. package/dist/{ports-BguS83ex.mjs → ports-R2k4Knz3.mjs} +3 -3
  77. package/dist/prebuild-D2fsca_Z.mjs +213 -0
  78. package/dist/preview-DhVbvPAZ.mjs +318 -0
  79. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  80. package/dist/{project-CWlvEaeN.mjs → project-Du91Ilvc.mjs} +74 -19
  81. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  82. package/dist/pull-requests.d.mts +120 -0
  83. package/dist/pull-requests.mjs +2 -0
  84. package/dist/recordings-BIXOzVnr.mjs +37 -0
  85. package/dist/{reload-BiniwsSZ.mjs → reload-rM9jV1fz.mjs} +109 -24
  86. package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-C8-V13XN.mjs} +5 -5
  87. package/dist/run-f4vI6EnK.mjs +119 -0
  88. package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-CpPGmnmO.mjs} +33 -15
  89. package/dist/server-expo-CVqO2c35.mjs +2 -0
  90. package/dist/{server-expo-BVuwQ65v.mjs → server-expo-MC0rHlOC.mjs} +8 -12
  91. package/dist/settings-BBpMcDbD.mjs +25 -0
  92. package/dist/{settings-B-fsC-Om.mjs → settings-ZkDzkzvB.mjs} +22 -10
  93. package/dist/settings-oTSkkNrM.mjs +1439 -0
  94. package/dist/settings.schema.json +845 -97
  95. package/dist/{simslim-DrDN4WuV.mjs → simslim-B8-7AoTI.mjs} +8 -132
  96. package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-B8VrHxn1.mjs} +11 -8
  97. package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  98. package/dist/spawn-entry-jw19m8Nl.mjs +17 -0
  99. package/dist/stage-CzaiwFsI.mjs +143 -0
  100. package/dist/start-CX8Ig-U2.mjs +2 -0
  101. package/dist/{start-s4RXp1wy.mjs → start-ClDfwXa4.mjs} +116 -65
  102. package/dist/state-3DwSQIBK.mjs +41 -0
  103. package/dist/state-B4PQFXMW.d.mts +12 -0
  104. package/dist/{state-Dk9RVXI7.mjs → state-C1jB7Q8q.mjs} +70 -8
  105. package/dist/{state-DHqvsbqC.mjs → state-DcW6vOd_.mjs} +7 -4
  106. package/dist/stats-CEZ_i5je.mjs +77 -0
  107. package/dist/{status-BgAuxESg.mjs → status-BuvBFeII.mjs} +118 -20
  108. package/dist/{status-BsPAnfOI.mjs → status-Cs7VLg9D.mjs} +542 -283
  109. package/dist/status-DlKvVJBE.mjs +49 -0
  110. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  111. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  112. package/dist/stim-footprint.swift +32 -0
  113. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  114. package/dist/stop-B0Yxtmiy.mjs +48 -0
  115. package/dist/{stop-BzreOWJp.mjs → stop-Bffozsac.mjs} +83 -16
  116. package/dist/{stop-BWIF_6BK.mjs → stop-Dt_dM16A.mjs} +2 -2
  117. package/dist/stop-cause-CidxIqup.mjs +97 -0
  118. package/dist/supervisor-run.d.mts +43 -11
  119. package/dist/supervisor-run.mjs +363 -24
  120. package/dist/support-CAP8qDUF.mjs +316 -0
  121. package/dist/support-D4aYJniO.mjs +201 -0
  122. package/dist/teardown-CL6vDs2Y.mjs +727 -0
  123. package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DkUflduf.mjs} +364 -14
  124. package/dist/trigger-Bemu8ytw.mjs +65 -0
  125. package/dist/trigger-CXts6qt1.mjs +2 -0
  126. package/dist/warm-progress-CpGv3Wb6.mjs +732 -0
  127. package/dist/{web-BsQs1WlO.mjs → web-DWUaMp8P.mjs} +37 -21
  128. package/dist/web-run.mjs +310 -13
  129. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  130. package/dist/workspace-state-B8-aJ1_5.mjs +371 -0
  131. package/dist/workspaces-BV4pdHFz.mjs +306 -0
  132. package/dist/{worktree-UrQWXSeb.mjs → worktree-K-xj9Db0.mjs} +625 -256
  133. package/dist/worktree-jnG8Iph5.mjs +2 -0
  134. package/package.json +10 -5
  135. package/shim/bundle-response.cjs +28 -12
  136. package/dist/activity-CYVDcrt3.mjs +0 -356
  137. package/dist/budget-CGDkhvv_.mjs +0 -1745
  138. package/dist/build-progress-DxKpwnaS.mjs +0 -247
  139. package/dist/idle-DXtx3RbX.mjs +0 -191
  140. package/dist/in-use-DlqBDKqY.mjs +0 -249
  141. package/dist/logs-Civ4gbBH.mjs +0 -486
  142. package/dist/merge-state-ytFQCeh4.mjs +0 -262
  143. package/dist/server-expo-DIHDhvGg.mjs +0 -2
  144. package/dist/settings-D61iNOsB.mjs +0 -629
  145. package/dist/start-Cetfvy8l.mjs +0 -2
  146. package/dist/stats-CFpFGlJV.mjs +0 -62
  147. package/dist/stats-CX1Z8Tca.mjs +0 -368
  148. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  149. package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
  150. package/dist/worktree-BVrHsYZG.mjs +0 -752
  151. package/dist/worktree-CHWWvm5v.mjs +0 -2
  152. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -0,0 +1,167 @@
1
+ import { d as getConfigDir } from "./config-D0DbpZtM.mjs";
2
+ import { r as register } from "./cache-manifest-DnUHBJaU.mjs";
3
+ import { u as ensureWorkspaceStorage, w as withDirLock } from "./workspace-state-B8-aJ1_5.mjs";
4
+ import { createHash, randomUUID } from "node:crypto";
5
+ import { accessSync, constants, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, symlinkSync, writeFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { isJsonObject, readJsonFile } from "@stim-cli/core/state";
8
+ import { fileURLToPath } from "node:url";
9
+ //#region src/engine/android-cas.ts
10
+ const CAS_TOOLCHAIN_FIELDS = [
11
+ "clang",
12
+ "clangxx",
13
+ "lld",
14
+ "ar",
15
+ "ranlib",
16
+ "ndk",
17
+ "resourceDir"
18
+ ];
19
+ const CAS_TOOLCHAIN_BINARIES = [
20
+ "clang",
21
+ "clangxx",
22
+ "lld",
23
+ "ar",
24
+ "ranlib"
25
+ ];
26
+ function executableFile(path) {
27
+ try {
28
+ accessSync(path, constants.X_OK);
29
+ return statSync(path).isFile();
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+ function directory(path) {
35
+ try {
36
+ return statSync(path).isDirectory();
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+ function isAndroidCasToolchain(value) {
42
+ return CAS_TOOLCHAIN_FIELDS.every((field) => typeof value[field] === "string");
43
+ }
44
+ function readAndroidCasToolchain(manifest) {
45
+ const parsed = readJsonFile(manifest);
46
+ const toolchain = isJsonObject(parsed) ? parsed : {};
47
+ if (!isAndroidCasToolchain(toolchain)) {
48
+ const missing = CAS_TOOLCHAIN_FIELDS.filter((field) => typeof toolchain[field] !== "string");
49
+ throw new Error(`${manifest} declares no ${missing.join(", ")}.`);
50
+ }
51
+ const notExecutable = CAS_TOOLCHAIN_BINARIES.filter((field) => !executableFile(toolchain[field]));
52
+ if (notExecutable.length > 0) throw new Error(`${manifest} names no executable ${notExecutable.join(", ")}.`);
53
+ if (!directory(toolchain.resourceDir)) throw new Error(`${manifest} names resourceDir ${toolchain.resourceDir}, which is not a directory.`);
54
+ const ndkProperties = readFileSync(join(toolchain.ndk, "source.properties"), "utf8");
55
+ const ndkVersion = /Pkg.Revision\s*=\s*([^\r\n]+)/.exec(ndkProperties)?.[1]?.trim();
56
+ if (!ndkVersion) throw new Error(`Missing NDK version in ${toolchain.ndk}/source.properties.`);
57
+ return {
58
+ toolchain,
59
+ ndkVersion,
60
+ ndkProperties
61
+ };
62
+ }
63
+ function resolveAndroidCompilerCache({ optimizations, use, env = process.env }) {
64
+ if (optimizations.android.compilerCache !== "cas") return {
65
+ cas: null,
66
+ optimizations
67
+ };
68
+ const manifest = optimizations.android.casToolchain;
69
+ try {
70
+ return {
71
+ cas: use(manifest),
72
+ optimizations
73
+ };
74
+ } catch (error) {
75
+ const fromEnvironment = Boolean(env.STIM_ANDROID_CAS_TOOLCHAIN);
76
+ return {
77
+ cas: null,
78
+ optimizations: {
79
+ ...optimizations,
80
+ android: {
81
+ ...optimizations.android,
82
+ compilerCache: "ccache",
83
+ compilerCacheFallback: {
84
+ key: fromEnvironment ? "STIM_ANDROID_CAS_TOOLCHAIN" : "optimizations.android.casToolchain",
85
+ reason: `could not be used: ${error.message.replace(/\.$/, "")}`,
86
+ fromEnvironment,
87
+ manifest
88
+ }
89
+ }
90
+ }
91
+ };
92
+ }
93
+ }
94
+ function resolveAndroidCas(root, env = process.env) {
95
+ const manifest = env.STIM_ANDROID_CAS_TOOLCHAIN;
96
+ if (!manifest) return null;
97
+ const { toolchain, ndkVersion, ndkProperties } = readAndroidCasToolchain(manifest);
98
+ const scripts = [
99
+ "shim/android-cas.gradle",
100
+ "shim/android-cas.toolchain.cmake",
101
+ "shim/android-cas-pch.cmake",
102
+ "dist/android-cas-compiler.mjs"
103
+ ].map((name) => {
104
+ const path = [`../${name}`, `../../${name}`].map((candidate) => fileURLToPath(new URL(candidate, import.meta.url))).find((candidate) => existsSync(candidate));
105
+ if (!path) throw new Error(`Stim installation is missing ${name}.`);
106
+ return path;
107
+ });
108
+ const hash = createHash("sha256").update(JSON.stringify(toolchain));
109
+ for (const path of [
110
+ ...scripts,
111
+ toolchain.clang,
112
+ toolchain.clangxx,
113
+ toolchain.lld,
114
+ toolchain.ar,
115
+ toolchain.ranlib
116
+ ]) hash.update(readFileSync(path));
117
+ hash.update(ndkProperties);
118
+ const id = `apple-cas-${hash.digest("hex")}`;
119
+ const dir = join(getConfigDir(), "android-cas", id);
120
+ const state = join(ensureWorkspaceStorage(root), "android-cas", id);
121
+ const context = join(state, "context.json");
122
+ mkdirSync(state, { recursive: true });
123
+ mkdirSync(dir, { recursive: true });
124
+ withDirLock(join(state, "setup.lock"), () => {
125
+ for (const name of ["clang", "clang++"]) {
126
+ const path = join(state, name);
127
+ const temporary = `${path}.${randomUUID()}.tmp`;
128
+ symlinkSync(scripts[3], temporary);
129
+ renameSync(temporary, path);
130
+ }
131
+ const temporary = `${context}.${process.pid}.tmp`;
132
+ writeFileSync(temporary, JSON.stringify({
133
+ ...toolchain,
134
+ source: realpathSync(root),
135
+ state,
136
+ cache: dir
137
+ }));
138
+ renameSync(temporary, context);
139
+ });
140
+ register({
141
+ dir,
142
+ name: "android-cas",
143
+ prune: "atomic",
144
+ note: "Experimental Android compiler CAS"
145
+ });
146
+ return {
147
+ id,
148
+ dir,
149
+ initScript: scripts[0],
150
+ env: {
151
+ STIM_ANDROID_CAS_CONTEXT: context,
152
+ STIM_ANDROID_CAS_SOURCE_ROOT: realpathSync(root),
153
+ STIM_ANDROID_CAS_STATE: state,
154
+ STIM_ANDROID_CAS_NDK: toolchain.ndk,
155
+ STIM_ANDROID_CAS_NDK_VERSION: ndkVersion,
156
+ STIM_ANDROID_CAS_AR: toolchain.ar,
157
+ STIM_ANDROID_CAS_RANLIB: toolchain.ranlib,
158
+ STIM_ANDROID_CAS_CMAKE_TOOLCHAIN: scripts[1],
159
+ STIM_ANDROID_CAS_CMAKE_PCH: scripts[2],
160
+ CMAKE_C_COMPILER_LAUNCHER: "",
161
+ CMAKE_CXX_COMPILER_LAUNCHER: "",
162
+ CCACHE_DISABLE: "1"
163
+ }
164
+ };
165
+ }
166
+ //#endregion
167
+ export { resolveAndroidCas as n, resolveAndroidCompilerCache as r, readAndroidCasToolchain as t };
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
2
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
3
+ import { createHash, randomUUID } from "node:crypto";
3
4
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
4
5
  import { join } from "node:path";
5
- import { createHash, randomUUID } from "node:crypto";
6
6
  import { once } from "node:events";
7
7
  //#region bin/android-cas-compiler.ts
8
8
  const context = process.env.STIM_ANDROID_CAS_CONTEXT;
@@ -1,5 +1,5 @@
1
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
2
- import { l as androidToolCwd } from "./android-d6rGq1pA.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { u as androidToolCwd } from "./android-B_NumVyQ.mjs";
3
3
  //#region src/collector/android.ts
4
4
  const LEVEL_BY_LETTER = {
5
5
  V: "debug",
@@ -1,136 +1,47 @@
1
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
2
- import { T as uninstallIosApp, c as iosSimulatorFailureAdvice, p as listUserApps } from "./ios-BwDPrnYQ.mjs";
3
- import { closeSync, lstatSync, openSync, readSync, readdirSync } from "node:fs";
4
- import { isAbsolute, join } from "node:path";
5
- import { createHash } from "node:crypto";
6
- //#region src/engine/installed-artifact.ts
7
- const READ_CHUNK_BYTES = 1048576;
8
- const DEVICE_PATH_TIMEOUT_MS = 3e4;
9
- const DEVICE_HASH_TIMEOUT_MS = 12e4;
10
- const SHA256_HEX = /^[0-9a-f]{64}$/;
11
- function parseInstalledApkPath(text) {
12
- if (typeof text !== "string") return null;
13
- const paths = [];
14
- for (const raw of text.split("\n")) {
15
- const line = raw.trim();
16
- if (!line.startsWith("package:")) continue;
17
- const path = line.slice(8);
18
- if (path.startsWith("/")) paths.push(path);
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { D as uninstallIosApp, M as deviceHoldsApk, N as deviceHoldsBundle, l as iosSimulatorFailureAdvice, m as listUserApps } from "./ios-BaInn3sW.mjs";
3
+ import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-Ee7sDXfp.mjs";
4
+ import { join } from "node:path";
5
+ //#region src/engine/app-schemes.ts
6
+ function schemesFromInfoPlist(plist) {
7
+ const types = plist?.CFBundleURLTypes;
8
+ if (!Array.isArray(types)) return [];
9
+ const out = [];
10
+ for (const type of types) {
11
+ const schemes = type?.CFBundleURLSchemes;
12
+ if (Array.isArray(schemes)) out.push(...schemes.filter((s) => typeof s === "string" && s.trim() !== ""));
19
13
  }
20
- return paths.length === 1 ? paths[0] : null;
21
- }
22
- function parseDeviceSha256(text) {
23
- if (typeof text !== "string") return null;
24
- const first = text.trim().split(/\s+/)[0] ?? "";
25
- return SHA256_HEX.test(first) ? first : null;
26
- }
27
- function parseAppContainerPath(text) {
28
- if (typeof text !== "string") return null;
29
- const lines = text.split("\n").map((line) => line.trim()).filter(Boolean);
30
- return lines.length === 1 && isAbsolute(lines[0]) ? lines[0] : null;
31
- }
32
- function artifactsMatch(local, installed) {
33
- return Boolean(local) && local === installed;
34
- }
35
- function hashFile(file) {
36
- let fd = null;
37
- try {
38
- fd = openSync(file, "r");
39
- const digest = createHash("sha256");
40
- const buffer = Buffer.allocUnsafe(READ_CHUNK_BYTES);
41
- for (;;) {
42
- const read = readSync(fd, buffer, 0, READ_CHUNK_BYTES, null);
43
- if (read <= 0) break;
44
- digest.update(buffer.subarray(0, read));
45
- }
46
- return digest.digest("hex");
47
- } catch {
48
- return null;
49
- } finally {
50
- if (fd !== null) try {
51
- closeSync(fd);
52
- } catch {}
53
- }
54
- }
55
- function hashBundle(dir) {
56
- const digest = createHash("sha256");
57
- const walk = (abs, rel) => {
58
- let names;
59
- try {
60
- names = readdirSync(abs);
61
- } catch {
62
- return false;
63
- }
64
- for (const name of names.toSorted()) {
65
- const child = join(abs, name);
66
- const path = rel === "" ? name : `${rel}/${name}`;
67
- let stat;
68
- try {
69
- stat = lstatSync(child);
70
- } catch {
71
- return false;
72
- }
73
- if (stat.isDirectory()) {
74
- if (!walk(child, path)) return false;
75
- continue;
76
- }
77
- if (!stat.isFile()) return false;
78
- const file = hashFile(child);
79
- if (file === null) return false;
80
- digest.update(`${path}\0${file}\n`);
81
- }
82
- return true;
83
- };
84
- return walk(dir, "") ? digest.digest("hex") : null;
14
+ return out;
85
15
  }
86
- function deviceHoldsApk({ serial, packageName, apkPath }, { exec = null } = {}) {
16
+ function readBundleSchemes(appPath, { exec = null } = {}) {
17
+ if (typeof appPath !== "string" || appPath.trim() === "") return [];
87
18
  const e = exec || getExecutor();
88
- let path;
19
+ let out;
89
20
  try {
90
- path = parseInstalledApkPath(e.runFile("adb", [
91
- "-s",
92
- serial,
93
- "shell",
94
- "pm",
95
- "path",
96
- packageName
97
- ], { timeoutMs: DEVICE_PATH_TIMEOUT_MS }));
21
+ out = e.runFile("plutil", [
22
+ "-convert",
23
+ "json",
24
+ "-o",
25
+ "-",
26
+ join(appPath, "Info.plist")
27
+ ]);
98
28
  } catch {
99
- return false;
29
+ return [];
100
30
  }
101
- if (!path) return false;
102
- let installed;
103
31
  try {
104
- installed = parseDeviceSha256(e.runFile("adb", [
105
- "-s",
106
- serial,
107
- "shell",
108
- "sha256sum",
109
- path
110
- ], { timeoutMs: DEVICE_HASH_TIMEOUT_MS }));
32
+ return schemesFromInfoPlist(JSON.parse(String(out)));
111
33
  } catch {
112
- return false;
34
+ return [];
113
35
  }
114
- if (!installed) return false;
115
- return artifactsMatch(hashFile(apkPath), installed);
116
36
  }
117
- function deviceHoldsBundle({ udid, bundleId, appPath }, { exec = null } = {}) {
118
- const e = exec || getExecutor();
119
- let container;
120
- try {
121
- container = parseAppContainerPath(e.runFile("xcrun", [
122
- "simctl",
123
- "get_app_container",
124
- udid,
125
- bundleId
126
- ], { timeoutMs: DEVICE_PATH_TIMEOUT_MS }));
127
- } catch {
128
- return false;
129
- }
130
- if (!container) return false;
131
- const installed = hashBundle(container);
132
- if (!installed) return false;
133
- return artifactsMatch(hashBundle(appPath), installed);
37
+ const THIRD_PARTY_SCHEME = /^(?:fb\d+|com\.googleusercontent\.apps\.|msauth\.|msauthv2|twitterkit-|db-[a-z0-9]+$|spotify|snapchat|com\.facebook)/i;
38
+ function approvableSchemes(schemes) {
39
+ return (Array.isArray(schemes) ? schemes : []).filter((s) => typeof s === "string" && s.trim() !== "").map((s) => s.trim()).filter((s) => !/^(?:https?|mailto|tel|sms|itms(?:-apps)?)$/i.test(s) && !THIRD_PARTY_SCHEME.test(s));
40
+ }
41
+ function pickDevClientScheme(schemes) {
42
+ const all = approvableSchemes(schemes);
43
+ const expo = all.filter((s) => s.startsWith("exp+"));
44
+ return (expo.length ? expo : all).toSorted((a, b) => b.length - a.length)[0] ?? null;
134
45
  }
135
46
  //#endregion
136
47
  //#region src/engine/app-install.ts
@@ -142,12 +53,37 @@ const IOS_SCHEME_APPROVAL_OPENER = "com.apple.CoreSimulator.CoreSimulatorBridge"
142
53
  const IOS_DEV_MENU_OFF_KEYS = ["EXDevMenuShowsAtLaunch", "EXDevMenuShowFloatingActionButton"];
143
54
  const DEV_CLIENT_ONBOARDING_QUERY = "disableOnboarding=1";
144
55
  const DEV_CLIENT_DISABLE_FAB_QUERY = "disableFab=1";
56
+ const DEV_CLIENT_DEV_MENU_OFF_QUERY = "disableFab=1&disableAutoLaunch=1";
57
+ const DEV_CLIENT_RESERVED_DEV_MENU_OFF_QUERY = "__expo_disable_fab=1&__expo_disable_auto_launch=1";
145
58
  const ANDROID_DISABLE_AUTO_LAUNCH_EXTRA = "EXDevMenuDisableAutoLaunch";
59
+ const IOS_DEV_MENU_OFF_DEFAULTS_PLIST = [
60
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
61
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">",
62
+ "<plist version=\"1.0\">",
63
+ "<dict>",
64
+ ...IOS_DEV_MENU_OFF_KEYS.flatMap((key) => [` <key>${key}</key>`, " <false/>"]),
65
+ "</dict>",
66
+ "</plist>",
67
+ ""
68
+ ].join("\n");
69
+ function writeIosDevMenuOffDefaults(udid, bundleId, exec) {
70
+ for (const key of IOS_DEV_MENU_OFF_KEYS) exec.runFile("xcrun", [
71
+ "simctl",
72
+ "spawn",
73
+ udid,
74
+ "defaults",
75
+ "write",
76
+ bundleId,
77
+ key,
78
+ "-bool",
79
+ "false"
80
+ ], { timeoutMs: 6e4 });
81
+ }
146
82
  function describeIosSimulatorFailure(error, exec) {
147
83
  const detail = describe(error);
148
84
  return error?.code === "ETIMEDOUT" ? `${detail}. ${iosSimulatorFailureAdvice(exec)}` : detail;
149
85
  }
150
- function installIosApp({ udid, appPath, bundleId = null, devClientScheme = null, proveInstalled = true }, { exec = null, now = null } = {}) {
86
+ function installIosApp({ udid, appPath, bundleId = null, devClientScheme = null, schemeApprovals = [], proveInstalled = true }, { exec = null, now = null } = {}) {
151
87
  const e = exec || getExecutor();
152
88
  const artifactStartedAt = now?.();
153
89
  const skipped = bundleId && proveInstalled ? deviceHoldsBundle({
@@ -174,20 +110,13 @@ function installIosApp({ udid, appPath, bundleId = null, devClientScheme = null,
174
110
  }
175
111
  const artifactFinishedAt = now?.();
176
112
  const artifactDurationMs = artifactStartedAt !== void 0 && artifactFinishedAt !== void 0 ? artifactFinishedAt - artifactStartedAt : void 0;
177
- const preparationStartedAt = bundleId && devClientScheme ? artifactFinishedAt : void 0;
178
- if (bundleId && devClientScheme) try {
179
- for (const key of IOS_DEV_MENU_OFF_KEYS) e.runFile("xcrun", [
180
- "simctl",
181
- "spawn",
182
- udid,
183
- "defaults",
184
- "write",
185
- bundleId,
186
- key,
187
- "-bool",
188
- "false"
189
- ], { timeoutMs: 6e4 });
190
- for (const key of iosSchemeApprovalKeys(bundleId, devClientScheme)) e.runFile("xcrun", [
113
+ const devMenuKeys = bundleId && devClientScheme && !skipped ? IOS_DEV_MENU_OFF_KEYS : [];
114
+ const missingApprovals = (bundleId ? iosSchemeApprovalKeys(bundleId, [...approvableSchemes(readBundleSchemes(appPath, { exec: e })), ...devClientScheme ? [devClientScheme] : []]) : []).filter((key) => !schemeApprovals.includes(`${key}=${bundleId}`));
115
+ const preparing = bundleId && (devMenuKeys.length > 0 || missingApprovals.length > 0);
116
+ const preparationStartedAt = preparing ? artifactFinishedAt : void 0;
117
+ if (bundleId && preparing) try {
118
+ if (devMenuKeys.length > 0) writeIosDevMenuOffDefaults(udid, bundleId, e);
119
+ for (const key of missingApprovals) e.runFile("xcrun", [
191
120
  "simctl",
192
121
  "spawn",
193
122
  udid,
@@ -208,7 +137,8 @@ function installIosApp({ udid, appPath, bundleId = null, devClientScheme = null,
208
137
  const devClientPreparationDurationMs = now && preparationStartedAt !== void 0 ? now() - preparationStartedAt : void 0;
209
138
  const timing = {
210
139
  ...artifactDurationMs === void 0 ? {} : { artifactDurationMs },
211
- ...devClientPreparationDurationMs === void 0 ? {} : { devClientPreparationDurationMs }
140
+ ...devClientPreparationDurationMs === void 0 ? {} : { devClientPreparationDurationMs },
141
+ ...missingApprovals.length > 0 ? { schemeApprovals: [...schemeApprovals, ...missingApprovals.map((key) => `${key}=${bundleId}`)] } : {}
212
142
  };
213
143
  return skipped ? {
214
144
  ok: true,
@@ -230,6 +160,7 @@ function clearOtherUserApps({ udid, keep }, { list = listUserApps, uninstall = u
230
160
  } catch {
231
161
  return {
232
162
  listed: false,
163
+ kept: false,
233
164
  removed,
234
165
  failed
235
166
  };
@@ -245,6 +176,7 @@ function clearOtherUserApps({ udid, keep }, { list = listUserApps, uninstall = u
245
176
  }
246
177
  return {
247
178
  listed: true,
179
+ kept: keep ? installed.includes(keep) : false,
248
180
  removed,
249
181
  failed
250
182
  };
@@ -252,15 +184,16 @@ function clearOtherUserApps({ udid, keep }, { list = listUserApps, uninstall = u
252
184
  function jsLocationValue(metroPort) {
253
185
  return `localhost:${metroPort}`;
254
186
  }
255
- function devClientDeepLink(scheme, projectOrigin) {
256
- const projectUrl = `${projectOrigin.replace(/\/+$/, "")}/?${DEV_CLIENT_ONBOARDING_QUERY}`;
257
- return `${scheme}://expo-development-client/?url=${encodeURIComponent(projectUrl)}&${DEV_CLIENT_DISABLE_FAB_QUERY}`;
187
+ function devClientDeepLink(scheme, projectOrigin, { devMenuParams = false } = {}) {
188
+ const projectQuery = devMenuParams ? `${DEV_CLIENT_ONBOARDING_QUERY}&${DEV_CLIENT_DEV_MENU_OFF_QUERY}&${DEV_CLIENT_RESERVED_DEV_MENU_OFF_QUERY}` : DEV_CLIENT_ONBOARDING_QUERY;
189
+ const projectUrl = `${projectOrigin.replace(/\/+$/, "")}/?${projectQuery}`;
190
+ return `${scheme}://expo-development-client/?url=${encodeURIComponent(projectUrl)}&${devMenuParams ? DEV_CLIENT_DEV_MENU_OFF_QUERY : DEV_CLIENT_DISABLE_FAB_QUERY}`;
258
191
  }
259
- function devClientUrl(scheme, metroPort, host = "localhost") {
260
- return devClientDeepLink(scheme, `http://${host}:${metroPort}`);
192
+ function devClientUrl(scheme, metroPort, host = "localhost", options = {}) {
193
+ return devClientDeepLink(scheme, `http://${host}:${metroPort}`, options);
261
194
  }
262
- function iosSchemeApprovalKeys(bundleId, devClientScheme) {
263
- return [.../* @__PURE__ */ new Set([bundleId, devClientScheme])].map((target) => `${IOS_SCHEME_APPROVAL_OPENER}-->${target}`);
195
+ function iosSchemeApprovalKeys(bundleId, schemes) {
196
+ return [.../* @__PURE__ */ new Set([bundleId, ...schemes])].map((target) => `${IOS_SCHEME_APPROVAL_OPENER}-->${target}`);
264
197
  }
265
198
  function restartedAppNote(restartedPid, lead = " ") {
266
199
  return restartedPid ? `${lead}restarted running app (was pid ${restartedPid})` : "";
@@ -299,7 +232,7 @@ function launchedIosAppAfterNoHandle(error, udid, bundleId, exec) {
299
232
  if (!stderr.includes("domain=NSPOSIXErrorDomain, code=3") || !stderr.includes(`Application launch for '${bundleId}' did not return a process handle nor launch error.`)) return null;
300
233
  return iosAppProcess(udid, bundleId, { exec }) ?? null;
301
234
  }
302
- function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null, consolePaths }, { exec = null } = {}) {
235
+ function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null, devMenuParams = false, consolePaths }, { exec = null } = {}) {
303
236
  const e = exec || getExecutor();
304
237
  const launchArgs = [
305
238
  "simctl",
@@ -348,16 +281,27 @@ function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null, conso
348
281
  };
349
282
  }
350
283
  if (devClientScheme) {
351
- const url = devClientUrl(devClientScheme, metroPort);
284
+ const url = devClientUrl(devClientScheme, metroPort, "localhost", { devMenuParams });
285
+ const viaInitialUrl = Boolean(consolePaths) && runningPid === null;
286
+ if (!viaInitialUrl) try {
287
+ writeIosDevMenuOffDefaults(udid, bundleId, e);
288
+ } catch (err) {
289
+ return {
290
+ failed: true,
291
+ code: LAUNCH_ERROR,
292
+ reason: `Could not turn off ${bundleId}'s dev menu before opening it (defaults write): ${describeIosSimulatorFailure(err, e)}`
293
+ };
294
+ }
352
295
  let launchedWithInitialUrl = false;
353
296
  try {
354
- if (consolePaths && runningPid === null) {
297
+ if (viaInitialUrl) {
355
298
  const initialUrl = new URL(url).searchParams.get("url");
356
299
  launchedWithInitialUrl = true;
357
300
  const pid = parseLaunchedPid(e.runFile("xcrun", [
358
301
  ...launchArgs,
359
302
  "--initialUrl",
360
- initialUrl
303
+ initialUrl,
304
+ ...DEV_MENU_LAUNCH_ARGS
361
305
  ], { timeoutMs: 6e4 }));
362
306
  return {
363
307
  ok: true,
@@ -560,6 +504,39 @@ function reverseMetroPorts({ serial, metroPort, devicePorts = null }, { exec = n
560
504
  reversed: pairs.map(([device, host]) => `tcp:${device}->tcp:${host}`)
561
505
  };
562
506
  }
507
+ function hasMetroReverse(listing, metroPort) {
508
+ return String(listing ?? "").split("\n").some((line) => {
509
+ const cols = line.trim().split(/\s+/);
510
+ return cols[1] === `tcp:${metroPort}` && cols[2] === `tcp:${metroPort}`;
511
+ });
512
+ }
513
+ /** Re-applies the launch's `metroPort` reverse when the device no longer has it. */
514
+ function ensureMetroReverse({ serial, metroPort }, { exec = null } = {}) {
515
+ const e = exec || getExecutor();
516
+ let listing;
517
+ try {
518
+ listing = e.runFile("adb", [
519
+ "-s",
520
+ serial,
521
+ "reverse",
522
+ "--list"
523
+ ], ADB_SHELL_OPTIONS);
524
+ } catch {
525
+ return {
526
+ failed: true,
527
+ reason: `adb reverse --list failed on ${serial}`
528
+ };
529
+ }
530
+ if (hasMetroReverse(listing, metroPort)) return { restored: false };
531
+ const reversed = reverseMetroPorts({
532
+ serial,
533
+ metroPort
534
+ }, { exec: e });
535
+ return reversed.failed ? {
536
+ failed: true,
537
+ reason: reversed.reason ?? "adb reverse failed"
538
+ } : { restored: true };
539
+ }
563
540
  const EMULATOR_HOST_LOOPBACK = "127.0.0.1";
564
541
  const PHYSICAL_HOST_LOOPBACK = "localhost";
565
542
  function androidMetroHost(physical) {
@@ -871,4 +848,4 @@ function androidAppProcess(serial, packageName, { exec = null } = {}) {
871
848
  }
872
849
  }
873
850
  //#endregion
874
- export { launchAndroidReleaseApp as _, LAUNCH_ERROR as a, clearOtherUserApps as c, deviceShellArg as d, installAndroidApp as f, launchAndroidApp as g, iosAppProcess as h, INSTALL_ERROR as i, devClientDeepLink as l, installIosApp as m, ANDROID_DISABLE_AUTO_LAUNCH_EXTRA as n, androidAppProcess as o, installConflictKind as p, DEFAULT_METRO_PORT as r, androidDevClientUrl as s, ADB_INSTALL_TIMEOUT_MS as t, devClientUrl as u, launchIosApp as v, restartedAppNote as y };
851
+ export { pickDevClientScheme as C, restartedAppNote as S, schemesFromInfoPlist as T, installIosApp as _, IOS_DEV_MENU_OFF_DEFAULTS_PLIST as a, launchAndroidReleaseApp as b, androidDevClientUrl as c, devClientUrl as d, deviceShellArg as f, installConflictKind as g, installAndroidApp as h, INSTALL_ERROR as i, clearOtherUserApps as l, hasMetroReverse as m, ANDROID_DISABLE_AUTO_LAUNCH_EXTRA as n, LAUNCH_ERROR as o, ensureMetroReverse as p, DEFAULT_METRO_PORT as r, androidAppProcess as s, ADB_INSTALL_TIMEOUT_MS as t, devClientDeepLink as u, iosAppProcess as v, readBundleSchemes as w, launchIosApp as x, launchAndroidApp as y };
@@ -0,0 +1,39 @@
1
+ import { mkdirSync, renameSync, statSync, truncateSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { maintenanceAttemptFile, maintenanceChildLogFile, maintenanceDir } from "@stim-cli/core/state";
3
+ //#region src/maintenance/due.ts
4
+ const DEFERRED_RETRY_MS = 3e5;
5
+ const ATTEMPT_BACKOFF_MS = 6e4;
6
+ function due(state, settings, now, attemptedAt) {
7
+ const elapsed = (stamp) => stamp === void 0 || stamp > now ? Infinity : now - stamp;
8
+ if (elapsed(attemptedAt) < ATTEMPT_BACKOFF_MS) return [];
9
+ return ["pressure", "size"].filter((check) => {
10
+ if (elapsed(state?.deferredAt?.[check]) < DEFERRED_RETRY_MS) return false;
11
+ const interval = check === "pressure" ? settings.pressureCheckMinutes : settings.sizeCheckMinutes;
12
+ return elapsed(state?.lastAt[check]) >= interval * 6e4;
13
+ });
14
+ }
15
+ //#endregion
16
+ //#region src/maintenance/attempt.ts
17
+ const CHILD_LOG_MAX_BYTES = 65536;
18
+ /** Records that a child is about to start, so a child that dies before it can write anything does not respawn on every command. */
19
+ function stampAttempt(now) {
20
+ mkdirSync(maintenanceDir(), { recursive: true });
21
+ const file = maintenanceAttemptFile();
22
+ const tmp = `${file}.${process.pid}.tmp`;
23
+ writeFileSync(tmp, `${JSON.stringify({ attemptedAt: now })}\n`);
24
+ try {
25
+ renameSync(tmp, file);
26
+ } finally {
27
+ try {
28
+ unlinkSync(tmp);
29
+ } catch {}
30
+ }
31
+ }
32
+ function capChildLog() {
33
+ try {
34
+ const file = maintenanceChildLogFile();
35
+ if (statSync(file).size > CHILD_LOG_MAX_BYTES) truncateSync(file);
36
+ } catch {}
37
+ }
38
+ //#endregion
39
+ export { stampAttempt as n, due as r, capChildLog as t };