stim 1.0.0 → 1.1.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 (63) hide show
  1. package/README.md +22 -10
  2. package/dist/android-CC5bUer7.mjs +204 -0
  3. package/dist/android-DnZlT3aU.mjs +2279 -0
  4. package/dist/android-cas-DGlTStWu.mjs +1078 -0
  5. package/dist/android-cas-compiler.mjs +1 -1
  6. package/dist/android-xmyGBNy5.mjs +665 -0
  7. package/dist/app-install-oZIQRbdD.mjs +1141 -0
  8. package/dist/build-lock-vE45ZzJZ.mjs +645 -0
  9. package/dist/build-slots-CgacA9zQ.mjs +152 -0
  10. package/dist/{cache-manifest-4tH64LQ9.mjs → cache-manifest-oeH18ERr.mjs} +2 -2
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +24 -27166
  13. package/dist/collector-run.mjs +6 -4
  14. package/dist/command-output-8wHMEde0.mjs +289 -0
  15. package/dist/{config-7kmtuhO2.mjs → config-D7uu8Gbq.mjs} +43 -12
  16. package/dist/deps-64wVnpZc.mjs +435 -0
  17. package/dist/dev-client-Bvky1hya.mjs +557 -0
  18. package/dist/device-DJKv5Qej.mjs +250 -0
  19. package/dist/device-lease-D-G3SgVe.mjs +335 -0
  20. package/dist/device-pool-BlHXnYYs.mjs +408 -0
  21. package/dist/device-remote-CCgkG82J.mjs +1209 -0
  22. package/dist/doctor-BRF9aAJ3.mjs +530 -0
  23. package/dist/doctor-DlVxTtMq.mjs +1137 -0
  24. package/dist/error-diagnostics-B5IJwv3-.mjs +636 -0
  25. package/dist/gc-DhXdgSdt.mjs +1651 -0
  26. package/dist/guide-DrhrDBo5.mjs +4319 -0
  27. package/dist/ios-BZxKPuqP.mjs +382 -0
  28. package/dist/ios-CK1Vrk6Q.mjs +2721 -0
  29. package/dist/ios-device-CYbuRNXR.mjs +81 -0
  30. package/dist/ios-device-CdSm0GDw.mjs +413 -0
  31. package/dist/logs-Dxoy16Qv.mjs +176 -0
  32. package/dist/logs-query-oqn47Ffx.mjs +273 -0
  33. package/dist/metro-B01H3EuD.mjs +184 -0
  34. package/dist/metro-store-CDDZHtPl.mjs +92 -0
  35. package/dist/ownership-B9QIdQOw.mjs +475 -0
  36. package/dist/ownership-D8HmSv2E.mjs +60 -0
  37. package/dist/ownership-claim-CeJLEpjD.mjs +569 -0
  38. package/dist/{project-Dit1CckW.mjs → project-COj6nzzh.mjs} +12 -3
  39. package/dist/reclaim-BPvN2HlP.mjs +347 -0
  40. package/dist/reload-DYZEBbZy.mjs +332 -0
  41. package/dist/remote-cache-DOi5-zLj.mjs +934 -0
  42. package/dist/{server-bare-BmTVVmBB.mjs → server-bare-BYHqEeyw.mjs} +3 -2
  43. package/dist/{server-expo-CHJe1r6j.mjs → server-expo-BEMr_m6p.mjs} +5 -4
  44. package/dist/server-expo-rb0k-0w7.mjs +2 -0
  45. package/dist/settings-BSwrY4FZ.mjs +687 -0
  46. package/dist/spawn-entry-nCE6m770.mjs +14 -0
  47. package/dist/start-ByCylYtB.mjs +822 -0
  48. package/dist/{state-2nush3MK.mjs → state-IYEY-zi8.mjs} +21 -22
  49. package/dist/stats-DZ4Q5J-7.mjs +62 -0
  50. package/dist/stats-wqxPm7Tv.mjs +307 -0
  51. package/dist/status-Dia0XTqG.mjs +363 -0
  52. package/dist/stop-BApyaF21.mjs +591 -0
  53. package/dist/stop-D6o5zAdm.mjs +3 -0
  54. package/dist/supervisor-run.d.mts +0 -1
  55. package/dist/supervisor-run.mjs +4 -4
  56. package/dist/workspace-process-lock-CxOO9-pf.mjs +47 -0
  57. package/dist/worktree-Dx3ZC5lS.mjs +539 -0
  58. package/dist/worktree-DxEmUI_n.mjs +1322 -0
  59. package/dist/xcode-CDB6fytw.mjs +1705 -0
  60. package/package.json +5 -5
  61. package/dist/android-jc8MnUyL.mjs +0 -514
  62. package/dist/metro-store-COLl1pOk.mjs +0 -1249
  63. package/dist/server-expo-BZJm--wA.mjs +0 -2
