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.
- package/README.md +29 -10
- package/dist/android-BW8i_YxU.mjs +204 -0
- package/dist/android-Bcmev2s2.mjs +2334 -0
- package/dist/android-K63_0UfF.mjs +727 -0
- package/dist/android-cas-D6HPZ6UQ.mjs +1078 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/app-install-NFxUahnH.mjs +1149 -0
- package/dist/build-lock-BsVoLDaq.mjs +645 -0
- package/dist/build-slots-D0b6sJyo.mjs +152 -0
- package/dist/{cache-manifest-4tH64LQ9.mjs → cache-manifest-Cu4_Znw9.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +24 -27166
- package/dist/collector-run.d.mts +1 -0
- package/dist/collector-run.mjs +7 -5
- package/dist/command-output-8wHMEde0.mjs +289 -0
- package/dist/{config-7kmtuhO2.mjs → config-CMtIaTk9.mjs} +44 -13
- package/dist/deps-BLBsTNDS.mjs +435 -0
- package/dist/dev-client-RpvXxCXV.mjs +620 -0
- package/dist/device-CoCPmazS.mjs +250 -0
- package/dist/device-lease-Dpik41RY.mjs +335 -0
- package/dist/device-pool-B5pEVa3t.mjs +408 -0
- package/dist/device-remote-E81KAdXf.mjs +1209 -0
- package/dist/doctor-CqPeMsSm.mjs +1130 -0
- package/dist/doctor-D_CX7ib5.mjs +530 -0
- package/dist/error-diagnostics-Dw8Gn3yw.mjs +636 -0
- package/dist/{exec-bsN9MJXb.mjs → exec-CyylIdq9.mjs} +4 -2
- package/dist/gc-B7KkDinF.mjs +1665 -0
- package/dist/guide-BS2Xc24o.mjs +4503 -0
- package/dist/ios-B7KLgIp5.mjs +2733 -0
- package/dist/ios-D1h1Di39.mjs +451 -0
- package/dist/ios-device-D3pzTKQE.mjs +81 -0
- package/dist/ios-device-xNt0Lg6v.mjs +413 -0
- package/dist/logs-DcX9AvRF.mjs +176 -0
- package/dist/logs-query-oqn47Ffx.mjs +273 -0
- package/dist/metro-COslRK_F.mjs +184 -0
- package/dist/metro-store-CDDZHtPl.mjs +92 -0
- package/dist/ownership-CGPart6T.mjs +60 -0
- package/dist/ownership-CYOyqxUo.mjs +475 -0
- package/dist/ownership-claim-7zRxxTfZ.mjs +569 -0
- package/dist/{project-Dit1CckW.mjs → project-BBTVABUz.mjs} +12 -3
- package/dist/reclaim-C5Jmuahx.mjs +348 -0
- package/dist/reload-Z8nKifAk.mjs +332 -0
- package/dist/remote-cache-DDmJ1Roq.mjs +994 -0
- package/dist/{server-bare-BmTVVmBB.mjs → server-bare-DEk37Mz2.mjs} +8 -2
- package/dist/{server-expo-CHJe1r6j.mjs → server-expo-C3ktAwUG.mjs} +6 -5
- package/dist/server-expo-Drd7ksHu.mjs +2 -0
- package/dist/settings-DwU1_U_q.mjs +708 -0
- package/dist/spawn-entry-nCE6m770.mjs +14 -0
- package/dist/start-BrY4EuIg.mjs +822 -0
- package/dist/{state-2nush3MK.mjs → state-CZ-ER-8d.mjs} +21 -22
- package/dist/stats-BCWEVLrM.mjs +307 -0
- package/dist/stats-BsF15Bti.mjs +62 -0
- package/dist/status-DFI1Hfpg.mjs +393 -0
- package/dist/stop-B7DR4awK.mjs +3 -0
- package/dist/stop-Btg770vV.mjs +591 -0
- package/dist/supervisor-run.d.mts +0 -1
- package/dist/supervisor-run.mjs +4 -4
- package/dist/workspace-process-lock-BKfdys1q.mjs +47 -0
- package/dist/worktree-BPTbI-LN.mjs +1322 -0
- package/dist/worktree-C79xx_kM.mjs +543 -0
- package/dist/xcode-DeKCribl.mjs +1832 -0
- package/package.json +5 -5
- package/shim/bundle-response.cjs +5 -0
- package/dist/android-jc8MnUyL.mjs +0 -514
- package/dist/metro-store-COLl1pOk.mjs +0 -1249
- 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.
|
|
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
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
107
|
-
and local configuration files. Existing entries are preserved;
|
|
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.
|
|
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.
|
|
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 };
|