stim 1.0.0 → 1.2.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 (66) hide show
  1. package/README.md +29 -10
  2. package/dist/android-BW8i_YxU.mjs +204 -0
  3. package/dist/android-Bcmev2s2.mjs +2334 -0
  4. package/dist/android-K63_0UfF.mjs +727 -0
  5. package/dist/android-cas-D6HPZ6UQ.mjs +1078 -0
  6. package/dist/android-cas-compiler.mjs +2 -2
  7. package/dist/app-install-NFxUahnH.mjs +1149 -0
  8. package/dist/build-lock-BsVoLDaq.mjs +645 -0
  9. package/dist/build-slots-D0b6sJyo.mjs +152 -0
  10. package/dist/{cache-manifest-4tH64LQ9.mjs → cache-manifest-Cu4_Znw9.mjs} +2 -2
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +24 -27166
  13. package/dist/collector-run.d.mts +1 -0
  14. package/dist/collector-run.mjs +7 -5
  15. package/dist/command-output-8wHMEde0.mjs +289 -0
  16. package/dist/{config-7kmtuhO2.mjs → config-CMtIaTk9.mjs} +44 -13
  17. package/dist/deps-BLBsTNDS.mjs +435 -0
  18. package/dist/dev-client-RpvXxCXV.mjs +620 -0
  19. package/dist/device-CoCPmazS.mjs +250 -0
  20. package/dist/device-lease-Dpik41RY.mjs +335 -0
  21. package/dist/device-pool-B5pEVa3t.mjs +408 -0
  22. package/dist/device-remote-E81KAdXf.mjs +1209 -0
  23. package/dist/doctor-CqPeMsSm.mjs +1130 -0
  24. package/dist/doctor-D_CX7ib5.mjs +530 -0
  25. package/dist/error-diagnostics-Dw8Gn3yw.mjs +636 -0
  26. package/dist/{exec-bsN9MJXb.mjs → exec-CyylIdq9.mjs} +4 -2
  27. package/dist/gc-B7KkDinF.mjs +1665 -0
  28. package/dist/guide-BS2Xc24o.mjs +4503 -0
  29. package/dist/ios-B7KLgIp5.mjs +2733 -0
  30. package/dist/ios-D1h1Di39.mjs +451 -0
  31. package/dist/ios-device-D3pzTKQE.mjs +81 -0
  32. package/dist/ios-device-xNt0Lg6v.mjs +413 -0
  33. package/dist/logs-DcX9AvRF.mjs +176 -0
  34. package/dist/logs-query-oqn47Ffx.mjs +273 -0
  35. package/dist/metro-COslRK_F.mjs +184 -0
  36. package/dist/metro-store-CDDZHtPl.mjs +92 -0
  37. package/dist/ownership-CGPart6T.mjs +60 -0
  38. package/dist/ownership-CYOyqxUo.mjs +475 -0
  39. package/dist/ownership-claim-7zRxxTfZ.mjs +569 -0
  40. package/dist/{project-Dit1CckW.mjs → project-BBTVABUz.mjs} +12 -3
  41. package/dist/reclaim-C5Jmuahx.mjs +348 -0
  42. package/dist/reload-Z8nKifAk.mjs +332 -0
  43. package/dist/remote-cache-DDmJ1Roq.mjs +994 -0
  44. package/dist/{server-bare-BmTVVmBB.mjs → server-bare-DEk37Mz2.mjs} +8 -2
  45. package/dist/{server-expo-CHJe1r6j.mjs → server-expo-C3ktAwUG.mjs} +6 -5
  46. package/dist/server-expo-Drd7ksHu.mjs +2 -0
  47. package/dist/settings-DwU1_U_q.mjs +708 -0
  48. package/dist/spawn-entry-nCE6m770.mjs +14 -0
  49. package/dist/start-BrY4EuIg.mjs +822 -0
  50. package/dist/{state-2nush3MK.mjs → state-CZ-ER-8d.mjs} +21 -22
  51. package/dist/stats-BCWEVLrM.mjs +307 -0
  52. package/dist/stats-BsF15Bti.mjs +62 -0
  53. package/dist/status-DFI1Hfpg.mjs +393 -0
  54. package/dist/stop-B7DR4awK.mjs +3 -0
  55. package/dist/stop-Btg770vV.mjs +591 -0
  56. package/dist/supervisor-run.d.mts +0 -1
  57. package/dist/supervisor-run.mjs +4 -4
  58. package/dist/workspace-process-lock-BKfdys1q.mjs +47 -0
  59. package/dist/worktree-BPTbI-LN.mjs +1322 -0
  60. package/dist/worktree-C79xx_kM.mjs +543 -0
  61. package/dist/xcode-DeKCribl.mjs +1832 -0
  62. package/package.json +5 -5
  63. package/shim/bundle-response.cjs +5 -0
  64. package/dist/android-jc8MnUyL.mjs +0 -514
  65. package/dist/metro-store-COLl1pOk.mjs +0 -1249
  66. package/dist/server-expo-BZJm--wA.mjs +0 -2
