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,27 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as deviceSlotPlatforms, C as setDevice, E as withConfigLock$1, S as saveConfig, U as projectDeviceSlots, _ as loadConfig$1, a as clearDevice, d as getConfigDir, i as clearAndroidBootPending, k as acquireAvdClaim, t as allConsolePortsAndSerials, y as releaseAndroidConsolePort } from "./config-0jCcMM0E.mjs";
|
|
2
2
|
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, c as gitCommonDirOnDisk } from "./worktree-
|
|
5
|
-
import {
|
|
6
|
-
import { a as spawnDeclared } from "./spawn-claims-
|
|
7
|
-
import {
|
|
3
|
+
import { A as workspaceId, C as sharedBuildCache, _ as updateWorkspaceState, a as pidExists, h as readWorkspaceState$1 } from "./metro-BPeY1YMh.mjs";
|
|
4
|
+
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, c as gitCommonDirOnDisk } from "./worktree-DeBQSVtR.mjs";
|
|
5
|
+
import { c as ownedDeviceLabel, r as detectIsExpo, s as isPackageResolvable } from "./project-DGeclhvO.mjs";
|
|
6
|
+
import { a as spawnDeclared } from "./spawn-claims-PT8aQpCa.mjs";
|
|
7
|
+
import { a as withWorkspaceProcessLock } from "./named-ports-n7tnJsI8.mjs";
|
|
8
8
|
import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
|
|
9
|
-
import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting, v as iosSimSlimProfileSetting } from "./settings-
|
|
13
|
-
import {
|
|
14
|
-
import { E as configuredIosSimulatorViewer, O as hostMemoryPressureAdvice, S as resolveOwnedIosSim, b as resetIosPrivacy, c as iosSimulatorFailureAdvice, d as listIosDeviceTypes, g as parseRuntimeVersion, k as readHostMemoryPressure, l as listAllIosSims, m as ownedSimName, n as bootIosSim, r as createOwnedIosSim, v as renameIosSim, x as resolveIosCreation, y as resetIosKeychain } from "./ios-
|
|
15
|
-
import { o as estimateBuild } from "./build-progress-
|
|
16
|
-
import { i as expoBinRefusal, r as expoBinPath, t as cleanLine } from "./server-expo-
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import { r as readStats } from "./stats-BAgJaLA4.mjs";
|
|
22
|
-
import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-B5SPD--o.mjs";
|
|
9
|
+
import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-BIL7rlX7.mjs";
|
|
10
|
+
import { C as diffFingerprintSources, I as storedSources, w as entryDir, y as artifactIn } from "./device-lease-CL9dZf73.mjs";
|
|
11
|
+
import { $ as waitForBoot, A as listInstalledSystemImages, D as listAdbDevices, I as ownedAvdMatchesConfiguration, L as ownedAvdName, N as nextConsolePort, P as ownedAvdDeviceProfile, T as getAvdNameForSerial, U as pickDefaultSystemImage, _ as bootAndroidEmulator, at as dropParked, ct as parkedMaxSetting, d as assertOwnedAvdStopped, dt as selectParked, h as avdPoolConfiguration, it as adoptParked, k as listAvds, lt as readParked, n as OWNED_AVD_CONFIG_DEFAULTS, ot as isLegacyDeletionClaim, q as resolveOwnedAvdSerial, t as DEFAULT_AVD_DEVICE_PROFILE, v as configureNewOwnedAvd, y as createOwnedAvd, z as ownedAvdSystemImage } from "./android-qx_31wJo.mjs";
|
|
12
|
+
import { l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting, v as iosSimSlimProfileSetting } from "./settings-xB0Frkg6.mjs";
|
|
13
|
+
import { f as teardownOwnedAvd, g as teardownParkedIosSim, h as teardownParkedAvd } from "./ownership-PUP4Dwai.mjs";
|
|
14
|
+
import { E as configuredIosSimulatorViewer, O as hostMemoryPressureAdvice, S as resolveOwnedIosSim, b as resetIosPrivacy, c as iosSimulatorFailureAdvice, d as listIosDeviceTypes, f as listIosRuntimes, g as parseRuntimeVersion, k as readHostMemoryPressure, l as listAllIosSims, m as ownedSimName, n as bootIosSim, r as createOwnedIosSim, v as renameIosSim, x as resolveIosCreation, y as resetIosKeychain } from "./ios-CmEzZiOj.mjs";
|
|
15
|
+
import { o as estimateBuild } from "./build-progress-l7d-X-ZP.mjs";
|
|
16
|
+
import { i as expoBinRefusal, r as expoBinPath, t as cleanLine } from "./server-expo-DUCg52-K.mjs";
|
|
17
|
+
import { a as easAuthNote } from "./remote-cache--aehr4Pr.mjs";
|
|
18
|
+
import { a as liveOwnedDeviceCount, i as deviceTypeMismatch, o as runtimeMismatch, t as reconcileSimSlim } from "./simslim-Bbg9UWrt.mjs";
|
|
19
|
+
import { r as readStats } from "./stats-D8rUTPHd.mjs";
|
|
20
|
+
import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-LM6A8cNZ.mjs";
|
|
23
21
|
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
24
|
-
import { LAST_BUILD_KEYS, isJsonObject, loadConfig,
|
|
22
|
+
import { LAST_BUILD_KEYS, isJsonObject, loadConfig, readJsonObject, readWorkspaceState } from "@stim-cli/core/state";
|
|
25
23
|
import { basename, dirname, join, relative } from "node:path";
|
|
26
24
|
import { claimRemoveCommand, clearClaimChild, markClaimChildPending, processGroupAlive, releaseClaim, setClaimChild } from "@stim-cli/core/ownership-claim";
|
|
27
25
|
import { captureProcessIdentity } from "@stim-cli/core/process-identity";
|
|
@@ -93,73 +91,6 @@ async function warmMetro({ port, platform, isExpo, appId, bundleUrl }) {
|
|
|
93
91
|
} catch {}
|
|
94
92
|
}
|
|
95
93
|
//#endregion
|
|
96
|
-
//#region src/commands/native-runtime.ts
|
|
97
|
-
const sleep$1 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
98
|
-
async function ensureDevServer({ root, port, settings, remote, note, resolve = resolveProjectMetro, start = startDevServer, readState = readWorkspaceState$1 }) {
|
|
99
|
-
const held = port === null ? null : (await resolve(port, root)).metro;
|
|
100
|
-
if (port !== null && held) return {
|
|
101
|
-
ok: true,
|
|
102
|
-
port,
|
|
103
|
-
pid: held.pid ?? null,
|
|
104
|
-
devServer: null,
|
|
105
|
-
reclaimed: []
|
|
106
|
-
};
|
|
107
|
-
const reason = readIdleStop(readState(root)) ? "stopped (idle)" : "not running";
|
|
108
|
-
note(chalk.dim(phaseLine("metro", `dev server ${reason}; starting it${remote ? " for a remote device" : ""}`)));
|
|
109
|
-
const result = await start({
|
|
110
|
-
root,
|
|
111
|
-
settings,
|
|
112
|
-
remote,
|
|
113
|
-
out: note,
|
|
114
|
-
note
|
|
115
|
-
});
|
|
116
|
-
if (!result.ok) return {
|
|
117
|
-
ok: false,
|
|
118
|
-
code: result.error.code,
|
|
119
|
-
message: `Could not start this workspace's dev server: ${result.error.message}`,
|
|
120
|
-
remedy: result.error.remedy,
|
|
121
|
-
lines: result.lines,
|
|
122
|
-
reclaimed: result.reclaimed
|
|
123
|
-
};
|
|
124
|
-
const { facts } = result;
|
|
125
|
-
return {
|
|
126
|
-
ok: true,
|
|
127
|
-
port: facts.port,
|
|
128
|
-
pid: facts.supervisorPid,
|
|
129
|
-
devServer: facts.alreadyRunning ? null : {
|
|
130
|
-
started: true,
|
|
131
|
-
reason
|
|
132
|
-
},
|
|
133
|
-
reclaimed: result.reclaimed
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
async function ensureWorkspaceStorageSafely(root, { note = (_line) => {} } = {}) {
|
|
137
|
-
try {
|
|
138
|
-
return ensureWorkspaceStorage(root);
|
|
139
|
-
} catch (err) {
|
|
140
|
-
note(chalk.dim(`Could not prepare this workspace's Stim state: ${err?.message || err}`));
|
|
141
|
-
throw err;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
function launchOutcomeRecord({ launchState, release, bundleId, configuration, metroPort }) {
|
|
145
|
-
const unverified = launchState === LAUNCH_UNVERIFIED;
|
|
146
|
-
const bundling = launchState === LAUNCH_BUNDLING;
|
|
147
|
-
let msg;
|
|
148
|
-
if (release) msg = unverified ? `${bundleId} could not be verified as running after its ${configuration} launch` : `${bundleId} is running its embedded ${configuration} bundle`;
|
|
149
|
-
else if (unverified) msg = `no bundle request from ${bundleId} reached this workspace's Metro on port ${metroPort}`;
|
|
150
|
-
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`;
|
|
151
|
-
else msg = `${bundleId} fetched a bundle from this workspace's Metro on port ${metroPort}`;
|
|
152
|
-
return {
|
|
153
|
-
src: "build",
|
|
154
|
-
level: unverified ? "warn" : "info",
|
|
155
|
-
event: unverified ? "launch_unverified" : bundling ? "launch_bundling" : "launch_verified",
|
|
156
|
-
msg
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
function isPhysicalDeviceRequest(flag) {
|
|
160
|
-
return flag !== null && flag !== void 0 && flag !== false;
|
|
161
|
-
}
|
|
162
|
-
//#endregion
|
|
163
94
|
//#region src/engine/device-ios.ts
|
|
164
95
|
function startIosBoot(udid, configure, label, out, simulatorApp) {
|
|
165
96
|
const done = (async () => {
|
|
@@ -183,13 +114,15 @@ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, l
|
|
|
183
114
|
if (record?.deviceUdid) {
|
|
184
115
|
if (record.owned) {
|
|
185
116
|
const resolved = resolveOwnedIosSim(record.deviceUdid);
|
|
186
|
-
if (resolved.notOwned) note(chalk.yellow(`Note: recorded sim is now named "${resolved.notOwned}", not Stim-owned
|
|
117
|
+
if (resolved.notOwned) note(chalk.yellow(`Note: recorded sim is now named "${resolved.notOwned}", not Stim-owned -- creating a fresh owned sim instead of booting it.`));
|
|
187
118
|
else if (resolved.missing) {} else {
|
|
188
119
|
const sim = resolved.sim;
|
|
189
120
|
const wantedType = flags.deviceType || settings?.ios?.deviceType;
|
|
190
121
|
const deviceTypes = listIosDeviceTypes();
|
|
191
122
|
const mismatch = deviceTypeMismatch(sim.deviceTypeIdentifier, wantedType, deviceTypes);
|
|
192
123
|
if (mismatch) throw new Error(`${mismatch}. Stim will not silently boot a different model. Run \`stim worktree remove\` (or \`stim gc --delete\`) to reap the current sim, then \`stim ios\` again to create the requested one.`);
|
|
124
|
+
const versionMismatch = flags.runtimeFlag ? runtimeMismatch(sim.runtime, flags.runtimeFlag, listIosRuntimes()) : null;
|
|
125
|
+
if (versionMismatch) throw new Error(`${versionMismatch}. Stim will not silently boot a different iOS version. Run \`stim worktree remove\` (or \`stim gc --delete\`) to reap the current sim, then \`stim ios\` again to create the requested one, or pass \`--slot <name>\` to create it beside the current one.`);
|
|
193
126
|
const model = {
|
|
194
127
|
model: deviceTypes.find((d) => d.identifier === sim.deviceTypeIdentifier)?.name ?? null,
|
|
195
128
|
runtime: parseRuntimeVersion(sim.runtime)
|
|
@@ -641,7 +574,10 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
|
|
|
641
574
|
avdName,
|
|
642
575
|
owned: true,
|
|
643
576
|
deviceName: avdName,
|
|
644
|
-
...prepared.created ? {
|
|
577
|
+
...prepared.created ? {
|
|
578
|
+
poolConfiguration: configuration,
|
|
579
|
+
bootPending: true
|
|
580
|
+
} : {},
|
|
645
581
|
...prepared.consolePort ? { consolePort: prepared.consolePort } : {}
|
|
646
582
|
}, slot);
|
|
647
583
|
});
|
|
@@ -676,10 +612,13 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
|
|
|
676
612
|
const ANDROID_FRESH_BOOT_TIMEOUT_MS = process.platform === "win32" ? 12e5 : 12e4;
|
|
677
613
|
const ANDROID_BOOT_TIMEOUT_MS = process.platform === "win32" ? 12e5 : 24e4;
|
|
678
614
|
async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoot, label, settings, flags, note, out, logFile, alive, configureAvd, teardownAvd }) {
|
|
679
|
-
const
|
|
615
|
+
const projectAvdConfig = androidAvdConfigSetting(settings, settingsRoot);
|
|
616
|
+
const avdConfig = {
|
|
617
|
+
...OWNED_AVD_CONFIG_DEFAULTS,
|
|
618
|
+
...projectAvdConfig
|
|
619
|
+
};
|
|
680
620
|
const requestedProfile = flags.deviceProfile || settings.android?.deviceProfile || null;
|
|
681
|
-
|
|
682
|
-
const configuration = avdPoolConfiguration(androidDataPartitionSizeGbSetting(settings), avdConfig, deviceProfile);
|
|
621
|
+
let replacedProfile = null;
|
|
683
622
|
if (record?.setupIncomplete && record.avdName) {
|
|
684
623
|
const avdName = record.avdName;
|
|
685
624
|
const cleanup = teardownAvd(avdName, {
|
|
@@ -696,12 +635,36 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
696
635
|
if (cleanup.status === "failed" || cleanup.status === "skipped") throw new AvdRecoveryError(`Owned AVD ${record.avdName} has incomplete setup and could not be deleted (${cleanup.reason || cleanup.status}). Fix the cause, then retry; Stim kept the device record for cleanup.`, "Wait for any active AVD operation to finish. If the refusal names an unresolved claim, inspect its creator and native processes before removing only that claim. Retry `stim android` to reconcile the incomplete device.");
|
|
697
636
|
record = null;
|
|
698
637
|
}
|
|
638
|
+
const requestedImage = flags.systemImageFlag || null;
|
|
639
|
+
const currentImage = requestedImage && record?.owned && record.avdName ? ownedAvdSystemImage(record.avdName) : null;
|
|
640
|
+
if (record?.avdName && requestedImage && currentImage && currentImage !== requestedImage) {
|
|
641
|
+
const avdName = record.avdName;
|
|
642
|
+
if (!record.bootPending) throw new AvdRecoveryError(`this project's emulator ${avdName} uses system image ${currentImage}, but ${requestedImage} was requested. --system-image replaces only an emulator that never finished a boot, and this one has.`, "Run `stim worktree remove` (or `stim gc --delete`) to reap the current emulator, then `stim android` again to create the requested one, or pass `--slot <name>` to create it beside the current one.");
|
|
643
|
+
replacedProfile = ownedAvdDeviceProfile(avdName);
|
|
644
|
+
const cleanup = teardownAvd(avdName, {
|
|
645
|
+
del: true,
|
|
646
|
+
owner: {
|
|
647
|
+
projectPath,
|
|
648
|
+
slot,
|
|
649
|
+
expectedRecord: record
|
|
650
|
+
},
|
|
651
|
+
onRemoved: () => {
|
|
652
|
+
clearDevice(projectPath, "android", slot, avdName);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
if (cleanup.status === "failed" || cleanup.status === "skipped") throw new AvdRecoveryError(`Owned AVD ${avdName} never finished a boot, but could not be deleted to create one on ${requestedImage} (${cleanup.reason || cleanup.status}). Stim kept the device record.`, "Fix the cause, then retry, or pass `--slot <name>` to create the requested emulator beside it.");
|
|
656
|
+
out(phaseLine("device", `deleted ${avdName}, which never finished a boot, to create one on ${requestedImage}`));
|
|
657
|
+
record = null;
|
|
658
|
+
}
|
|
659
|
+
const deviceProfile = requestedProfile ?? replacedProfile ?? DEFAULT_AVD_DEVICE_PROFILE;
|
|
660
|
+
const configuration = avdPoolConfiguration(androidDataPartitionSizeGbSetting(settings), avdConfig, deviceProfile);
|
|
661
|
+
const configurationBeforeDefaults = avdPoolConfiguration(androidDataPartitionSizeGbSetting(settings), projectAvdConfig, deviceProfile);
|
|
699
662
|
if (record?.avdName) {
|
|
700
663
|
if (record.owned) {
|
|
701
664
|
const resolved = resolveOwnedAvdSerial(record.avdName);
|
|
702
665
|
const currentProfile = resolved.notOwned || resolved.missing ? null : ownedAvdDeviceProfile(record.avdName);
|
|
703
666
|
if (requestedProfile && currentProfile && currentProfile !== requestedProfile) throw new AvdRecoveryError(`this project's emulator uses device profile ${currentProfile}, but ${requestedProfile} was requested. Stim will not silently boot a different model.`, "Run `stim worktree remove` (or `stim gc --delete`) to reap the current emulator, then `stim android` again to create the requested one, or pass `--slot <name>` to create it beside the current one.");
|
|
704
|
-
if (resolved.notOwned) note(chalk.yellow(`Note: recorded AVD ${record.avdName} is not Stim-owned
|
|
667
|
+
if (resolved.notOwned) note(chalk.yellow(`Note: recorded AVD ${record.avdName} is not Stim-owned -- creating a fresh owned AVD instead of reusing it.`));
|
|
705
668
|
else if (resolved.serial) {
|
|
706
669
|
const consolePort = Number(resolved.serial.replace(/^emulator-/, ""));
|
|
707
670
|
if (record.consolePort && record.consolePort !== consolePort) out(phaseLine("device", `${record.avdName} changed serial (emulator-${record.consolePort} -> ${resolved.serial}); reconnecting this run, reopen agent-device on ${resolved.serial}`));
|
|
@@ -748,7 +711,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
748
711
|
}
|
|
749
712
|
if (parkedMaxSetting("android").max > 0) {
|
|
750
713
|
const systemImage = pickDefaultSystemImage(listInstalledSystemImages(), { systemImage: flags.systemImage || settings.android?.systemImage })?.pkg;
|
|
751
|
-
const candidates = readParked("android").filter((entry) => entry.systemImage === systemImage && entry.configuration === configuration).toSorted((a, b) => a.parkedAt.localeCompare(b.parkedAt));
|
|
714
|
+
const candidates = readParked("android").filter((entry) => entry.systemImage === systemImage && (entry.configuration === configuration || entry.configuration === configurationBeforeDefaults)).toSorted((a, b) => a.parkedAt.localeCompare(b.parkedAt));
|
|
752
715
|
for (const parked of candidates) {
|
|
753
716
|
if (isLegacyDeletionClaim(parked.deletionClaim)) continue;
|
|
754
717
|
const resolved = resolveOwnedAvdSerial(parked.name);
|
|
@@ -764,7 +727,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
764
727
|
out(phaseLine("device", `kept parked ${parked.name}: ${String(error?.message || error)}`));
|
|
765
728
|
continue;
|
|
766
729
|
}
|
|
767
|
-
if (ownedAvdSystemImage(parked.name) !== systemImage || !ownedAvdMatchesConfiguration(parked.name, configuration)) {
|
|
730
|
+
if (ownedAvdSystemImage(parked.name) !== systemImage || !ownedAvdMatchesConfiguration(parked.name, parked.configuration)) {
|
|
768
731
|
const result = teardownParkedAvd(parked.name);
|
|
769
732
|
if (result.status === "failed") out(phaseLine("device", `kept ${parked.name}: ${result.reason}`));
|
|
770
733
|
continue;
|
|
@@ -773,7 +736,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
773
736
|
avdName: parked.name,
|
|
774
737
|
deviceName: parked.name,
|
|
775
738
|
owned: true,
|
|
776
|
-
poolConfiguration: configuration,
|
|
739
|
+
poolConfiguration: parked.configuration,
|
|
777
740
|
adoptionPending: true
|
|
778
741
|
};
|
|
779
742
|
if (!adoptParked({
|
|
@@ -783,6 +746,15 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
783
746
|
udid: parked.udid,
|
|
784
747
|
device: adopted
|
|
785
748
|
})) continue;
|
|
749
|
+
if (parked.configuration !== configuration) try {
|
|
750
|
+
configureAvd(parked.name, {
|
|
751
|
+
dataPartitionSizeGb: androidDataPartitionSizeGbSetting(settings),
|
|
752
|
+
avdConfig
|
|
753
|
+
});
|
|
754
|
+
adopted.poolConfiguration = configuration;
|
|
755
|
+
} catch (error) {
|
|
756
|
+
out(phaseLine("device", `${parked.name} keeps its old AVD settings: ${String(error?.message || error)}`));
|
|
757
|
+
}
|
|
786
758
|
return {
|
|
787
759
|
...await bootOwnedAvdOnFreshPort({
|
|
788
760
|
avdName: parked.name,
|
|
@@ -824,7 +796,10 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
824
796
|
return {
|
|
825
797
|
...await bootOwnedAvdOnFreshPort({
|
|
826
798
|
avdName: created.avdName,
|
|
827
|
-
metadata: created.created ? {
|
|
799
|
+
metadata: created.created ? {
|
|
800
|
+
poolConfiguration: configuration,
|
|
801
|
+
bootPending: true
|
|
802
|
+
} : void 0,
|
|
828
803
|
projectPath,
|
|
829
804
|
slot,
|
|
830
805
|
deviceName: created.avdName,
|
|
@@ -843,6 +818,7 @@ function claimAndroidConsolePort({ projectPath, slot, avdName, deviceName, liveP
|
|
|
843
818
|
const claim = {
|
|
844
819
|
...metadata?.poolConfiguration ? { poolConfiguration: metadata.poolConfiguration } : {},
|
|
845
820
|
...metadata?.adoptionPending ? { adoptionPending: true } : {},
|
|
821
|
+
...metadata?.bootPending ? { bootPending: true } : {},
|
|
846
822
|
avdName,
|
|
847
823
|
consolePort,
|
|
848
824
|
owned: true,
|
|
@@ -880,8 +856,10 @@ async function bootOwnedAvdOnFreshPort({ avdName, metadata, projectPath, slot, d
|
|
|
880
856
|
if (result.failed) throw new AvdBootError(result.reason, result.remedy);
|
|
881
857
|
const running = getAvdNameForSerial(serial);
|
|
882
858
|
if (running && running !== avdName) throw new Error(`${serial} is running AVD ${running}, not this workspace's owned AVD ${avdName}; refusing to use it.`);
|
|
859
|
+
clearAndroidBootPending(projectPath, avdName, slot);
|
|
860
|
+
const { bootPending: _booted, ...booted } = claim;
|
|
883
861
|
return {
|
|
884
|
-
...
|
|
862
|
+
...booted,
|
|
885
863
|
serial
|
|
886
864
|
};
|
|
887
865
|
} catch (error) {
|
|
@@ -958,19 +936,24 @@ async function ensureAndroidBooted({ device, projectPath, slot, timeoutMs, out,
|
|
|
958
936
|
};
|
|
959
937
|
if (resolved.notOwned) return {
|
|
960
938
|
failed: true,
|
|
961
|
-
reason: `AVD ${device.avdName} is not Stim-owned
|
|
939
|
+
reason: `AVD ${device.avdName} is not Stim-owned; refusing to boot it.`
|
|
962
940
|
};
|
|
963
|
-
|
|
941
|
+
const avdName = device.avdName;
|
|
942
|
+
const booted = (result) => {
|
|
943
|
+
if (!result.failed) clearAndroidBootPending(projectPath, avdName, slot);
|
|
944
|
+
return result;
|
|
945
|
+
};
|
|
946
|
+
if (resolved.serial) return booted(await waitForAndroidBoot({
|
|
964
947
|
serial: resolved.serial,
|
|
965
948
|
timeoutMs,
|
|
966
949
|
out
|
|
967
|
-
});
|
|
950
|
+
}));
|
|
968
951
|
const freshSerial = `emulator-${device.consolePort}`;
|
|
969
|
-
if (device.owned && device.serial === freshSerial) return waitForAndroidBoot({
|
|
952
|
+
if (device.owned && device.serial === freshSerial) return booted(await waitForAndroidBoot({
|
|
970
953
|
serial: freshSerial,
|
|
971
954
|
timeoutMs,
|
|
972
955
|
out
|
|
973
|
-
});
|
|
956
|
+
}));
|
|
974
957
|
const claim = claimAndroidConsolePort({
|
|
975
958
|
projectPath,
|
|
976
959
|
slot,
|
|
@@ -1000,7 +983,7 @@ async function ensureAndroidBooted({ device, projectPath, slot, timeoutMs, out,
|
|
|
1000
983
|
out
|
|
1001
984
|
});
|
|
1002
985
|
if (result.failed) releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
|
|
1003
|
-
return result;
|
|
986
|
+
return booted(result);
|
|
1004
987
|
}
|
|
1005
988
|
//#endregion
|
|
1006
989
|
//#region src/engine/device.ts
|
|
@@ -1776,6 +1759,28 @@ function explainBuildMiss({ root, platform, current, rekeyedBy = [], baselineDep
|
|
|
1776
1759
|
previousHash: baseline?.fingerprint ?? null
|
|
1777
1760
|
};
|
|
1778
1761
|
}
|
|
1762
|
+
function predictBuildMiss({ root, platform, current, prebuild }) {
|
|
1763
|
+
const { reason } = explainBuildMiss({
|
|
1764
|
+
root,
|
|
1765
|
+
platform,
|
|
1766
|
+
current
|
|
1767
|
+
});
|
|
1768
|
+
const baseline = reason.baseline && {
|
|
1769
|
+
fingerprint: reason.baseline.fingerprint,
|
|
1770
|
+
from: reason.baseline.from
|
|
1771
|
+
};
|
|
1772
|
+
if (!prebuild || !baseline) return {
|
|
1773
|
+
...reason,
|
|
1774
|
+
baseline
|
|
1775
|
+
};
|
|
1776
|
+
const dir = platform === "ios" ? "ios/" : "android/";
|
|
1777
|
+
return {
|
|
1778
|
+
...reason,
|
|
1779
|
+
kind: "prebuild-pending",
|
|
1780
|
+
summary: `${reason.summary} (before prebuild ${prebuild}s ${dir})`,
|
|
1781
|
+
baseline
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1779
1784
|
function skippedMissReason(summary) {
|
|
1780
1785
|
return {
|
|
1781
1786
|
kind: "cache-skipped",
|
|
@@ -1887,6 +1892,7 @@ async function planCachedBuild(lookup, deps) {
|
|
|
1887
1892
|
}
|
|
1888
1893
|
let prebuild = null;
|
|
1889
1894
|
let refusal = null;
|
|
1895
|
+
let missReason;
|
|
1890
1896
|
if (!cacheHit) {
|
|
1891
1897
|
prebuild = deps.planPrebuild(root, platform, {
|
|
1892
1898
|
isExpo,
|
|
@@ -1894,6 +1900,15 @@ async function planCachedBuild(lookup, deps) {
|
|
|
1894
1900
|
sources
|
|
1895
1901
|
});
|
|
1896
1902
|
if (prebuild === "refuse") refusal = staleNativeDirRefusal(platform);
|
|
1903
|
+
else if (cachePolicy.read) missReason = predictBuildMiss({
|
|
1904
|
+
root,
|
|
1905
|
+
platform,
|
|
1906
|
+
current: {
|
|
1907
|
+
hash: fingerprint,
|
|
1908
|
+
sources
|
|
1909
|
+
},
|
|
1910
|
+
prebuild: prebuild === "generate" || prebuild === "regenerate" ? prebuild : null
|
|
1911
|
+
});
|
|
1897
1912
|
}
|
|
1898
1913
|
return planPayload(lookup, {
|
|
1899
1914
|
fingerprint,
|
|
@@ -1902,11 +1917,12 @@ async function planCachedBuild(lookup, deps) {
|
|
|
1902
1917
|
provider,
|
|
1903
1918
|
cacheSkipped: !cachePolicy.read,
|
|
1904
1919
|
prebuild,
|
|
1920
|
+
...missReason ? { missReason } : {},
|
|
1905
1921
|
refusal
|
|
1906
1922
|
});
|
|
1907
1923
|
}
|
|
1908
1924
|
function planPayload({ platform, slot, projectKey }, found) {
|
|
1909
|
-
const { refusal, ...rest } = found;
|
|
1925
|
+
const { refusal, missReason, ...rest } = found;
|
|
1910
1926
|
const outcome = refusal ? null : found.cacheHit ? "hit" : "cold";
|
|
1911
1927
|
const estimate = outcome ? estimateBuild(readStats().record?.history?.[projectKey], platform, outcome, "prepare") : {
|
|
1912
1928
|
expectedMs: null,
|
|
@@ -1919,6 +1935,7 @@ function planPayload({ platform, slot, projectKey }, found) {
|
|
|
1919
1935
|
outcome,
|
|
1920
1936
|
expectedMs: estimate.expectedMs,
|
|
1921
1937
|
basis: estimate.basis,
|
|
1938
|
+
...missReason ? { missReason } : {},
|
|
1922
1939
|
...refusal ? { refusal } : {}
|
|
1923
1940
|
};
|
|
1924
1941
|
}
|
|
@@ -1935,6 +1952,7 @@ function printPlan(plan, json) {
|
|
|
1935
1952
|
}
|
|
1936
1953
|
const slot = plan.slot ? ` [${plan.slot}]` : "";
|
|
1937
1954
|
console.log(phaseLine("plan", `${plan.platform}${slot} ${shortHash(plan.fingerprint)} -> ${cacheLine(plan)}`));
|
|
1955
|
+
if (plan.missReason) console.log(phaseLine("cache", `miss: ${plan.missReason.summary}`));
|
|
1938
1956
|
if (plan.refusal) {
|
|
1939
1957
|
console.log(phaseLine("error", plan.refusal.message));
|
|
1940
1958
|
console.log(phaseLine("remedy", plan.refusal.remedy));
|
|
@@ -1963,4 +1981,4 @@ function planFlagRefusal(flag) {
|
|
|
1963
1981
|
};
|
|
1964
1982
|
}
|
|
1965
1983
|
//#endregion
|
|
1966
|
-
export {
|
|
1984
|
+
export { warmMetro as C, clearIosAdoptionPending as S, staleNativeDirRefusal as _, refusePlan as a, AvdBootError as b, skippedMissReason as c, schemesFromInfoPlist as d, detectEntryFile as f, runPrebuild as g, recordPrebuild as h, printPlan as i, devClientScheme as l, planPrebuild as m, planFlagRefusal as n, explainBuildMiss as o, swapJsBundle as p, planPayload as r, fingerprintErrorMissReason as s, planCachedBuild as t, pickDevClientScheme as u, ensureBooted as v, AvdRecoveryError as x, ensureOwnedDevice as y };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { a as formatElapsed, u as plural } from "./command-output-
|
|
4
|
-
import { BUILD_PHASES } from "@stim-cli/core/state";
|
|
1
|
+
import { A as ownership_claim_exports } from "./config-0jCcMM0E.mjs";
|
|
2
|
+
import { _ as updateWorkspaceState, p as clearWorkspaceStateKey } from "./metro-BPeY1YMh.mjs";
|
|
3
|
+
import { a as formatElapsed, u as plural } from "./command-output-BIL7rlX7.mjs";
|
|
4
|
+
import { BUILD_HISTORY_KEY, BUILD_HISTORY_LIMIT, BUILD_PHASES, LAST_BUILD_KEYS } from "@stim-cli/core/state";
|
|
5
5
|
//#region src/engine/build-progress.ts
|
|
6
6
|
const ACTIVE_BUILD_KEY = "activeBuild";
|
|
7
7
|
const NO_BUILD_PROGRESS = {
|
|
@@ -44,11 +44,11 @@ function startBuildProgress({ root, platform, slot, claim, now = Date.now, note
|
|
|
44
44
|
note(`Build progress could not be recorded in the workspace state: ${error?.message || error}`);
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
|
-
const write = () => guard(() => updateWorkspaceState(root, (state) => ({
|
|
48
|
-
...state,
|
|
47
|
+
const write = (before = (state) => state) => guard(() => updateWorkspaceState(root, (state) => ({
|
|
48
|
+
...before(state),
|
|
49
49
|
[ACTIVE_BUILD_KEY]: record
|
|
50
50
|
})));
|
|
51
|
-
write();
|
|
51
|
+
write(withInterruptedBuild);
|
|
52
52
|
return {
|
|
53
53
|
step(phase) {
|
|
54
54
|
if (phase === record.phase) return;
|
|
@@ -82,6 +82,74 @@ function phaseDurations(phases, now) {
|
|
|
82
82
|
});
|
|
83
83
|
return out;
|
|
84
84
|
}
|
|
85
|
+
const HISTORY_FIELDS = [
|
|
86
|
+
"platform",
|
|
87
|
+
"status",
|
|
88
|
+
"configuration",
|
|
89
|
+
"fingerprint",
|
|
90
|
+
"cacheKey",
|
|
91
|
+
"cacheHit",
|
|
92
|
+
"cacheSkipped",
|
|
93
|
+
"durationMs",
|
|
94
|
+
"startedAt",
|
|
95
|
+
"errorCode",
|
|
96
|
+
"missReason",
|
|
97
|
+
"diagnostics"
|
|
98
|
+
];
|
|
99
|
+
function withHistoryEntry(state, platform, entry) {
|
|
100
|
+
const saved = state[BUILD_HISTORY_KEY];
|
|
101
|
+
const history = saved && typeof saved === "object" && !Array.isArray(saved) ? saved : {};
|
|
102
|
+
const list = Array.isArray(history[platform]) ? history[platform] : [];
|
|
103
|
+
return {
|
|
104
|
+
...state,
|
|
105
|
+
[BUILD_HISTORY_KEY]: {
|
|
106
|
+
...history,
|
|
107
|
+
[platform]: [entry, ...list].slice(0, BUILD_HISTORY_LIMIT)
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function finishedResult(record) {
|
|
112
|
+
if (record.status === "ok") return "succeeded";
|
|
113
|
+
return record.errorCode === "STIM_CANCELLED" ? "cancelled" : "failed";
|
|
114
|
+
}
|
|
115
|
+
function recordFinishedBuild(root, record, { update = updateWorkspaceState, now = Date.now } = {}) {
|
|
116
|
+
const platform = record.platform;
|
|
117
|
+
update(root, (state) => {
|
|
118
|
+
const active = parseActiveBuild(state[ACTIVE_BUILD_KEY]);
|
|
119
|
+
const own = active?.platform === platform ? active : null;
|
|
120
|
+
const entry = {
|
|
121
|
+
...Object.fromEntries(HISTORY_FIELDS.filter((key) => key in record).map((key) => [key, record[key]])),
|
|
122
|
+
result: finishedResult(record),
|
|
123
|
+
slot: own?.slot ?? "default",
|
|
124
|
+
phases: own ? phaseDurations(own.phases, now()) : {}
|
|
125
|
+
};
|
|
126
|
+
return withHistoryEntry({
|
|
127
|
+
...state,
|
|
128
|
+
lastBuild: record,
|
|
129
|
+
[LAST_BUILD_KEYS[platform]]: record
|
|
130
|
+
}, platform, entry);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function withInterruptedBuild(state) {
|
|
134
|
+
const left = parseActiveBuild(state[ACTIVE_BUILD_KEY]);
|
|
135
|
+
if (!left) return state;
|
|
136
|
+
const last = state[LAST_BUILD_KEYS[left.platform]];
|
|
137
|
+
if (Date.parse(String(last?.startedAt)) >= Date.parse(left.startedAt)) return state;
|
|
138
|
+
return withHistoryEntry(state, left.platform, {
|
|
139
|
+
platform: left.platform,
|
|
140
|
+
status: "failed",
|
|
141
|
+
result: "interrupted",
|
|
142
|
+
slot: left.slot,
|
|
143
|
+
configuration: null,
|
|
144
|
+
fingerprint: null,
|
|
145
|
+
cacheKey: null,
|
|
146
|
+
cacheHit: false,
|
|
147
|
+
cacheSkipped: false,
|
|
148
|
+
durationMs: null,
|
|
149
|
+
startedAt: left.startedAt,
|
|
150
|
+
phases: phaseDurations(left.phases, Date.parse(left.phaseStartedAt))
|
|
151
|
+
});
|
|
152
|
+
}
|
|
85
153
|
function parseActiveBuild(value) {
|
|
86
154
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
87
155
|
const record = value;
|
|
@@ -176,4 +244,4 @@ function buildStatusLine(report, now) {
|
|
|
176
244
|
return remainingMs > 0 ? `${head} -- ${remainingText(remainingMs)} (${basis})` : `${head} (usually ~${formatElapsed(report.expectedMs)}, ${basis})`;
|
|
177
245
|
}
|
|
178
246
|
//#endregion
|
|
179
|
-
export { buildStatusLine as a,
|
|
247
|
+
export { buildStatusLine as a, recordFinishedBuild as c, buildReport as i, startBuildProgress as l, NO_BUILD_PROGRESS as n, estimateBuild as o, activeBuildState as r, parseActiveBuild as s, ACTIVE_BUILD_KEY as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as getConfigDir } from "./config-0jCcMM0E.mjs";
|
|
2
2
|
import { existsSync } from "fs";
|
|
3
3
|
import { isAbsolute, join, resolve } from "path";
|
|
4
4
|
import { readCacheManifest, updateCacheManifest } from "@stim-cli/core";
|
package/dist/cache-manifest.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as unregister, i as registeredCaches, n as readManifest, r as register, t as manifestPath } from "./cache-manifest-
|
|
1
|
+
import { a as unregister, i as registeredCaches, n as readManifest, r as register, t as manifestPath } from "./cache-manifest-DWPF4RKJ.mjs";
|
|
2
2
|
export { manifestPath, readManifest, register, registeredCaches, unregister };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { WebSocket } from "ws";
|
|
2
|
+
//#region src/web/cdp.ts
|
|
3
|
+
const COMMAND_TIMEOUT_MS = 15e3;
|
|
4
|
+
function connectCdp(url, { timeoutMs = 5e3 } = {}) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const socket = new WebSocket(url, {
|
|
7
|
+
perMessageDeflate: false,
|
|
8
|
+
maxPayload: 268435456
|
|
9
|
+
});
|
|
10
|
+
const pending = /* @__PURE__ */ new Map();
|
|
11
|
+
const eventListeners = [];
|
|
12
|
+
const closeListeners = [];
|
|
13
|
+
let nextId = 1;
|
|
14
|
+
let closed = false;
|
|
15
|
+
const timer = setTimeout(() => {
|
|
16
|
+
socket.terminate();
|
|
17
|
+
reject(/* @__PURE__ */ new Error(`DevTools did not accept a connection at ${url} within ${timeoutMs}ms`));
|
|
18
|
+
}, timeoutMs);
|
|
19
|
+
socket.on("message", (data) => {
|
|
20
|
+
let message;
|
|
21
|
+
try {
|
|
22
|
+
message = JSON.parse(String(data));
|
|
23
|
+
} catch {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (typeof message.id === "number") {
|
|
27
|
+
const waiter = pending.get(message.id);
|
|
28
|
+
if (!waiter) return;
|
|
29
|
+
pending.delete(message.id);
|
|
30
|
+
if (message.error) waiter.reject(new Error(message.error.message ?? "DevTools command failed"));
|
|
31
|
+
else waiter.resolve(message.result ?? {});
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (typeof message.method === "string") {
|
|
35
|
+
const event = {
|
|
36
|
+
method: message.method,
|
|
37
|
+
params: message.params ?? {},
|
|
38
|
+
...message.sessionId ? { sessionId: message.sessionId } : {}
|
|
39
|
+
};
|
|
40
|
+
for (const listener of eventListeners) listener(event);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
socket.on("close", () => {
|
|
44
|
+
closed = true;
|
|
45
|
+
for (const waiter of pending.values()) waiter.reject(/* @__PURE__ */ new Error("DevTools connection closed"));
|
|
46
|
+
pending.clear();
|
|
47
|
+
for (const listener of closeListeners) listener();
|
|
48
|
+
});
|
|
49
|
+
socket.on("error", (error) => {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
reject(error);
|
|
52
|
+
});
|
|
53
|
+
socket.on("open", () => {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
resolve({
|
|
56
|
+
send(method, params = {}, sessionId) {
|
|
57
|
+
if (closed) return Promise.reject(/* @__PURE__ */ new Error("DevTools connection closed"));
|
|
58
|
+
const id = nextId++;
|
|
59
|
+
return new Promise((resolveCommand, rejectCommand) => {
|
|
60
|
+
const commandTimer = setTimeout(() => {
|
|
61
|
+
pending.delete(id);
|
|
62
|
+
rejectCommand(/* @__PURE__ */ new Error(`DevTools command ${method} timed out`));
|
|
63
|
+
}, COMMAND_TIMEOUT_MS);
|
|
64
|
+
pending.set(id, {
|
|
65
|
+
resolve: (value) => {
|
|
66
|
+
clearTimeout(commandTimer);
|
|
67
|
+
resolveCommand(value);
|
|
68
|
+
},
|
|
69
|
+
reject: (error) => {
|
|
70
|
+
clearTimeout(commandTimer);
|
|
71
|
+
rejectCommand(error);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
socket.send(JSON.stringify({
|
|
75
|
+
id,
|
|
76
|
+
method,
|
|
77
|
+
params,
|
|
78
|
+
...sessionId ? { sessionId } : {}
|
|
79
|
+
}));
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
onEvent(listener) {
|
|
83
|
+
eventListeners.push(listener);
|
|
84
|
+
},
|
|
85
|
+
onClose(listener) {
|
|
86
|
+
if (closed) listener();
|
|
87
|
+
else closeListeners.push(listener);
|
|
88
|
+
},
|
|
89
|
+
close() {
|
|
90
|
+
socket.close();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Connects to the browser DevTools endpoint on loopback `port` only when the process serving it is `chromePid`,
|
|
98
|
+
* the Chrome Stim started. Chrome reports its own browser process through `SystemInfo.getProcessInfo`.
|
|
99
|
+
*/
|
|
100
|
+
async function connectOwnedBrowser(port, chromePid) {
|
|
101
|
+
const { webSocketDebuggerUrl } = await (await fetch(`http://127.0.0.1:${port}/json/version`, { signal: AbortSignal.timeout(2e3) })).json();
|
|
102
|
+
if (typeof webSocketDebuggerUrl !== "string" || !webSocketDebuggerUrl.startsWith(`ws://127.0.0.1:${port}/`)) throw new Error(`port ${port} does not serve a browser DevTools endpoint`);
|
|
103
|
+
const cdp = await connectCdp(webSocketDebuggerUrl);
|
|
104
|
+
try {
|
|
105
|
+
const { processInfo } = await cdp.send("SystemInfo.getProcessInfo");
|
|
106
|
+
const browser = processInfo?.find((entry) => entry.type === "browser")?.id;
|
|
107
|
+
if (browser !== chromePid) throw new Error(`port ${port} is served by browser pid ${browser ?? "unknown"}, not the owned Chrome ${chromePid}`);
|
|
108
|
+
return cdp;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
cdp.close();
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { connectOwnedBrowser as t };
|