package/README.md CHANGED
@@ -77,7 +77,7 @@ artifact caches and Xcode build directories; run `stim guide lifecycle builds`
77
77
  for provider behavior. This selects an Xcode scheme, not a URL scheme.
78
78
 
79
79
  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),
80
+ [declare readiness with two debug log messages](https://stim.appandflow.com/docs/dev-server-and-logs#optional-app-declared-readiness),
81
81
  without a package or SDK. A captured pending message extends the default
82
82
  three-second stability window after bundle delivery to a bounded wait for ready.
83
83
  Managed Metro response capture distinguishes a finished build from a finished
@@ -86,10 +86,12 @@ response; servers without capture use the build-complete marker. Run
86
86
  expected screen separately.
87
87
 
88
88
  `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.
89
+ workspace's owned local device. Use it when an error screen remains after a fix,
90
+ and on Android after a failed first bundle load. An iOS app whose first bundle
91
+ failed never connects to Metro, so reload cannot reach it. It reloads
92
+ JavaScript and never restarts the app. It is not part of the normal workflow
93
+ and does not build, install, boot, or launch an app. The platform is optional
94
+ when only one app is live.
93
95
  Success confirms that the request was sent; Stim does not observe completion.
94
96
  Verify the expected UI on the reported device and inspect `stim logs --errors`
95
97
  before claiming recovery.
@@ -103,9 +105,19 @@ stim worktree warm
103
105
  ```
104
106
 
105
107
  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.
108
+ there. Warm copies missing ignored state from the source checkout, including
109
+ eligible `.env` and local configuration files. Existing entries are preserved;
110
+ existing ignored directories such as `node_modules` are skipped whole.
111
+
112
+ `stim worktree warm --refresh` brings the source checkout up to date first: it
113
+ fetches, fast-forwards whatever branch is checked out there, and installs what
114
+ the new commits moved before copying. It refuses a source checkout it cannot
115
+ move (uncommitted tracked changes, a rebase or merge in progress, a detached
116
+ HEAD, a diverged branch) and never switches branches. One lock per repository
117
+ keeps a copy from reading a `node_modules` a refresh is rewriting; plain warms
118
+ still run side by side. A plain warm also refuses (`STIM_DEPS_INCOMPLETE`) when the last
119
+ install of the lockfile on disk did not finish, instead of copying a partial
120
+ `node_modules`; `stim worktree warm --refresh` reinstalls it.
109
121
 
110
122
  Wait for warm to finish before editing, installing dependencies, starting
111
123
  Metro/builds, or running another warm in that worktree. Concurrent writes are
@@ -113,12 +125,12 @@ unsafe: entries created after the initial check can be overwritten or removed.
113
125
 
114
126
  After the work is preserved, `stim worktree remove` removes any linked
115
127
  worktree, warmed or not. Git-created branches stay. See the
116
- [worktree guide](https://appandflow.github.io/stim/docs/worktrees) for exclusions
128
+ [worktree guide](https://stim.appandflow.com/docs/worktrees) for exclusions
117
129
  and cleanup rules.
118
130
 
119
131
  ## Reference
120
132
 
121
- The [documentation website](https://appandflow.github.io/stim/) explains the
133
+ The [documentation website](https://stim.appandflow.com/) explains the
122
134
  human workflow and all commands.
123
135
 
124
136
  The installed CLI contains version-matched operational guidance:
@@ -0,0 +1,204 @@
1
+ import { t as getExecutor } from "./exec-bsN9MJXb.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 };