package/README.md CHANGED
@@ -58,6 +58,13 @@ one native platform is in scope; shared project checks still run. Doctor also
58
58
  prints the running CLI version and the `stim` installation resolved from PATH,
59
59
  and flags a resolved installation that is older than another available one.
60
60
 
61
+ For parallel iOS work, SimSlim is recommended as an optional way to reduce
62
+ simulator background services and memory use. Run `stim guide lifecycle simslim`
63
+ to review the service tradeoffs and configure a profile. Doctor recommends the
64
+ setup without installing or enabling it. When a simulator cannot start
65
+ processes, Stim bounds the wait and reports observed host memory pressure when
66
+ available; free memory before retrying. A timeout alone does not prove OOM.
67
+
61
68
  For stale Android CMake launcher findings, stop native builds and run
62
69
  `stim doctor --fix --platform android` in the affected checkout. It clears
63
70
  affected ignored, untracked generated `.cxx` configurations in the app and
@@ -77,7 +84,7 @@ artifact caches and Xcode build directories; run `stim guide lifecycle builds`
77
84
  for provider behavior. This selects an Xcode scheme, not a URL scheme.
78
85
 
79
86
  Launch evidence does not prove that the UI is interactive. Apps can optionally
80
- [declare readiness with two debug log messages](https://appandflow.github.io/stim/docs/dev-server-and-logs#optional-app-declared-readiness),
87
+ [declare readiness with two debug log messages](https://stim.appandflow.com/docs/dev-server-and-logs#optional-app-declared-readiness),
81
88
  without a package or SDK. A captured pending message extends the default
82
89
  three-second stability window after bundle delivery to a bounded wait for ready.
83
90
  Managed Metro response capture distinguishes a finished build from a finished
@@ -86,10 +93,12 @@ response; servers without capture use the build-complete marker. Run
86
93
  expected screen separately.
87
94
 
88
95
  `stim reload [ios|android]` requests a JavaScript reload in the live app on this
89
- workspace's owned local device. Use it after a failed first bundle load, when
90
- an error screen remains after a fix, or when you explicitly need an app
91
- restart. It is not part of the normal workflow and does not build, install,
92
- boot, or launch an app. The platform is optional when only one app is live.
96
+ workspace's owned local device. Use it when an error screen remains after a fix,
97
+ and on Android after a failed first bundle load. An iOS app whose first bundle
98
+ failed never connects to Metro, so reload cannot reach it. It reloads
99
+ JavaScript and never restarts the app. It is not part of the normal workflow
100
+ and does not build, install, boot, or launch an app. The platform is optional
101
+ when only one app is live.
93
102
  Success confirms that the request was sent; Stim does not observe completion.
94
103
  Verify the expected UI on the reported device and inspect `stim logs --errors`
95
104
  before claiming recovery.
@@ -103,9 +112,19 @@ stim worktree warm
103
112
  ```
104
113
 
105
114
  If a harness already created the linked worktree, run only `stim worktree warm`
106
- there. Warm copies missing ignored state from main, including eligible `.env`
107
- and local configuration files. Existing entries are preserved; existing
108
- ignored directories such as `node_modules` are skipped whole.
115
+ there. Warm copies missing ignored state from the source checkout, including
116
+ eligible `.env` and local configuration files. Existing entries are preserved;
117
+ existing ignored directories such as `node_modules` are skipped whole.
118
+
119
+ `stim worktree warm --refresh` brings the source checkout up to date first: it
120
+ fetches, fast-forwards whatever branch is checked out there, and installs what
121
+ the new commits moved before copying. It refuses a source checkout it cannot
122
+ move (uncommitted tracked changes, a rebase or merge in progress, a detached
123
+ HEAD, a diverged branch) and never switches branches. One lock per repository
124
+ keeps a copy from reading a `node_modules` a refresh is rewriting; plain warms
125
+ still run side by side. A plain warm also refuses (`STIM_DEPS_INCOMPLETE`) when the last
126
+ install of the lockfile on disk did not finish, instead of copying a partial
127
+ `node_modules`; `stim worktree warm --refresh` reinstalls it.
109
128
 
110
129
  Wait for warm to finish before editing, installing dependencies, starting
111
130
  Metro/builds, or running another warm in that worktree. Concurrent writes are
@@ -113,12 +132,12 @@ unsafe: entries created after the initial check can be overwritten or removed.
113
132
 
114
133
  After the work is preserved, `stim worktree remove` removes any linked
115
134
  worktree, warmed or not. Git-created branches stay. See the
116
- [worktree guide](https://appandflow.github.io/stim/docs/worktrees) for exclusions
135
+ [worktree guide](https://stim.appandflow.com/docs/worktrees) for exclusions
117
136
  and cleanup rules.
118
137
 
119
138
  ## Reference
120
139
 
121
- The [documentation website](https://appandflow.github.io/stim/) explains the
140
+ The [documentation website](https://stim.appandflow.com/) explains the
122
141
  human workflow and all commands.
123
142
 
124
143
  The installed CLI contains version-matched operational guidance:
@@ -0,0 +1,204 @@
1
+ import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
+ //#region src/collector/android.ts
3
+ const LEVEL_BY_LETTER = {
4
+ V: "debug",
5
+ D: "debug",
6
+ I: "info",
7
+ W: "warn",
8
+ E: "error",
9
+ F: "fatal",
10
+ A: "fatal",
11
+ S: "debug"
12
+ };
13
+ function levelFromLogcatLetter(letter) {
14
+ return LEVEL_BY_LETTER[String(letter || "").toUpperCase()] ?? "info";
15
+ }
16
+ const NOISE_TAGS = /* @__PURE__ */ new Set([
17
+ "libEGL",
18
+ "EGL_emulation",
19
+ "eglCodecCommon",
20
+ "emuglGLESv2_enc",
21
+ "HostConnection",
22
+ "OpenGLRenderer",
23
+ "gralloc4",
24
+ "Gralloc4",
25
+ "vulkan",
26
+ "GraphicBufferAllocator",
27
+ "BufferQueueProducer",
28
+ "Surface",
29
+ "ziparchive",
30
+ "chatty"
31
+ ]);
32
+ function levelForLogcat(letter, tag) {
33
+ const level = levelFromLogcatLetter(letter);
34
+ if (level !== "error") return level;
35
+ return NOISE_TAGS.has(String(tag || "").trim()) ? "info" : level;
36
+ }
37
+ const LOGCAT_TIME = /^(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\.(\d{3})\s+([A-Z])\/(.*?)\(\s*(\d+)\):\s?(.*)$/;
38
+ const LOGCAT_EPOCH = /^\s*(\d+)\.(\d{3,6})\s+([A-Z])\/(.*?)\(\s*(\d+)\):\s?(.*)$/;
39
+ function parseLogcatTimestamp({ month, day, hour, minute, second, millis }, now = Date.now()) {
40
+ const ref = new Date(now);
41
+ let ts = new Date(ref.getFullYear(), month - 1, day, hour, minute, second, millis).getTime();
42
+ if (ts - now > 864e5) ts = new Date(ref.getFullYear() - 1, month - 1, day, hour, minute, second, millis).getTime();
43
+ return ts;
44
+ }
45
+ function parseLogcatLine(line, { now = Date.now, clockOffsetMs = null } = {}) {
46
+ if (typeof line !== "string") return null;
47
+ const epoch = LOGCAT_EPOCH.exec(line.trimEnd());
48
+ if (epoch) {
49
+ const [, seconds, fraction, letter, tag, pid, msg] = epoch;
50
+ if (!letter || !tag || !msg?.trim()) return null;
51
+ const deviceTs = Number(seconds) * 1e3 + Number(fraction.slice(0, 3));
52
+ if (!Number.isSafeInteger(deviceTs)) return null;
53
+ return {
54
+ ts: deviceTs + (clockOffsetMs ?? 0),
55
+ deviceTs,
56
+ ...clockOffsetMs === null ? {} : { clockOffsetMs },
57
+ src: "device",
58
+ level: levelForLogcat(letter, tag),
59
+ msg,
60
+ proc: `${tag.trim()}(${pid})`
61
+ };
62
+ }
63
+ const m = LOGCAT_TIME.exec(line.trimEnd());
64
+ if (!m) return null;
65
+ const [, month, day, hour, minute, second, millis, letter, tag, pid, msg] = m;
66
+ if (letter === void 0 || tag === void 0 || msg === void 0) return null;
67
+ if (!msg.trim()) return null;
68
+ return {
69
+ ts: parseLogcatTimestamp({
70
+ month: Number(month),
71
+ day: Number(day),
72
+ hour: Number(hour),
73
+ minute: Number(minute),
74
+ second: Number(second),
75
+ millis: Number(millis)
76
+ }, now()),
77
+ src: "device",
78
+ level: levelForLogcat(letter, tag),
79
+ msg,
80
+ proc: `${tag.trim()}(${pid})`
81
+ };
82
+ }
83
+ function logcatArgs(serial, pid) {
84
+ return [
85
+ "-s",
86
+ serial,
87
+ "logcat",
88
+ "--pid",
89
+ String(pid),
90
+ "-v",
91
+ "time,epoch"
92
+ ];
93
+ }
94
+ function androidClockOffset(serial, { exec = getExecutor(), now = Date.now } = {}) {
95
+ const before = now();
96
+ try {
97
+ const output = exec.runFile("adb", [
98
+ "-s",
99
+ serial,
100
+ "shell",
101
+ "date",
102
+ "+%s%3N"
103
+ ], { timeoutMs: 1e3 }).trim();
104
+ const after = now();
105
+ if (!/^\d{13}$/.test(output) || after < before || after - before > 1e3) return null;
106
+ return Math.round((before + after) / 2) - Number(output);
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+ function parsePidof(text) {
112
+ const first = String(text ?? "").trim().split(/\s+/)[0];
113
+ const pid = Number(first);
114
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
115
+ }
116
+ function resolveAppPid(serial, packageName, { exec = null } = {}) {
117
+ const e = exec || getExecutor();
118
+ try {
119
+ return parsePidof(e.runFile("adb", [
120
+ "-s",
121
+ serial,
122
+ "shell",
123
+ "pidof",
124
+ "-s",
125
+ packageName
126
+ ]));
127
+ } catch {
128
+ return null;
129
+ }
130
+ }
131
+ async function waitForAppPid({ serial, packageName, timeoutMs = 3e4, pollMs = 500, exec = null, now = Date.now, sleep = defaultSleep }) {
132
+ const deadline = now() + timeoutMs;
133
+ for (;;) {
134
+ const pid = resolveAppPid(serial, packageName, { exec });
135
+ if (pid) return {
136
+ ok: true,
137
+ pid
138
+ };
139
+ if (now() >= deadline) return {
140
+ failed: true,
141
+ reason: `No process for ${packageName} appeared on ${serial} within ${Math.round(timeoutMs / 1e3)}s.`
142
+ };
143
+ await sleep(pollMs);
144
+ }
145
+ }
146
+ function defaultSleep(ms) {
147
+ return new Promise((r) => setTimeout(r, ms));
148
+ }
149
+ function startAndroidLogcat({ serial, pid, spawnFn = null }) {
150
+ return (spawnFn || ((cmd, args, opts) => getExecutor().spawn(cmd, args, opts)))("adb", logcatArgs(serial, pid), {
151
+ stdio: [
152
+ "ignore",
153
+ "pipe",
154
+ "pipe"
155
+ ],
156
+ detached: false
157
+ });
158
+ }
159
+ const PID_WATCH_MS = 3e3;
160
+ function pidWatchInterval() {
161
+ const override = Number(process.env.STIM_PID_WATCH_MS);
162
+ return Number.isFinite(override) && override > 0 ? override : PID_WATCH_MS;
163
+ }
164
+ function watchAppPid({ serial, packageName, pid, intervalMs = null, exec = null, onChange, resolve = resolveAppPid, setTimer = setTimeout, clearTimer = clearTimeout }) {
165
+ const every = intervalMs ?? pidWatchInterval();
166
+ let current = pid;
167
+ let stopped = false;
168
+ let timer = null;
169
+ const tick = async () => {
170
+ timer = null;
171
+ if (stopped) return;
172
+ let next = null;
173
+ try {
174
+ next = resolve(serial, packageName, { exec });
175
+ } catch {}
176
+ if (next && next !== current) {
177
+ current = next;
178
+ try {
179
+ await onChange(next);
180
+ } catch {}
181
+ }
182
+ if (!stopped) timer = setTimer(tick, every);
183
+ };
184
+ timer = setTimer(tick, every);
185
+ return {
186
+ stop() {
187
+ stopped = true;
188
+ if (timer) clearTimer(timer);
189
+ timer = null;
190
+ },
191
+ probe() {
192
+ try {
193
+ return resolve(serial, packageName, { exec });
194
+ } catch {
195
+ return null;
196
+ }
197
+ },
198
+ get pid() {
199
+ return current;
200
+ }
201
+ };
202
+ }
203
+ //#endregion
204
+ export { watchAppPid as a, waitForAppPid as i, parseLogcatLine as n, startAndroidLogcat as r, androidClockOffset as t };