stim 1.11.0 → 1.13.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 +3 -1
- package/dist/{activity-BtG4Va9l.mjs → activity-DfVKZTEB.mjs} +44 -26
- package/dist/{android-BA2i_KAV.mjs → android-BHfAu2mf.mjs} +1 -1
- package/dist/{android-C1q4l523.mjs → android-CD8pr8Q2.mjs} +112 -54
- package/dist/{android-cas-DfJkwCXW.mjs → android-cas-B9Kv9aIy.mjs} +12 -10
- package/dist/{android-C4zG2Tuc.mjs → android-qx_31wJo.mjs} +414 -118
- package/dist/{app-install-DHut6JnV.mjs → app-install-CDFY2nUL.mjs} +1 -1
- package/dist/budget-E5o91NR1.mjs +1739 -0
- package/dist/{build-plan-CeAYgKAx.mjs → build-plan-BTa93o_r.mjs} +125 -107
- package/dist/{build-progress-CDwogz6R.mjs → build-progress-l7d-X-ZP.mjs} +76 -8
- package/dist/{cache-manifest-Pubfp8wV.mjs → cache-manifest-DWPF4RKJ.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cdp-Dq_ypAlJ.mjs +115 -0
- package/dist/chrome-B5hWgQJS.mjs +61 -0
- package/dist/cli.mjs +17 -16
- package/dist/collector-run.mjs +15 -9
- package/dist/{command-output-D5z2_bcF.mjs → command-output-BIL7rlX7.mjs} +1 -1
- package/dist/{config-BEq0Qw_a.mjs → config-0jCcMM0E.mjs} +10 -1
- package/dist/created-devices-Cd558h31.mjs +37 -0
- package/dist/{dependency-state-r9UesA9a.mjs → dependency-state-DNN0yfx6.mjs} +1 -1
- package/dist/{deps-OF7CD5Dx.mjs → deps-2Wo81np9.mjs} +2 -2
- package/dist/{device-BwH2iI3O.mjs → device-Cf7vl-so.mjs} +7 -7
- package/dist/{device-lease-D83nqeMX.mjs → device-lease-CL9dZf73.mjs} +55 -6
- package/dist/{device-pool-DHj7cdPy.mjs → device-pool-MuoiyRcu.mjs} +4 -4
- package/dist/{device-remote-BQZSnnmE.mjs → device-remote-DT9ngZiD.mjs} +254 -579
- package/dist/{doctor-D8QdMRMf.mjs → doctor-D2iysNNH.mjs} +50 -20
- package/dist/{error-diagnostics-UYFSQwRy.mjs → error-diagnostics-BoiOhck8.mjs} +7 -7
- package/dist/{gc-COZteyiq.mjs → gc-eA-PRBX8.mjs} +1022 -131
- package/dist/{guide-NDvTmMi-.mjs → guide-BfZ0OmiP.mjs} +909 -132
- package/dist/{guide-status-CWvti2Lg.mjs → guide-status-BaMYu55M.mjs} +2 -2
- package/dist/{idle-NETbeNiQ.mjs → idle-DxLi5OkN.mjs} +43 -10
- package/dist/{in-use-DVhlSoyn.mjs → in-use-wYEaKQM9.mjs} +11 -7
- package/dist/{ios-CQLKpNfr.mjs → ios-CcfCeZ19.mjs} +103 -58
- package/dist/{ios-DArbmwer.mjs → ios-CmEzZiOj.mjs} +31 -22
- package/dist/{ios-device-CwDZo0X4.mjs → ios-device-BW-t_Lkv.mjs} +1 -1
- package/dist/{launch-verify-CuPDGXdr.mjs → launch-verify-CuJaBpEa.mjs} +33 -20
- package/dist/{logs-B44a1UeU.mjs → logs-DcSRbC2t.mjs} +12 -11
- package/dist/{merge-state-DDxS-XIZ.mjs → merge-state-ytFQCeh4.mjs} +38 -8
- package/dist/{metro-BWLOJ_Wr.mjs → metro-BPeY1YMh.mjs} +5 -5
- package/dist/{named-ports-Dc7neAlc.mjs → named-ports-n7tnJsI8.mjs} +78 -6
- package/dist/native-runtime-CiiFHt0e.mjs +74 -0
- package/dist/{ownership-D2xbLVoz.mjs → ownership-Bz2kETAS.mjs} +2 -2
- package/dist/{ownership-BiojdtAp.mjs → ownership-PUP4Dwai.mjs} +175 -217
- package/dist/page-BTu7I60K.mjs +29 -0
- package/dist/{ports-DwBaRFJs.mjs → ports-C5UutqpJ.mjs} +5 -5
- package/dist/{project-BXZzOABt.mjs → project-DGeclhvO.mjs} +55 -87
- package/dist/{reload-By9Dwsrt.mjs → reload-C-cV5yy_.mjs} +80 -21
- package/dist/remote-cache--aehr4Pr.mjs +526 -0
- package/dist/{server-bare-D_RK1RCj.mjs → server-bare-Bq8BpSIo.mjs} +1 -1
- package/dist/server-expo-BeYwP_Xy.mjs +2 -0
- package/dist/{server-expo-o6H5gcBc.mjs → server-expo-DUCg52-K.mjs} +2 -2
- package/dist/{settings-C0ugALSC.mjs → settings-ssVBToDR.mjs} +29 -17
- package/dist/{settings-B5oFZEri.mjs → settings-xB0Frkg6.mjs} +13 -4
- package/dist/settings.schema.json +74 -2
- package/dist/{simslim-CIR3IYJO.mjs → simslim-Bbg9UWrt.mjs} +13 -6
- package/dist/{slot-launch-rggO1Esw.mjs → slot-launch-BIWEW-wI.mjs} +4 -4
- package/dist/{spawn-claims-BJwXTMPt.mjs → spawn-claims-PT8aQpCa.mjs} +1 -1
- package/dist/{start-BSsf2tUs.mjs → start-BZhItkiL.mjs} +110 -18
- package/dist/{start-i8zc9VmS.mjs → start-Rw2GD3nV.mjs} +1 -1
- package/dist/{state-WGdIbi_d.mjs → state-DwAU2_-D.mjs} +15 -3
- package/dist/state-l1pYwOre.mjs +148 -0
- package/dist/{stats-DBYJ5bxL.mjs → stats-CaSwmsSZ.mjs} +4 -4
- package/dist/{stats-BAgJaLA4.mjs → stats-D8rUTPHd.mjs} +1 -1
- package/dist/{status-CitcBRsC.mjs → status-TJlL0HWo.mjs} +649 -116
- package/dist/{status-Q-WDEGCB.mjs → status-sszP4XKU.mjs} +130 -28
- package/dist/stim-desktop-CXt8-Z3J.mjs +40 -0
- package/dist/{stop-DIhrvu0B.mjs → stop-BZ2qMiBP.mjs} +50 -18
- package/dist/{stop-rst5mL14.mjs → stop-DskDKNTU.mjs} +2 -2
- package/dist/supervisor-run.mjs +11 -11
- package/dist/web-run.d.mts +22 -0
- package/dist/web-run.mjs +481 -0
- package/dist/web-sWXd5iek.mjs +394 -0
- package/dist/{worktree-DhxDTK44.mjs → worktree-3cBa-bQt.mjs} +1 -1
- package/dist/{worktree-KOjMX3zA.mjs → worktree-D4Do_XiQ.mjs} +229 -21
- package/dist/{worktree-BlizKDpZ.mjs → worktree-DeBQSVtR.mjs} +3 -1
- package/dist/{xcode-B5SPD--o.mjs → xcode-LM6A8cNZ.mjs} +7 -7
- package/package.json +5 -5
- package/dist/budget-De3P2rq_.mjs +0 -620
- package/dist/native-run-D7yB_AsB.mjs +0 -143
- package/dist/server-expo-B-CCUJZj.mjs +0 -2
- package/dist/workspace-process-lock-Cx74RSDZ.mjs +0 -57
- package/dist/workspaces-COD2xCz-.mjs +0 -1013
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { H as parseDeviceSlotOption, O as process_identity_exports, U as projectDeviceSlots, _ as loadConfig$1, g as isPathPrefix } from "./config-0jCcMM0E.mjs";
|
|
2
2
|
import { n as SOURCES, t as LEVELS } from "./ndjson-CDY9IkVC.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { f as refuseNoProject, v as launchErrorPreview } from "./command-output-
|
|
6
|
-
import {
|
|
7
|
-
import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-
|
|
3
|
+
import { M as workspaceLogsDir, h as readWorkspaceState$1 } from "./metro-BPeY1YMh.mjs";
|
|
4
|
+
import { i as findCommandWorkspace } from "./project-DGeclhvO.mjs";
|
|
5
|
+
import { f as refuseNoProject, v as launchErrorPreview } from "./command-output-BIL7rlX7.mjs";
|
|
6
|
+
import { a as readAgentDeviceRecords, t as agentDeviceLiveness } from "./activity-DfVKZTEB.mjs";
|
|
7
|
+
import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-BoiOhck8.mjs";
|
|
8
8
|
import { closeSync, fstatSync, openSync, readFileSync, readSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
9
|
-
import { buildCriteria, compileGrep, fileSizes, followLogs, logFiles, parseSince, queryLogs, readLogRecords, recordMatches, sortByTs } from "@stim-cli/core/state";
|
|
9
|
+
import { attachExpoErrorContext, buildCriteria, compileGrep, fileSizes, followLogs, logFiles, parseSince, queryLogs, readLogRecords, recordMatches, sortByTs } from "@stim-cli/core/state";
|
|
10
10
|
import { join, relative, resolve, sep } from "node:path";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import chalk from "chalk";
|
|
@@ -359,8 +359,8 @@ const LEVEL_COLOURS = {
|
|
|
359
359
|
fatal: chalk.bgRed
|
|
360
360
|
};
|
|
361
361
|
function logsCommand(program) {
|
|
362
|
-
program.command("logs").description("Query this workspace's merged NDJSON log timeline (bundler, client, device, build, agent-device actions). Prints and exits; an existing timeline with nothing matching is a successful, empty result. Use --follow to stream.").option("--slot <name>", "Only records attributed to this device slot", parseDeviceSlotOption).option("--source <s...>", "Only these sources: metro, client, device, build, agent, or all").option("--level <l>", `Minimum level: ${LEVELS.join(", ")}`).option("--since <d>", "Only records newer than this, e.g. 30s, 5m, 2h").option("--grep <re>", "Only records whose message matches this regular expression").option("--tail <n>", "Only the last n matching records").option("--errors", "Errors and fatals since the last marker, from metro, client and build, plus confirmed native app-crash reports. Add --source device or --source all for general device errors.").option("--follow", "Keep streaming new records until interrupted").option("--json", "Emit the raw records, one per line (valid NDJSON; zero matches is zero bytes, exit 0)").action(async (opts) => {
|
|
363
|
-
const root =
|
|
362
|
+
program.command("logs").description("Query this workspace's merged NDJSON log timeline (bundler, client, device, build, agent-device actions). Prints and exits; an existing timeline with nothing matching is a successful, empty result. Use --follow to stream.").option("--slot <name>", "Only records attributed to this device slot", parseDeviceSlotOption).option("--source <s...>", "Only these sources: metro, client, device, build, agent, or all").option("--level <l>", `Minimum level: ${LEVELS.join(", ")}`).option("--since <d>", "Only records newer than this, e.g. 30s, 5m, 2h").option("--grep <re>", "Only records whose message matches this regular expression").option("--tail <n>", "Only the last n matching records").option("--errors", "Errors and fatals since the last marker, from metro, client and build, plus confirmed native app-crash reports and stim web browser errors. Add --source device or --source all for general device errors.").option("--follow", "Keep streaming new records until interrupted").option("--json", "Emit the raw records, one per line (valid NDJSON; zero matches is zero bytes, exit 0)").action(async (opts) => {
|
|
363
|
+
const root = findCommandWorkspace(process.cwd());
|
|
364
364
|
if (!root) {
|
|
365
365
|
refuseNoProject({ json: false });
|
|
366
366
|
return;
|
|
@@ -430,7 +430,7 @@ function logsCommand(program) {
|
|
|
430
430
|
records: timeline
|
|
431
431
|
});
|
|
432
432
|
const supervisorPort = readWorkspaceState$1(root)?.supervisor?.port;
|
|
433
|
-
const records = opts.json ? rawRecords : await errorDiagnostics(rawRecords, {
|
|
433
|
+
const records = opts.json ? opts.errors ? attachExpoErrorContext(timeline, rawRecords, "field") : rawRecords : await errorDiagnostics(rawRecords, {
|
|
434
434
|
root,
|
|
435
435
|
logsDir: dir,
|
|
436
436
|
port: typeof supervisorPort === "number" ? supervisorPort : null,
|
|
@@ -463,7 +463,8 @@ function logsCommand(program) {
|
|
|
463
463
|
dir,
|
|
464
464
|
offsets,
|
|
465
465
|
criteria,
|
|
466
|
-
onRecord: emit
|
|
466
|
+
onRecord: emit,
|
|
467
|
+
errorContext: opts.errors ? opts.json ? "field" : "msg" : void 0
|
|
467
468
|
});
|
|
468
469
|
const agentTimer = readAgent ? setInterval(() => {
|
|
469
470
|
for (const record of readAgent()) if (recordMatches(record, criteria)) emit(record);
|
|
@@ -117,7 +117,9 @@ function committedOn(path, branch, head) {
|
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
120
|
-
* Whether the worktree's HEAD is merged into `target
|
|
120
|
+
* Whether the worktree's HEAD is merged into `target`, and when: `mergedAt` is the committer date, in epoch
|
|
121
|
+
* milliseconds, of the commit on `target` that brought the work in, the latest one for a rebase merge.
|
|
122
|
+
* The signals, all local git:
|
|
121
123
|
* - HEAD is an ancestor of the default branch, off its first-parent line, and the branch's reflog shows a commit made
|
|
122
124
|
* on it that HEAD contains, so a merge commit brought the branch's own work in. A branch with no commit of its own
|
|
123
125
|
* is not merged.
|
|
@@ -147,7 +149,21 @@ function mergeState(path, { ref, name }, { timeoutMs = GIT_TIMEOUT_MS } = {}) {
|
|
|
147
149
|
timeoutMs,
|
|
148
150
|
untrimmed: true
|
|
149
151
|
});
|
|
150
|
-
const
|
|
152
|
+
const patchIdCommits = (text) => new Map(text ? git(["patch-id", "--verbatim"], text).split("\n").flatMap((line) => {
|
|
153
|
+
const [id, commit] = line.split(" ");
|
|
154
|
+
return id && commit ? [[id, commit]] : [];
|
|
155
|
+
}) : []);
|
|
156
|
+
const patchIds = (text) => [...patchIdCommits(text).keys()];
|
|
157
|
+
const committedAt = (commits) => {
|
|
158
|
+
const times = commits.length ? git([
|
|
159
|
+
"show",
|
|
160
|
+
"-s",
|
|
161
|
+
"--format=%ct",
|
|
162
|
+
...commits
|
|
163
|
+
]).split("\n").map(Number) : [];
|
|
164
|
+
if (!times.length || !times.every(Number.isFinite)) throw new Error(`no committer date for ${commits.join(" ")}`);
|
|
165
|
+
return Math.max(...times) * 1e3;
|
|
166
|
+
};
|
|
151
167
|
const diffOptions = ["--no-color", "--no-ext-diff"];
|
|
152
168
|
const noOwnCommits = notMerged(`no commits of its own beyond ${name}`);
|
|
153
169
|
try {
|
|
@@ -169,11 +185,24 @@ function mergeState(path, { ref, name }, { timeoutMs = GIT_TIMEOUT_MS } = {}) {
|
|
|
169
185
|
"--parents",
|
|
170
186
|
`${head}..${ref}`
|
|
171
187
|
]).split("\n").some((line) => line.split(" ")[1] === head) || !committedOn(path, branch, head)) return noOwnCommits;
|
|
188
|
+
const descendants = new Set(git([
|
|
189
|
+
"rev-list",
|
|
190
|
+
"--ancestry-path",
|
|
191
|
+
`${head}..${ref}`
|
|
192
|
+
]).split("\n"));
|
|
193
|
+
const landed = git([
|
|
194
|
+
"rev-list",
|
|
195
|
+
"--first-parent",
|
|
196
|
+
"--reverse",
|
|
197
|
+
`${head}..${ref}`
|
|
198
|
+
]).split("\n").find((commit) => descendants.has(commit));
|
|
199
|
+
if (!landed) throw new Error(`no commit on ${name} merges ${head}`);
|
|
172
200
|
return {
|
|
173
201
|
merged: true,
|
|
174
202
|
into: name,
|
|
175
203
|
head,
|
|
176
|
-
coversUnpushed: false
|
|
204
|
+
coversUnpushed: false,
|
|
205
|
+
mergedAt: committedAt([landed])
|
|
177
206
|
};
|
|
178
207
|
}
|
|
179
208
|
const files = git([
|
|
@@ -195,12 +224,13 @@ function mergeState(path, { ref, name }, { timeoutMs = GIT_TIMEOUT_MS } = {}) {
|
|
|
195
224
|
"--",
|
|
196
225
|
...pathspec
|
|
197
226
|
]);
|
|
198
|
-
const upstream =
|
|
199
|
-
const patchEquivalent = () => ({
|
|
227
|
+
const upstream = patchIdCommits(log(`${base}..${ref}`, files));
|
|
228
|
+
const patchEquivalent = (ids) => ({
|
|
200
229
|
merged: true,
|
|
201
230
|
into: name,
|
|
202
231
|
head,
|
|
203
|
-
coversUnpushed: upstreamGone(path, branch)
|
|
232
|
+
coversUnpushed: upstreamGone(path, branch),
|
|
233
|
+
mergedAt: committedAt(ids.map((id) => upstream.get(id)))
|
|
204
234
|
});
|
|
205
235
|
const squash = patchIds(patch([
|
|
206
236
|
"diff",
|
|
@@ -208,14 +238,14 @@ function mergeState(path, { ref, name }, { timeoutMs = GIT_TIMEOUT_MS } = {}) {
|
|
|
208
238
|
base,
|
|
209
239
|
head
|
|
210
240
|
]));
|
|
211
|
-
if (squash.length === 1 && upstream.has(squash[0])) return patchEquivalent();
|
|
241
|
+
if (squash.length === 1 && upstream.has(squash[0])) return patchEquivalent(squash);
|
|
212
242
|
if (!git([
|
|
213
243
|
"rev-list",
|
|
214
244
|
"--merges",
|
|
215
245
|
`${base}..${head}`
|
|
216
246
|
])) {
|
|
217
247
|
const own = patchIds(log(`${base}..${head}`));
|
|
218
|
-
if (own.length && own.every((id) => upstream.has(id))) return patchEquivalent();
|
|
248
|
+
if (own.length && own.every((id) => upstream.has(id))) return patchEquivalent(own);
|
|
219
249
|
}
|
|
220
250
|
return notMerged(`not merged into ${name}`);
|
|
221
251
|
} catch (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { N as isOnMountedVolume, O as process_identity_exports, P as listMountedVolumes, _ as loadConfig$1, b as releaseMetroPort, n as allMetroPorts, r as claimMetroPort } from "./config-0jCcMM0E.mjs";
|
|
2
2
|
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
3
|
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { existsSync as existsSync$1, readlinkSync, realpathSync as realpathSync$1 } from "fs";
|
|
4
|
+
import { existsSync as existsSync$1, readlinkSync as readlinkSync$1, realpathSync as realpathSync$1 } from "fs";
|
|
5
5
|
import { join, resolve, sep } from "path";
|
|
6
6
|
import { buildCacheRoot as sharedBuildCache, withDirLock, workspaceId, workspaceName as workspaceName$1, workspaceStateDir as workspaceDir } from "@stim-cli/core";
|
|
7
7
|
import { emulatorLogFile, readJsonObject, readWorkspaceState, readWorkspaceState as readWorkspaceState$1, sharedCcache, sharedCompilationCache, supervisorLogFile, supervisorPidFile, workspaceDerivedData, workspaceLastUsed, workspaceLogErrorIndex, workspaceLogsDir, workspaceMetadataFile, workspaceStateFile, workspaceStateLock } from "@stim-cli/core/state";
|
|
@@ -296,7 +296,7 @@ async function processCwds(pids) {
|
|
|
296
296
|
for (const pid of new Set(pids)) {
|
|
297
297
|
let cwd = null;
|
|
298
298
|
if (process.platform === "linux") try {
|
|
299
|
-
cwd = readlinkSync(`/proc/${pid}/cwd`);
|
|
299
|
+
cwd = readlinkSync$1(`/proc/${pid}/cwd`);
|
|
300
300
|
} catch {}
|
|
301
301
|
if (cwd) cwds.set(pid, cwd);
|
|
302
302
|
else viaLsof.push(pid);
|
|
@@ -317,7 +317,7 @@ async function processCwds(pids) {
|
|
|
317
317
|
}
|
|
318
318
|
function processCwd(pid) {
|
|
319
319
|
if (process.platform === "linux") try {
|
|
320
|
-
return readlinkSync(`/proc/${pid}/cwd`);
|
|
320
|
+
return readlinkSync$1(`/proc/${pid}/cwd`);
|
|
321
321
|
} catch {}
|
|
322
322
|
return parseLsofCwd(getExecutor().runQuiet(`lsof -a -p ${pid} -d cwd -Fn`));
|
|
323
323
|
}
|
|
@@ -406,4 +406,4 @@ function killMetroTree(leader, processToken) {
|
|
|
406
406
|
}
|
|
407
407
|
}
|
|
408
408
|
//#endregion
|
|
409
|
-
export {
|
|
409
|
+
export { workspaceId as A, sharedBuildCache as C, supervisorPidFile as D, supervisorLogFile as E, withDirLock as F, workspaceLogsDir as M, workspaceName$1 as N, workspaceDerivedData as O, workspaceStateFile as P, ensureWorkspaceStorage as S, sharedCompilationCache as T, updateWorkspaceState as _, pidExists as a, writeWorkspaceState as b, resolveProjectMetro as c, isPortFree as d, reserveMetroPort as f, recordWorkspaceUse as g, readWorkspaceState$1 as h, listeningPidsByPort as i, workspaceLogErrorIndex as j, workspaceDir as k, signalProcessTree as l, clearWorkspaceStateKeys as m, killMetroTree as n, processCwd as o, clearWorkspaceStateKey as p, listeningPids as r, processCwds as s, NOT_OURS_FOREIGN_CWD as t, isMetroRunning as u, withWorkspaceStateLock as v, sharedCcache as w, emulatorLogFile as x, workspaceLastUsed as y };
|
|
@@ -1,13 +1,69 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as ownership_claim_exports, E as withConfigLock, O as process_identity_exports, S as saveConfig, _ as loadConfig, c as ensureConfig, d as getConfigDir, p as getProject } from "./config-0jCcMM0E.mjs";
|
|
2
2
|
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { d as isPortFree, l as signalProcessTree, r as listeningPids } from "./metro-BPeY1YMh.mjs";
|
|
4
|
+
import { n as declareSpawnsOn, o as stopDeclaringSpawnsOn } from "./spawn-claims-PT8aQpCa.mjs";
|
|
5
5
|
import { realpathSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
//#region src/engine/workspace-process-lock.ts
|
|
8
|
+
const DEFAULT_WAIT_MS = 6e4;
|
|
9
|
+
const POLL_MS = 25;
|
|
10
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
11
|
+
function workspaceProcessLockError(err) {
|
|
12
|
+
const code = err?.code;
|
|
13
|
+
if (code === "STIM_LOCK_REFUSED") return "refused";
|
|
14
|
+
if (code === "STIM_LOCK_TIMEOUT") return "timeout";
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
function workspaceProcessLockPath(root, name, external) {
|
|
18
|
+
return external ? join(root, `${name}.lock`) : join(root, ".stim", `${name}.lock`);
|
|
19
|
+
}
|
|
20
|
+
async function withWorkspaceProcessLock(root, name, fn, { now = Date.now, sleep = defaultSleep, waitMs = DEFAULT_WAIT_MS, ownerPurpose, details = {}, onHeld, rejectOwnerPurposes = [], external = false, declareSpawns = false } = {}) {
|
|
21
|
+
const path = workspaceProcessLockPath(root, name, external);
|
|
22
|
+
const deadline = now() + waitMs;
|
|
23
|
+
for (;;) {
|
|
24
|
+
const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
|
|
25
|
+
root: path,
|
|
26
|
+
mode: "exclusive",
|
|
27
|
+
label: `${name} lock`,
|
|
28
|
+
details: ownerPurpose ? {
|
|
29
|
+
...details,
|
|
30
|
+
purpose: ownerPurpose
|
|
31
|
+
} : details
|
|
32
|
+
});
|
|
33
|
+
if (attempt.pending) (0, ownership_claim_exports.releaseClaim)(attempt.pending);
|
|
34
|
+
if (attempt.acquired) {
|
|
35
|
+
if (declareSpawns) declareSpawnsOn(attempt.acquired);
|
|
36
|
+
try {
|
|
37
|
+
return await fn(attempt.acquired);
|
|
38
|
+
} finally {
|
|
39
|
+
stopDeclaringSpawnsOn(attempt.acquired);
|
|
40
|
+
(0, ownership_claim_exports.releaseClaim)(attempt.acquired);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const holder = attempt.held ?? attempt.waitingFor?.[0];
|
|
44
|
+
const purpose = holder?.details.purpose;
|
|
45
|
+
if (typeof purpose === "string" && rejectOwnerPurposes.includes(purpose)) {
|
|
46
|
+
const error = /* @__PURE__ */ new Error(`The ${name} lock at ${path} is held for ${purpose}.`);
|
|
47
|
+
error.code = "STIM_LOCK_REFUSED";
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
if (holder && onHeld) await onHeld(holder);
|
|
51
|
+
if (now() >= deadline) {
|
|
52
|
+
const error = /* @__PURE__ */ new Error(`Timed out waiting for the ${name} lock at ${path}.`);
|
|
53
|
+
error.code = "STIM_LOCK_TIMEOUT";
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
await sleep(POLL_MS);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
6
60
|
//#region src/named-ports.ts
|
|
7
61
|
const FIRST_PORT = 8900;
|
|
8
62
|
const LAST_PORT = 8999;
|
|
63
|
+
const BROWSER_LABEL = "web-cdp";
|
|
9
64
|
function validatePortLabel(label) {
|
|
10
65
|
if (label === "metro") throw new Error("metro is managed by stim start and stim stop; choose another label.");
|
|
66
|
+
if (label === BROWSER_LABEL) throw new Error(`${BROWSER_LABEL} is managed by stim web and stim stop; choose another label.`);
|
|
11
67
|
if (!/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/.test(label)) throw new Error("A port label must start with a letter and contain at most 64 letters, digits, underscores or hyphens.");
|
|
12
68
|
}
|
|
13
69
|
function portListeners(port, platform) {
|
|
@@ -51,8 +107,24 @@ function processCommand(pid, platform) {
|
|
|
51
107
|
function withPortsLock(fn) {
|
|
52
108
|
return withWorkspaceProcessLock(getConfigDir(), "ports", fn, { external: true });
|
|
53
109
|
}
|
|
54
|
-
async function getNamedPort(projectPath, label,
|
|
110
|
+
async function getNamedPort(projectPath, label, options = {}) {
|
|
55
111
|
validatePortLabel(label);
|
|
112
|
+
return allocateNamedPort(projectPath, label, options);
|
|
113
|
+
}
|
|
114
|
+
/** The loopback DevTools port of the workspace's owned Chrome, allocated like a named port under `web-cdp`. */
|
|
115
|
+
function reserveBrowserPort(projectPath, options = {}) {
|
|
116
|
+
return allocateNamedPort(projectPath, BROWSER_LABEL, options);
|
|
117
|
+
}
|
|
118
|
+
function releaseBrowserPort(projectPath, port) {
|
|
119
|
+
return withPortsLock(async () => withConfigLock(() => {
|
|
120
|
+
const cfg = loadConfig();
|
|
121
|
+
const ports = cfg?.projects?.[projectPath]?.ports;
|
|
122
|
+
if (!cfg || ports?.[BROWSER_LABEL] !== port) return;
|
|
123
|
+
delete ports[BROWSER_LABEL];
|
|
124
|
+
saveConfig(cfg);
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
async function allocateNamedPort(projectPath, label, { isFree = async (port) => await isPortFree(port) && portListeners(port, process.platform).length === 0, log = console.error }) {
|
|
56
128
|
const root = realpathSync(projectPath);
|
|
57
129
|
return withPortsLock(async () => {
|
|
58
130
|
const existing = getProject(root)?.ports;
|
|
@@ -121,7 +193,7 @@ async function clearNamedPorts(projectPath, { label, stop = false, dryRun = fals
|
|
|
121
193
|
if (label !== void 0) validatePortLabel(label);
|
|
122
194
|
await withPortsLock(async () => {
|
|
123
195
|
const ports = getProject(projectPath)?.ports ?? {};
|
|
124
|
-
const selected = Object.entries(ports).filter(([name]) => label === void 0 || name === label);
|
|
196
|
+
const selected = Object.entries(ports).filter(([name]) => name !== BROWSER_LABEL && (label === void 0 || name === label));
|
|
125
197
|
if (stop && selected.length > 0 && platform !== "win32" && !getExecutor().findExecutable("lsof")) throw new Error("Cannot stop named ports: lsof is not installed.");
|
|
126
198
|
const failures = [];
|
|
127
199
|
for (const [name, port] of selected) try {
|
|
@@ -149,4 +221,4 @@ async function clearNamedPorts(projectPath, { label, stop = false, dryRun = fals
|
|
|
149
221
|
});
|
|
150
222
|
}
|
|
151
223
|
//#endregion
|
|
152
|
-
export { getNamedPort as n, clearNamedPorts as t };
|
|
224
|
+
export { withWorkspaceProcessLock as a, reserveBrowserPort as i, getNamedPort as n, workspaceProcessLockError as o, releaseBrowserPort as r, workspaceProcessLockPath as s, clearNamedPorts as t };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { S as ensureWorkspaceStorage, c as resolveProjectMetro, h as readWorkspaceState$1 } from "./metro-BPeY1YMh.mjs";
|
|
2
|
+
import { l as phaseLine } from "./command-output-BIL7rlX7.mjs";
|
|
3
|
+
import { r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING } from "./launch-verify-CuJaBpEa.mjs";
|
|
4
|
+
import { s as startDevServer } from "./start-BZhItkiL.mjs";
|
|
5
|
+
import { readIdleStop } from "@stim-cli/core/state";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
//#region src/commands/native-runtime.ts
|
|
8
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
9
|
+
async function ensureDevServer({ root, port, settings, remote, note, resolve = resolveProjectMetro, start = startDevServer, readState = readWorkspaceState$1 }) {
|
|
10
|
+
const held = port === null ? null : (await resolve(port, root)).metro;
|
|
11
|
+
if (port !== null && held) return {
|
|
12
|
+
ok: true,
|
|
13
|
+
port,
|
|
14
|
+
pid: held.pid ?? null,
|
|
15
|
+
devServer: null,
|
|
16
|
+
reclaimed: []
|
|
17
|
+
};
|
|
18
|
+
const reason = readIdleStop(readState(root)) ? "stopped (idle)" : "not running";
|
|
19
|
+
note(chalk.dim(phaseLine("metro", `dev server ${reason}; starting it${remote ? " for a remote device" : ""}`)));
|
|
20
|
+
const result = await start({
|
|
21
|
+
root,
|
|
22
|
+
settings,
|
|
23
|
+
remote,
|
|
24
|
+
out: note,
|
|
25
|
+
note
|
|
26
|
+
});
|
|
27
|
+
if (!result.ok) return {
|
|
28
|
+
ok: false,
|
|
29
|
+
code: result.error.code,
|
|
30
|
+
message: `Could not start this workspace's dev server: ${result.error.message}`,
|
|
31
|
+
remedy: result.error.remedy,
|
|
32
|
+
lines: result.lines,
|
|
33
|
+
reclaimed: result.reclaimed
|
|
34
|
+
};
|
|
35
|
+
const { facts } = result;
|
|
36
|
+
return {
|
|
37
|
+
ok: true,
|
|
38
|
+
port: facts.port,
|
|
39
|
+
pid: facts.supervisorPid,
|
|
40
|
+
devServer: facts.alreadyRunning ? null : {
|
|
41
|
+
started: true,
|
|
42
|
+
reason
|
|
43
|
+
},
|
|
44
|
+
reclaimed: result.reclaimed
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async function ensureWorkspaceStorageSafely(root, { note = (_line) => {} } = {}) {
|
|
48
|
+
try {
|
|
49
|
+
return ensureWorkspaceStorage(root);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
note(chalk.dim(`Could not prepare this workspace's Stim state: ${err?.message || err}`));
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function launchOutcomeRecord({ launchState, release, bundleId, configuration, metroPort }) {
|
|
56
|
+
const unverified = launchState === LAUNCH_UNVERIFIED;
|
|
57
|
+
const bundling = launchState === LAUNCH_BUNDLING;
|
|
58
|
+
let msg;
|
|
59
|
+
if (release) msg = unverified ? `${bundleId} could not be verified as running after its ${configuration} launch` : `${bundleId} is running its embedded ${configuration} bundle`;
|
|
60
|
+
else if (unverified) msg = `no bundle request from ${bundleId} reached this workspace's Metro on port ${metroPort}`;
|
|
61
|
+
else if (bundling) msg = `${bundleId} requested a bundle from this workspace's Metro on port ${metroPort}; it was still being built when the launch check ended`;
|
|
62
|
+
else msg = `${bundleId} fetched a bundle from this workspace's Metro on port ${metroPort}`;
|
|
63
|
+
return {
|
|
64
|
+
src: "build",
|
|
65
|
+
level: unverified ? "warn" : "info",
|
|
66
|
+
event: unverified ? "launch_unverified" : bundling ? "launch_bundling" : "launch_verified",
|
|
67
|
+
msg
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function isPhysicalDeviceRequest(flag) {
|
|
71
|
+
return flag !== null && flag !== void 0 && flag !== false;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { sleep as a, launchOutcomeRecord as i, ensureWorkspaceStorageSafely as n, isPhysicalDeviceRequest as r, ensureDevServer as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { O as process_identity_exports } from "./config-0jCcMM0E.mjs";
|
|
2
|
+
import { a as pidExists, b as writeWorkspaceState, h as readWorkspaceState, v as withWorkspaceStateLock } from "./metro-BPeY1YMh.mjs";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
//#region src/collector/state.ts
|
|
5
5
|
function collectorRoot(root) {
|