stim 1.13.0 → 1.15.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 (133) hide show
  1. package/README.md +2 -0
  2. package/dist/{activity-DfVKZTEB.mjs → activity-B2-kb7k0.mjs} +281 -41
  3. package/dist/{android-BHfAu2mf.mjs → android-CYmq0jNi.mjs} +2 -2
  4. package/dist/{android-CD8pr8Q2.mjs → android-Cu_L4RpL.mjs} +524 -412
  5. package/dist/{android-qx_31wJo.mjs → android-Db2eEMaf.mjs} +152 -65
  6. package/dist/android-cas-PnKfAdB2.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{app-install-CDFY2nUL.mjs → app-install-CAAplXAt.mjs} +162 -173
  9. package/dist/{budget-E5o91NR1.mjs → budget-CMiqghig.mjs} +1048 -77
  10. package/dist/{build-plan-BTa93o_r.mjs → build-plan-aeGMFX7e.mjs} +193 -650
  11. package/dist/build-progress-C2Jv1AtA.mjs +872 -0
  12. package/dist/build-slots-Df1ApSuH.mjs +127 -0
  13. package/dist/{cache-manifest-DWPF4RKJ.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  14. package/dist/cache-manifest.mjs +1 -1
  15. package/dist/{chrome-B5hWgQJS.mjs → chrome-BrcVWQyD.mjs} +4 -4
  16. package/dist/cli-0cX5s4NM.mjs +45 -0
  17. package/dist/cli.mjs +6 -42
  18. package/dist/client-C2IS36Qf.mjs +1333 -0
  19. package/dist/collector-run.mjs +8 -7
  20. package/dist/{command-output-BIL7rlX7.mjs → command-output-CliHHn7a.mjs} +1 -1
  21. package/dist/{config-0jCcMM0E.mjs → config-D0DbpZtM.mjs} +29 -29
  22. package/dist/{created-devices-Cd558h31.mjs → created-devices-BwVb3liH.mjs} +2 -2
  23. package/dist/{dependency-state-DNN0yfx6.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
  24. package/dist/{deps-2Wo81np9.mjs → deps-BFlKbOIp.mjs} +3 -3
  25. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  26. package/dist/dev-client-CjzLoNs0.mjs +80 -0
  27. package/dist/{device-Cf7vl-so.mjs → device-BZ1IyS5A.mjs} +7 -7
  28. package/dist/device-capacity-DMuKC7nx.mjs +136 -0
  29. package/dist/device-host-worker.d.mts +1 -0
  30. package/dist/device-host-worker.mjs +769 -0
  31. package/dist/device-ios-DMQBUPus.mjs +381 -0
  32. package/dist/{device-lease-CL9dZf73.mjs → device-lease-W5Pi1q38.mjs} +43 -36
  33. package/dist/{device-pool-MuoiyRcu.mjs → device-pool-B2r38YjS.mjs} +4 -4
  34. package/dist/{device-remote-DT9ngZiD.mjs → device-remote-9ulPsdhY.mjs} +68 -162
  35. package/dist/{doctor-D2iysNNH.mjs → doctor-BcE5xsBn.mjs} +191 -99
  36. package/dist/{error-diagnostics-BoiOhck8.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
  37. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  38. package/dist/{gc-eA-PRBX8.mjs → gc-BuL1Genr.mjs} +430 -102
  39. package/dist/{android-cas-B9Kv9aIy.mjs → gradle-CmdOBq1d.mjs} +8 -294
  40. package/dist/{guide-BfZ0OmiP.mjs → guide-cCSGRFd3.mjs} +2052 -240
  41. package/dist/{guide-status-BaMYu55M.mjs → guide-status-CIoWK6mG.mjs} +3 -3
  42. package/dist/hosted-macos-DwowNU7W.mjs +447 -0
  43. package/dist/idle-shutdown-B5LPaNyY.mjs +122 -0
  44. package/dist/in-use-CndPMilN.mjs +110 -0
  45. package/dist/{ios-CmEzZiOj.mjs → ios-3CtCRhwr.mjs} +201 -10
  46. package/dist/{ios-CcfCeZ19.mjs → ios-Chob8ny7.mjs} +560 -259
  47. package/dist/{ios-device-BW-t_Lkv.mjs → ios-device-DERmMmk-.mjs} +12 -7
  48. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  49. package/dist/{launch-verify-CuJaBpEa.mjs → launch-verify-CLlofxRt.mjs} +42 -11
  50. package/dist/{logs-DcSRbC2t.mjs → logs-h9h6MUvE.mjs} +95 -13
  51. package/dist/machine-usage-D_YYMzfR.mjs +283 -0
  52. package/dist/machines-CYtOCqcj.mjs +259 -0
  53. package/dist/macos-BwtbUtTN.mjs +468 -0
  54. package/dist/macos-run.d.mts +5 -0
  55. package/dist/macos-run.mjs +2 -0
  56. package/dist/{merge-state-ytFQCeh4.mjs → merge-state-BnxLNQiM.mjs} +22 -12
  57. package/dist/{metro-BPeY1YMh.mjs → metro-Di8EN2Tb.mjs} +20 -120
  58. package/dist/{named-ports-n7tnJsI8.mjs → named-ports-Dj_XBNaL.mjs} +8 -64
  59. package/dist/native-run-DFDoIRbY.mjs +147 -0
  60. package/dist/{native-runtime-CiiFHt0e.mjs → native-runtime-i6OkVg-w.mjs} +11 -6
  61. package/dist/offload-worker.d.mts +121 -0
  62. package/dist/offload-worker.mjs +601 -0
  63. package/dist/{ownership-Bz2kETAS.mjs → ownership-BkYxUtuj.mjs} +3 -2
  64. package/dist/{page-BTu7I60K.mjs → page-QF_Ke10s.mjs} +7 -2
  65. package/dist/{ports-C5UutqpJ.mjs → ports-DddwttKM.mjs} +3 -3
  66. package/dist/prebuild-BvxDlD6C.mjs +213 -0
  67. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  68. package/dist/{project-DGeclhvO.mjs → project-CV3tDFHl.mjs} +9 -18
  69. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  70. package/dist/pull-requests.d.mts +120 -0
  71. package/dist/pull-requests.mjs +2 -0
  72. package/dist/recordings-GEXp4iBT.mjs +37 -0
  73. package/dist/{reload-C-cV5yy_.mjs → reload-ChPiwvot.mjs} +70 -22
  74. package/dist/{remote-cache--aehr4Pr.mjs → remote-cache-CO2elm_b.mjs} +4 -4
  75. package/dist/run-B-xqRrsg.mjs +119 -0
  76. package/dist/{server-bare-Bq8BpSIo.mjs → server-bare-X7PUgizL.mjs} +60 -13
  77. package/dist/{server-expo-DUCg52-K.mjs → server-expo-DhtX6bom.mjs} +6 -10
  78. package/dist/server-expo-KJeD109I.mjs +2 -0
  79. package/dist/{settings-xB0Frkg6.mjs → settings-CduVcoxj.mjs} +77 -26
  80. package/dist/{settings-ssVBToDR.mjs → settings-DbL6uhH8.mjs} +16 -10
  81. package/dist/settings.schema.json +357 -2
  82. package/dist/{simslim-Bbg9UWrt.mjs → simslim-Ciluxlq4.mjs} +8 -132
  83. package/dist/slot-launch-BY0BrOLh.mjs +65 -0
  84. package/dist/{spawn-claims-PT8aQpCa.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  85. package/dist/spawn-entry-DncpJvWL.mjs +16 -0
  86. package/dist/stage-CzaiwFsI.mjs +143 -0
  87. package/dist/start-CPO4wRg7.mjs +2 -0
  88. package/dist/{start-BZhItkiL.mjs → start-CUD9nasA.mjs} +108 -61
  89. package/dist/state-B4PQFXMW.d.mts +12 -0
  90. package/dist/state-BxCgeTQu.mjs +41 -0
  91. package/dist/{state-DwAU2_-D.mjs → state-CaeaHbDA.mjs} +7 -4
  92. package/dist/{state-l1pYwOre.mjs → state-DFm77wRn.mjs} +67 -5
  93. package/dist/stats-DBLLFmKd.mjs +66 -0
  94. package/dist/{status-TJlL0HWo.mjs → status-DYXxNOT0.mjs} +425 -260
  95. package/dist/{status-sszP4XKU.mjs → status-l84Gbvp2.mjs} +122 -20
  96. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  97. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  98. package/dist/stim-footprint.swift +32 -0
  99. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  100. package/dist/{stop-BZ2qMiBP.mjs → stop-Bad61z_2.mjs} +128 -19
  101. package/dist/stop-Bvf1MflS.mjs +48 -0
  102. package/dist/{stop-DskDKNTU.mjs → stop-CceYh1yG.mjs} +2 -2
  103. package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
  104. package/dist/supervisor-run.d.mts +44 -12
  105. package/dist/supervisor-run.mjs +276 -33
  106. package/dist/support-CDQbDkL5.mjs +315 -0
  107. package/dist/support-WLOzVKUv.mjs +176 -0
  108. package/dist/teardown-BYuQafiA.mjs +718 -0
  109. package/dist/{xcode-LM6A8cNZ.mjs → toolchain-DmeYjXD9.mjs} +363 -13
  110. package/dist/warm-progress-DVHLN4uC.mjs +463 -0
  111. package/dist/{idle-DxLi5OkN.mjs → watchman-Bx0wceoF.mjs} +55 -8
  112. package/dist/{web-sWXd5iek.mjs → web-k6w8-HnE.mjs} +39 -22
  113. package/dist/web-run.mjs +326 -13
  114. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  115. package/dist/workspace-state-CL2V2Ons.mjs +371 -0
  116. package/dist/worktree-BT_uULFx.mjs +2 -0
  117. package/dist/{worktree-DeBQSVtR.mjs → worktree-C0c5Udwa.mjs} +55 -54
  118. package/dist/{worktree-D4Do_XiQ.mjs → worktree-qXKzADSC.mjs} +161 -255
  119. package/package.json +10 -5
  120. package/shim/bundle-response.cjs +55 -10
  121. package/shim/bundle-response.d.cts +1 -0
  122. package/shim/expo-metro-config.cjs +10 -2
  123. package/dist/build-progress-l7d-X-ZP.mjs +0 -247
  124. package/dist/in-use-wYEaKQM9.mjs +0 -249
  125. package/dist/ownership-PUP4Dwai.mjs +0 -1138
  126. package/dist/server-expo-BeYwP_Xy.mjs +0 -2
  127. package/dist/slot-launch-BIWEW-wI.mjs +0 -19
  128. package/dist/start-Rw2GD3nV.mjs +0 -2
  129. package/dist/stats-CaSwmsSZ.mjs +0 -62
  130. package/dist/stats-D8rUTPHd.mjs +0 -368
  131. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  132. package/dist/worktree-3cBa-bQt.mjs +0 -2
  133. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -0,0 +1,143 @@
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { cpSync, existsSync, mkdirSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
3
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
+ import { validMacosResourceDestination } from "@stim-cli/core";
5
+ //#region src/macos/stage.ts
6
+ function validateInfoPlist(root, product, infoPlist) {
7
+ const plist = JSON.parse(getExecutor().runFile("plutil", [
8
+ "-convert",
9
+ "json",
10
+ "-o",
11
+ "-",
12
+ realpathSync(resolve(root, infoPlist))
13
+ ]));
14
+ if (typeof plist.CFBundleIdentifier !== "string" || plist.CFBundleExecutable !== product) throw new Error("macos.infoPlist must name a CFBundleIdentifier and the selected product as CFBundleExecutable.");
15
+ if (plist.CFBundleURLTypes || plist.SUFeedURL) throw new Error("Use a development Info.plist without shared URL schemes or an update feed.");
16
+ if (plist.LSMinimumSystemVersion !== void 0 && typeof plist.LSMinimumSystemVersion !== "string") throw new Error("macos.infoPlist LSMinimumSystemVersion must be a string. See stim guide macos.");
17
+ return {
18
+ bundleId: plist.CFBundleIdentifier,
19
+ minimumSystemVersion: plist.LSMinimumSystemVersion
20
+ };
21
+ }
22
+ function resourceError(key, entry, reason) {
23
+ throw new Error(`${key} entry ${JSON.stringify(entry)}: ${reason}. See stim guide macos.`);
24
+ }
25
+ function containsSymlink(dir) {
26
+ return readdirSync(dir, { withFileTypes: true }).some((entry) => entry.isSymbolicLink() || entry.isDirectory() && containsSymlink(join(dir, entry.name)));
27
+ }
28
+ function overlaps(a, b) {
29
+ a = a.normalize("NFC").toLowerCase();
30
+ b = b.normalize("NFC").toLowerCase();
31
+ return a === b || a.startsWith(`${b}/`) || b.startsWith(`${a}/`);
32
+ }
33
+ function resolveBundleExtras(root, repository, resources = {}, assetCatalog) {
34
+ const boundary = realpathSync(repository);
35
+ const sourcePath = (key, entry) => {
36
+ if (typeof entry !== "string" || !entry || isAbsolute(entry) || entry.includes("\0")) resourceError(key, entry, "source must be a non-empty relative path");
37
+ let source;
38
+ try {
39
+ source = realpathSync(resolve(root, entry));
40
+ } catch {
41
+ resourceError(key, entry, "source does not exist");
42
+ }
43
+ const path = relative(boundary, source);
44
+ if (path === ".." || path.startsWith(`..${sep}`) || isAbsolute(path)) resourceError(key, entry, "source leaves the repository");
45
+ if (path === "") resourceError(key, entry, "source is the repository root");
46
+ const stat = statSync(source);
47
+ if (!stat.isFile() && !stat.isDirectory()) resourceError(key, entry, "source must be a regular file or directory");
48
+ if (stat.isDirectory() && containsSymlink(source)) resourceError(key, entry, "source directory contains a symbolic link");
49
+ return source;
50
+ };
51
+ let catalog;
52
+ if (assetCatalog !== void 0 && assetCatalog !== null) {
53
+ catalog = sourcePath("macos.assetCatalog", assetCatalog);
54
+ if (!catalog.endsWith(".xcassets") || !statSync(catalog).isDirectory()) resourceError("macos.assetCatalog", assetCatalog, "catalog must be a .xcassets directory");
55
+ }
56
+ if (!resources || typeof resources !== "object" || Array.isArray(resources)) resourceError("macos.resources", resources, "expected an object");
57
+ const entries = Object.entries(resources);
58
+ if (entries.length > 256) {
59
+ const [destination, source] = entries[256];
60
+ resourceError(`macos.resources[${JSON.stringify(destination)}]`, source, "at most 256 entries are allowed");
61
+ }
62
+ const reserved = catalog ? ["Assets.car"] : [];
63
+ const resolved = [];
64
+ for (const [destination, source] of entries) {
65
+ const key = `macos.resources[${JSON.stringify(destination)}]`;
66
+ if (!validMacosResourceDestination(destination)) resourceError(key, source, "invalid destination");
67
+ if (reserved.some((other) => overlaps(destination, other))) resourceError(key, source, "destination collides with another resource");
68
+ reserved.push(destination);
69
+ resolved.push([destination, sourcePath(key, source)]);
70
+ }
71
+ return {
72
+ resources: Object.fromEntries(resolved),
73
+ assetCatalog: catalog
74
+ };
75
+ }
76
+ function stageBundle(root, product, infoPlist, bin, bundle, bundleId, extras = { resources: {} }) {
77
+ const { minimumSystemVersion } = validateInfoPlist(root, product, infoPlist);
78
+ for (const entry of readdirSync(bin).filter((name) => name.endsWith(".bundle"))) for (const [destination, source] of Object.entries(extras.resources)) if (overlaps(destination, entry)) resourceError(`macos.resources[${JSON.stringify(destination)}]`, source, `destination collides with ${entry}`);
79
+ const exec = getExecutor();
80
+ rmSync(bundle, {
81
+ recursive: true,
82
+ force: true
83
+ });
84
+ const contents = join(bundle, "Contents");
85
+ mkdirSync(join(contents, "MacOS"), { recursive: true });
86
+ mkdirSync(join(contents, "Resources"), { recursive: true });
87
+ mkdirSync(join(contents, "Frameworks"), { recursive: true });
88
+ const executable = join(contents, "MacOS", product);
89
+ cpSync(join(bin, product), executable);
90
+ cpSync(resolve(root, infoPlist), join(contents, "Info.plist"));
91
+ exec.runFile("/usr/libexec/PlistBuddy", [
92
+ "-c",
93
+ `Set :CFBundleIdentifier ${bundleId}`,
94
+ join(contents, "Info.plist")
95
+ ]);
96
+ for (const entry of readdirSync(bin)) if (entry.endsWith(".framework")) {
97
+ const target = join(contents, "Frameworks", entry);
98
+ cpSync(join(bin, entry), target, {
99
+ recursive: true,
100
+ dereference: false,
101
+ verbatimSymlinks: true
102
+ });
103
+ exec.runFile("codesign", [
104
+ "--force",
105
+ "--sign",
106
+ "-",
107
+ target
108
+ ]);
109
+ } else if (entry.endsWith(".bundle")) cpSync(join(bin, entry), join(contents, "Resources", entry), { recursive: true });
110
+ const frameworkPath = "@executable_path/../Frameworks";
111
+ if (!exec.runFile("otool", ["-l", executable]).includes(frameworkPath)) exec.runFile("install_name_tool", [
112
+ "-add_rpath",
113
+ frameworkPath,
114
+ executable
115
+ ]);
116
+ for (const [destination, source] of Object.entries(extras.resources)) {
117
+ const target = join(contents, "Resources", destination);
118
+ mkdirSync(dirname(target), { recursive: true });
119
+ cpSync(source, target, { recursive: true });
120
+ }
121
+ if (extras.assetCatalog) {
122
+ exec.runFile("xcrun", [
123
+ "actool",
124
+ extras.assetCatalog,
125
+ "--compile",
126
+ join(contents, "Resources"),
127
+ "--platform",
128
+ "macosx",
129
+ ...minimumSystemVersion === void 0 ? [] : ["--minimum-deployment-target", minimumSystemVersion],
130
+ "--output-partial-info-plist",
131
+ "/dev/null"
132
+ ]);
133
+ if (!existsSync(join(contents, "Resources", "Assets.car"))) resourceError("macos.assetCatalog", extras.assetCatalog, "actool did not produce Assets.car");
134
+ }
135
+ exec.runFile("codesign", [
136
+ "--force",
137
+ "--sign",
138
+ "-",
139
+ bundle
140
+ ]);
141
+ }
142
+ //#endregion
143
+ export { stageBundle as n, validateInfoPlist as r, resolveBundleExtras as t };
@@ -0,0 +1,2 @@
1
+ import { a as registerStart, c as startDevServer, d as tailLines, f as wantsExpoOwnTunnel, i as readLogTail, l as startFacts, n as liveSupervisor, o as resolveWorkspaceMetroPort, r as parseWait, s as startCommand, t as failureEvidence, u as supervisorEntry } from "./start-CUD9nasA.mjs";
2
+ export { startCommand as default, failureEvidence, liveSupervisor, parseWait, readLogTail, registerStart, resolveWorkspaceMetroPort, startDevServer, startFacts, supervisorEntry, tailLines, wantsExpoOwnTunnel };
@@ -1,24 +1,27 @@
1
- import { O as process_identity_exports, T as upsertProject, p as getProject } from "./config-0jCcMM0E.mjs";
2
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { E as upsertProject, p as getProject } from "./config-D0DbpZtM.mjs";
3
+ import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
4
  import { i as levelRank } from "./ndjson-CDY9IkVC.mjs";
4
- import { E as supervisorLogFile, M as workspaceLogsDir, S as ensureWorkspaceStorage, a as pidExists, b as writeWorkspaceState, c as resolveProjectMetro, f as reserveMetroPort, g as recordWorkspaceUse, h as readWorkspaceState$1, l as signalProcessTree, m as clearWorkspaceStateKeys } from "./metro-BPeY1YMh.mjs";
5
- import { s as gitCommonDir, y as repoRoot } from "./worktree-DeBQSVtR.mjs";
6
- import { a as findProjectRoot, d as readAppJson, n as appProjectProblem, r as detectIsExpo, t as NO_PROJECT_REFUSAL, u as readAppConfigText } from "./project-DGeclhvO.mjs";
7
- import { n as windowsLauncherArgs } from "./detached-entry-DJHoca49.mjs";
8
- import { c as readMetroTunnel, i as clearManagedMetroTunnel } from "./state-DwAU2_-D.mjs";
9
- import { a as withWorkspaceProcessLock, o as workspaceProcessLockError } from "./named-ports-n7tnJsI8.mjs";
10
- import { _ as stepTimer, l as phaseLine } from "./command-output-BIL7rlX7.mjs";
11
- import { A as resolveCacheProviderConfig, B as detectProviders, C as ngrokUrlSetting, E as publicUrlSetting, I as tunnelModeSetting, L as unknownSettingKeys, O as remoteAndroidSetting, P as settingShapeErrors, V as planMetroReach, a as SETTING_SHAPE_REMEDY, b as metroIdleStopMinutesSetting, d as cacheProviderSettingError, j as resolveSettings, k as remoteIosSetting, x as metroTunnelSettingError, z as PUBLIC_METRO_ENV } from "./settings-xB0Frkg6.mjs";
12
- import { a as terminateChild, c as withManagedTunnelLock, i as stopTunnel, o as withManagedRemoteWorktreeLock, r as startTunnelSequence, t as resolveSupervisorTarget } from "./ownership-PUP4Dwai.mjs";
13
- import { d as readProjectConfig } from "./remote-cache--aehr4Pr.mjs";
14
- import { I as stopOwnedMetroForReset, t as budgetGate } from "./budget-E5o91NR1.mjs";
5
+ import { C as workspaceLogsDir, _ as supervisorLogFile, c as writeWorkspaceState, i as recordWorkspaceUse, n as clearWorkspaceStateKeys, p as ensureWorkspaceStorage, r as readWorkspaceState$1 } from "./workspace-state-CL2V2Ons.mjs";
6
+ import { s as gitCommonDir, y as repoRoot } from "./worktree-C0c5Udwa.mjs";
7
+ import { a as findProjectRoot$1, d as readAppJson, n as appProjectProblem, r as detectIsExpo, t as NO_PROJECT_REFUSAL, u as readAppConfigText } from "./project-CV3tDFHl.mjs";
8
+ import { n as windowsLauncherArgs } from "./detached-entry-CiTEgMR1.mjs";
9
+ import { c as readMetroTunnel, i as clearManagedMetroTunnel } from "./state-CaeaHbDA.mjs";
10
+ import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
11
+ import { _ as stepTimer, l as phaseLine } from "./command-output-CliHHn7a.mjs";
12
+ import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Cnz8CPnl.mjs";
13
+ import { l as resolveProjectMetro, o as pidExists, p as reserveMetroPort, u as signalProcessTree } from "./metro-Di8EN2Tb.mjs";
14
+ import { A as remoteAndroidSetting, B as unknownSettingKeys, C as metroTunnelSettingError, H as PUBLIC_METRO_ENV, L as settingShapeErrors, M as resolveCacheProviderConfig, N as resolveSettings, O as publicUrlSetting, S as metroPortSetting, T as ngrokUrlSetting, U as detectProviders, W as planMetroReach, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, f as deviceIdleShutdownMinutesSetting, j as remoteIosSetting, x as metroIdleStopMinutesSetting, z as tunnelModeSetting } from "./settings-CduVcoxj.mjs";
15
+ import { _ as withManagedTunnelLock, f as startTunnelSequence, h as withManagedRemoteWorktreeLock, m as terminateChild, p as stopTunnel, u as resolveSupervisorTarget } from "./stop-cause-CZ_Mr-v9.mjs";
16
+ import { t as spawnEntry } from "./spawn-entry-DncpJvWL.mjs";
17
+ import { d as readProjectConfig } from "./remote-cache-CO2elm_b.mjs";
18
+ import { t as budgetGate, tt as stopOwnedMetroForReset } from "./budget-CMiqghig.mjs";
15
19
  import { appendFileSync, mkdirSync, openSync, readFileSync } from "node:fs";
20
+ import { dirname } from "node:path";
21
+ import { EventEmitter } from "node:events";
16
22
  import { existsSync as existsSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1 } from "fs";
17
- import { join } from "path";
23
+ import { join as join$1 } from "path";
18
24
  import { IDLE_STOP_KEY, queryLogs } from "@stim-cli/core/state";
19
- import { basename as basename$1, dirname as dirname$1, join as join$1 } from "node:path";
20
- import { EventEmitter } from "node:events";
21
- import { fileURLToPath } from "node:url";
22
25
  import chalk from "chalk";
23
26
  import { CACHE_PROVIDER_ENV, cacheProviderEnv } from "@stim-cli/cache";
24
27
  //#region src/workspace/app-id.ts
@@ -60,7 +63,7 @@ function detectAndroidPackage(projectRoot) {
60
63
  return detectAppIds(projectRoot).androidPackage;
61
64
  }
62
65
  function detectBundleIdFromPbxproj(projectRoot) {
63
- const iosDir = join(projectRoot, "ios");
66
+ const iosDir = join$1(projectRoot, "ios");
64
67
  if (!existsSync$1(iosDir)) return null;
65
68
  let entries;
66
69
  try {
@@ -70,7 +73,7 @@ function detectBundleIdFromPbxproj(projectRoot) {
70
73
  }
71
74
  for (const entry of entries) {
72
75
  if (!entry.isDirectory() || !entry.name.endsWith(".xcodeproj")) continue;
73
- const pbx = join(iosDir, entry.name, "project.pbxproj");
76
+ const pbx = join$1(iosDir, entry.name, "project.pbxproj");
74
77
  if (!existsSync$1(pbx)) continue;
75
78
  let text;
76
79
  try {
@@ -93,7 +96,7 @@ function detectBundleIdFromPbxproj(projectRoot) {
93
96
  return null;
94
97
  }
95
98
  function detectAndroidPackageFromGradle(projectRoot) {
96
- const gradle = join(projectRoot, "android", "app", "build.gradle");
99
+ const gradle = join$1(projectRoot, "android", "app", "build.gradle");
97
100
  if (!existsSync$1(gradle)) return null;
98
101
  let text;
99
102
  try {
@@ -108,18 +111,6 @@ function detectAndroidPackageFromGradle(projectRoot) {
108
111
  return null;
109
112
  }
110
113
  //#endregion
111
- //#region src/spawn-entry.ts
112
- const DEV_ENTRIES = {
113
- "supervisor-run": "./supervisor/run.ts",
114
- "collector-run": "./collector/run.ts",
115
- "web-run": "./web/run.ts"
116
- };
117
- function spawnEntry(name) {
118
- const here = fileURLToPath(import.meta.url);
119
- if (basename$1(dirname$1(here)) === "src") return fileURLToPath(new URL(DEV_ENTRIES[name], import.meta.url));
120
- return join$1(dirname$1(here), `${name}.mjs`);
121
- }
122
- //#endregion
123
114
  //#region src/commands/start.ts
124
115
  const DEFAULT_WAIT_SECONDS = 60;
125
116
  const POLL_MS = 500;
@@ -194,14 +185,14 @@ function recordedSupervisorProcess(pid) {
194
185
  });
195
186
  }
196
187
  const RECORDED_SUPERVISOR_WAIT_MS = 15e3;
197
- function providersOnPath() {
188
+ function providersOnPath(mode) {
198
189
  return detectProviders((bin) => {
199
190
  try {
200
191
  return Boolean(getExecutor().findExecutable(bin));
201
192
  } catch {
202
193
  return false;
203
194
  }
204
- });
195
+ }, mode);
205
196
  }
206
197
  const DEFAULT_START_DEPS = {
207
198
  providers: providersOnPath,
@@ -260,7 +251,7 @@ function registerStart(program, overrides = {}) {
260
251
  remedy: "Pass a whole number of seconds, e.g. --wait 90."
261
252
  });
262
253
  const waitSeconds = wait.seconds;
263
- const root = findProjectRoot(process.cwd());
254
+ const root = findProjectRoot$1(process.cwd());
264
255
  if (!root) return fail(NO_PROJECT_REFUSAL);
265
256
  const projectProblem = appProjectProblem(root);
266
257
  if (projectProblem) return fail({
@@ -313,7 +304,8 @@ function registerStart(program, overrides = {}) {
313
304
  out,
314
305
  facts: result.facts,
315
306
  waited: result.waited,
316
- reclaimed: result.reclaimed
307
+ reclaimed: result.reclaimed,
308
+ links: workspaceLinks(root)
317
309
  });
318
310
  });
319
311
  }
@@ -376,6 +368,11 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
376
368
  };
377
369
  if (resetCache) {
378
370
  await gateBudget();
371
+ const pin = metroPortSetting(root);
372
+ if (pin.port !== null || pin.error) {
373
+ const pinCheck = await resolveWorkspaceMetroPort(root, note, "start", false);
374
+ if (typeof pinCheck !== "number") return fail(pinCheck);
375
+ }
379
376
  try {
380
377
  await stopOwnedMetroForReset(root);
381
378
  } catch (error) {
@@ -390,7 +387,7 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
390
387
  }
391
388
  const logsDir = workspaceLogsDir(root);
392
389
  const logFile = supervisorLogFile(root);
393
- const port = await resolvePort(root, note);
390
+ const port = await resolvePort(root, note, fail);
394
391
  let publicOrigin = remote ? publicUrl : null;
395
392
  let resolution = await resolveProjectMetro(port, root);
396
393
  let supervisor = liveSupervisor({
@@ -510,7 +507,9 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
510
507
  ...tunnel ? ["--tunnel"] : [],
511
508
  ...resetCache ? ["--reset-cache"] : [],
512
509
  "--idle-stop-minutes",
513
- String(metroIdleStopMinutesSetting(settings))
510
+ String(metroIdleStopMinutesSetting(settings)),
511
+ "--device-idle-minutes",
512
+ String(deviceIdleShutdownMinutesSetting(settings))
514
513
  ];
515
514
  const childEnv = {
516
515
  ...process.env,
@@ -540,7 +539,7 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
540
539
  return null;
541
540
  };
542
541
  if (remote && !tunnel && !publicUrl && tunnelMode !== "off") {
543
- const available = d.providers();
542
+ const available = d.providers(tunnelMode);
544
543
  const plan = planMetroReach({
545
544
  mode: tunnelMode,
546
545
  metroPort: port,
@@ -554,7 +553,7 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
554
553
  remedy: plan.remedy
555
554
  });
556
555
  if ("start" in plan) {
557
- const candidates = tunnelMode === "auto" ? available : [plan.start];
556
+ const candidates = tunnelMode === "auto" ? available.filter((provider) => provider !== "tailscale") : [plan.start];
558
557
  const expectedStableUrl = ngrokUrlSetting(settings);
559
558
  let acquisition;
560
559
  try {
@@ -722,6 +721,7 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
722
721
  if ("failed" in acquisition) return fail(acquisition.failed);
723
722
  publicOrigin = acquisition.origin;
724
723
  managedTunnel = acquisition.tunnel;
724
+ if (managedTunnel.record.provider === "tailscale") note(phaseLine("lan", `tailnet-only Metro: ${publicOrigin}`));
725
725
  resolution = await resolveProjectMetro(port, root);
726
726
  supervisor = liveSupervisor({
727
727
  state: readWorkspaceState$1(root),
@@ -911,7 +911,7 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
911
911
  };
912
912
  const startLocked = async () => {
913
913
  try {
914
- return await withWorkspaceProcessLock(dirname$1(workspaceLogsDir(root)), "metro-start", runStart, { external: true });
914
+ return await withWorkspaceProcessLock(dirname(workspaceLogsDir(root)), "metro-start", runStart, { external: true });
915
915
  } catch (error) {
916
916
  if (workspaceProcessLockError(error) !== "timeout") throw error;
917
917
  return fail({
@@ -956,24 +956,69 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
956
956
  };
957
957
  }
958
958
  }
959
- async function resolvePort(root, note) {
959
+ async function resolvePort(root, note, fail) {
960
+ const result = await resolveWorkspaceMetroPort(root, note, "start");
961
+ return typeof result === "number" ? result : fail(result);
962
+ }
963
+ async function resolveWorkspaceMetroPort(root, note, command, reserve = true) {
964
+ const setting = metroPortSetting(root);
965
+ if (setting.error) return {
966
+ code: "STIM_BAD_ARG",
967
+ message: setting.error,
968
+ remedy: SETTING_SHAPE_REMEDY
969
+ };
970
+ const pinned = setting.port;
960
971
  const project = getProject(root);
961
972
  const recorded = project?.metroPort;
962
- if (!recorded) return await reserveMetroPort(root);
963
- const supervisor = resolveSupervisorTarget({
964
- state: readWorkspaceState$1(root)?.supervisor,
965
- record: project.supervisor,
966
- reservedPort: recorded
967
- });
968
- if (supervisor.status !== "none" && supervisor.status !== "stale") return recorded;
969
- const held = await resolveProjectMetro(recorded, root);
970
- if (!held.notOurs) return recorded;
971
- const fresh = await reserveMetroPort(root);
972
- if (fresh !== recorded) {
973
- note(chalk.yellow(`Port ${recorded} is held by something else (${held.notOurs}).`));
974
- note(chalk.dim(`Reserved port ${fresh} for this project instead.`));
973
+ if (pinned === null && command === "web") return recorded ?? await reserveMetroPort(root);
974
+ if (recorded) {
975
+ const supervisor = resolveSupervisorTarget({
976
+ state: readWorkspaceState$1(root)?.supervisor,
977
+ record: project.supervisor,
978
+ reservedPort: recorded
979
+ });
980
+ if (supervisor.status !== "none" && supervisor.status !== "stale") {
981
+ if (pinned === null || recorded === pinned) return recorded;
982
+ if (supervisor.status === "unverified") return {
983
+ code: "STIM_BAD_ARG",
984
+ message: `Cannot move this workspace's Metro to metro.port ${pinned}: ${supervisor.reason}.`,
985
+ remedy: `Stop that supervisor with the tool that started it, then retry, or restore metro.port to ${recorded}. Stim leaves unverified processes alone.`
986
+ };
987
+ return {
988
+ code: "STIM_BAD_ARG",
989
+ message: `This workspace's dev server is running on port ${recorded}, and metro.port pins it to ${pinned}.`,
990
+ remedy: "Run `stim stop`, then retry, or unset or restore metro.port."
991
+ };
992
+ }
993
+ }
994
+ if (pinned !== null) {
995
+ const held = await resolveProjectMetro(pinned, root);
996
+ if (held.notOurs) return {
997
+ code: "STIM_BAD_ARG",
998
+ message: `Port ${pinned} is held by something else (${held.notOurs}), and metro.port pins this workspace's Metro to it.`,
999
+ remedy: "Stop the process on that port, or set another metro.port."
1000
+ };
1001
+ if (recorded === pinned) return recorded;
1002
+ } else if (recorded) {
1003
+ const held = await resolveProjectMetro(recorded, root);
1004
+ if (!held.notOurs) return recorded;
1005
+ const fresh = await reserveMetroPort(root);
1006
+ if (fresh !== recorded) {
1007
+ note(chalk.yellow(`Port ${recorded} is held by something else (${held.notOurs}).`));
1008
+ note(chalk.dim(`Reserved port ${fresh} for this project instead.`));
1009
+ }
1010
+ return fresh;
1011
+ }
1012
+ if (!reserve && pinned !== null) return pinned;
1013
+ try {
1014
+ return await reserveMetroPort(root, void 0, void 0, pinned);
1015
+ } catch (error) {
1016
+ return {
1017
+ code: "STIM_BAD_ARG",
1018
+ message: error.message,
1019
+ remedy: "Give each workspace its own metro.port, in the workspace layer or the environment."
1020
+ };
975
1021
  }
976
- return fresh;
977
1022
  }
978
1023
  async function waitForMetro({ root, port, seconds, aborted = () => false, probe = resolveProjectMetro }) {
979
1024
  const deadline = Date.now() + seconds * 1e3;
@@ -1014,17 +1059,19 @@ function failureEvidence({ logFile, logsDir, sinceTs }) {
1014
1059
  if (recent.length > 0) lines.push(fellBack ? "Full records: `stim logs`" : "Full records: `stim logs --errors`");
1015
1060
  return lines;
1016
1061
  }
1017
- function report({ json, out, facts, waited, reclaimed }) {
1062
+ function report({ json, out, facts, waited, reclaimed, links }) {
1018
1063
  if (json) {
1019
- console.log(JSON.stringify(reclaimed.length ? {
1064
+ console.log(JSON.stringify({
1020
1065
  ...facts,
1021
- reclaimed
1022
- } : facts));
1066
+ ...links ? { links } : {},
1067
+ ...reclaimed.length ? { reclaimed } : {}
1068
+ }));
1023
1069
  return;
1024
1070
  }
1025
1071
  const who = facts.supervisorPid ? `supervisor pid ${facts.supervisorPid}${facts.mode ? ` (${facts.mode})` : ""}` : "started outside Stim";
1026
1072
  out(chalk.green(`OK: dev server on port ${facts.port}, ${who}${facts.alreadyRunning ? " (already running)" : ""} ${waited}`));
1027
1073
  out(chalk.dim(phaseLine("logs", facts.logsDir)));
1074
+ if (links) console.error(chalk.dim(workspaceLinkLine(links)));
1028
1075
  }
1029
1076
  //#endregion
1030
- export { registerStart as a, startFacts as c, wantsExpoOwnTunnel as d, spawnEntry as f, detectBundleId as h, readLogTail as i, supervisorEntry as l, detectAppIds as m, liveSupervisor as n, startCommand as o, detectAndroidPackage as p, parseWait as r, startDevServer as s, failureEvidence as t, tailLines as u };
1077
+ export { registerStart as a, startDevServer as c, tailLines as d, wantsExpoOwnTunnel as f, detectBundleId as h, readLogTail as i, startFacts as l, detectAppIds as m, liveSupervisor as n, resolveWorkspaceMetroPort as o, detectAndroidPackage as p, parseWait as r, startCommand as s, failureEvidence as t, supervisorEntry as u };
@@ -0,0 +1,12 @@
1
+ import "@stim-cli/core/state";
2
+ import * as import__stim_cli_core_process_identity from "@stim-cli/core/process-identity";
3
+ //#endregion
4
+ //#region src/supervisor/state.d.ts
5
+ declare const MODE_BARE = "bare-inproc";
6
+ declare const MODE_EXPO = "expo-child";
7
+ declare function clearWorkspaceSupervisor(root: string, expected?: process_identity_d_exports.ProcessRecord | null): boolean;
8
+ declare function clearExpoMetroTunnel(root: string): void;
9
+ declare function writePidFile(root: string, pid: number): string;
10
+ declare function readPidFile(root: string): number | null;
11
+ //#endregion
12
+ export { readPidFile as a, clearWorkspaceSupervisor as i, MODE_EXPO as n, writePidFile as o, clearExpoMetroTunnel as r, MODE_BARE as t };
@@ -0,0 +1,41 @@
1
+ import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
2
+ import { C as workspaceLogsDir, a as updateWorkspaceState, b as workspaceDir, r as readWorkspaceState$1 } from "./workspace-state-CL2V2Ons.mjs";
3
+ import { join } from "node:path";
4
+ import { readMacosRecord } from "@stim-cli/core/state";
5
+ //#region src/macos/state.ts
6
+ const macosDir = (root) => join(workspaceDir(root), "macos");
7
+ const macosLogFile = (root) => join(workspaceLogsDir(root), "macos.ndjson");
8
+ const macosRuntimeClaim = (root) => join(macosDir(root), "runtime.lock");
9
+ function macosProcess(pid) {
10
+ const processToken = (0, process_identity_exports.captureProcessToken)(pid);
11
+ const start = (0, process_identity_exports.processStartMicros)(pid);
12
+ if (!processToken || start.status !== "running") throw Object.assign(/* @__PURE__ */ new Error(`Cannot verify macOS process ${pid}; refusing unmanaged launch.`), { code: "STIM_CLAIM_UNAVAILABLE" });
13
+ return {
14
+ pid,
15
+ processToken,
16
+ startedAtMicros: start.startedAtMicros
17
+ };
18
+ }
19
+ function requiredMacosRecord(root) {
20
+ const record = readMacosRecord(root);
21
+ if (!record && readWorkspaceState$1(root)?.macos !== void 0) throw Object.assign(/* @__PURE__ */ new Error("The recorded macOS owner is malformed; repair its workspace state before running."), { code: "STIM_MACOS_OWNER_UNVERIFIED" });
22
+ return record;
23
+ }
24
+ function updateMacosRecord(root, owner, patch) {
25
+ let updated = false;
26
+ updateWorkspaceState(root, (state) => {
27
+ const record = readMacosRecord(root);
28
+ if (!record || !(0, process_identity_exports.sameProcessRecord)(record.supervisor, owner)) return state;
29
+ updated = true;
30
+ return {
31
+ ...state,
32
+ macos: {
33
+ ...record,
34
+ ...patch
35
+ }
36
+ };
37
+ });
38
+ return updated;
39
+ }
40
+ //#endregion
41
+ export { requiredMacosRecord as a, macosRuntimeClaim as i, macosLogFile as n, updateMacosRecord as o, macosProcess as r, macosDir as t };
@@ -1,5 +1,6 @@
1
- import { O as process_identity_exports, V as parseDeviceSlotKey, z as deviceSlotKey } from "./config-0jCcMM0E.mjs";
2
- import { D as supervisorPidFile, P as workspaceStateFile, _ as updateWorkspaceState, h as readWorkspaceState, p as clearWorkspaceStateKey, v as withWorkspaceStateLock } from "./metro-BPeY1YMh.mjs";
1
+ import { B as parseDeviceSlotKey, R as deviceSlotKey } from "./config-D0DbpZtM.mjs";
2
+ import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
+ import { T as workspaceStateFile, a as updateWorkspaceState, o as withWorkspaceStateLock, r as readWorkspaceState, t as clearWorkspaceStateKey, v as supervisorPidFile } from "./workspace-state-CL2V2Ons.mjs";
3
4
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
5
  import { dirname } from "node:path";
5
6
  //#region src/supervisor/state.ts
@@ -22,7 +23,7 @@ function readMetroTunnel(root) {
22
23
  const startedAt = record.startedAt;
23
24
  const processToken = record.processToken;
24
25
  const logFile = record.logFile;
25
- if (provider !== "ngrok" && provider !== "cloudflared" || typeof pid !== "number" || typeof port !== "number") return null;
26
+ if (provider !== "ngrok" && provider !== "cloudflared" && provider !== "tailscale" || typeof pid !== "number" || typeof port !== "number") return null;
26
27
  return {
27
28
  kind: "managed",
28
29
  provider,
@@ -88,11 +89,13 @@ function readRemoteSession(root) {
88
89
  const platform = record.platform;
89
90
  const startedAt = record.startedAt;
90
91
  const webPreviewUrl = record.webPreviewUrl;
92
+ const deviceType = record.deviceType;
91
93
  return {
92
94
  platform: platform === "ios" || platform === "android" ? platform : null,
93
95
  sessionId: id,
94
96
  startedAt: typeof startedAt === "string" ? startedAt : null,
95
- webPreviewUrl: typeof webPreviewUrl === "string" && webPreviewUrl.length > 0 ? webPreviewUrl : null
97
+ webPreviewUrl: typeof webPreviewUrl === "string" && webPreviewUrl.length > 0 ? webPreviewUrl : null,
98
+ deviceType: typeof deviceType === "string" && deviceType.length > 0 ? deviceType : null
96
99
  };
97
100
  }
98
101
  function readRemoteSessionId(root) {
@@ -1,8 +1,10 @@
1
- import { A as ownership_claim_exports, O as process_identity_exports } from "./config-0jCcMM0E.mjs";
2
- import { M as workspaceLogsDir, _ as updateWorkspaceState, a as pidExists, b as writeWorkspaceState, h as readWorkspaceState$1, k as workspaceDir, p as clearWorkspaceStateKey } from "./metro-BPeY1YMh.mjs";
3
- import { readlinkSync, rmSync } from "node:fs";
4
- import { WEB_VIEWPORTS } from "@stim-cli/core/state";
1
+ import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
2
+ import { C as workspaceLogsDir, a as updateWorkspaceState, b as workspaceDir, c as writeWorkspaceState, r as readWorkspaceState$1, t as clearWorkspaceStateKey } from "./workspace-state-CL2V2Ons.mjs";
3
+ import { o as pidExists } from "./metro-Di8EN2Tb.mjs";
4
+ import { readFileSync, readlinkSync, rmSync } from "node:fs";
5
5
  import { join } from "node:path";
6
+ import { rotatedLogPath } from "@stim-cli/core";
7
+ import { WEB_VIEWPORTS } from "@stim-cli/core/state";
6
8
  import { hostname } from "node:os";
7
9
  //#region src/web/profile.ts
8
10
  const SINGLETON_FILES = [
@@ -144,5 +146,65 @@ function webFacts(record) {
144
146
  status: supervisor === "same" && chrome === "running" ? "running" : "orphaned"
145
147
  };
146
148
  }
149
+ const PAGE_FAILURES = /* @__PURE__ */ new Set(["web_document_failed", "web_page_crashed"]);
150
+ const NAVIGATION = Buffer.from("\"event\":\"web_navigation\"");
151
+ const PAGE_EVENT = "\"event\":\"web_";
152
+ /**
153
+ * The latest page load in the web log's `lines`: the newest `web_navigation` marker, whether a document failure, a
154
+ * crash or the load event followed it, and the newest in-app route change after it. Null before the first
155
+ * navigation.
156
+ */
157
+ function latestPageLoad(lines) {
158
+ let failure = null;
159
+ let loaded = false;
160
+ let route = null;
161
+ for (let i = lines.length - 1; i >= 0; i--) {
162
+ let record;
163
+ try {
164
+ record = JSON.parse(lines[i]);
165
+ } catch {
166
+ continue;
167
+ }
168
+ if (record.event === "web_navigation" && typeof record.url === "string") {
169
+ const moved = route !== null && route !== record.url ? { route } : {};
170
+ if (failure !== null) return {
171
+ url: record.url,
172
+ state: "failed",
173
+ error: failure,
174
+ ...moved
175
+ };
176
+ return {
177
+ url: record.url,
178
+ state: loaded ? "loaded" : "loading",
179
+ ...moved
180
+ };
181
+ }
182
+ if (typeof record.event !== "string") continue;
183
+ if (record.event === "web_route" && typeof record.url === "string") route ??= record.url;
184
+ if (PAGE_FAILURES.has(record.event)) failure = typeof record.msg === "string" ? record.msg : record.event;
185
+ else if (record.event === "web_page_loaded") loaded = true;
186
+ }
187
+ return null;
188
+ }
189
+ /** The page lines from the newest navigation on, in the web log or, right after a rotation, its previous file. */
190
+ function pageLines(file) {
191
+ for (const path of [file, rotatedLogPath(file)]) {
192
+ let bytes;
193
+ try {
194
+ bytes = readFileSync(path);
195
+ } catch {
196
+ continue;
197
+ }
198
+ const at = bytes.lastIndexOf(NAVIGATION);
199
+ if (at < 0) continue;
200
+ const start = bytes.lastIndexOf(10, at) + 1;
201
+ return bytes.subarray(start).toString("utf8").split("\n").filter((line) => line.includes(PAGE_EVENT));
202
+ }
203
+ return null;
204
+ }
205
+ function readWebPage(root) {
206
+ const lines = pageLines(webLogFile(root));
207
+ return lines ? latestPageLoad(lines) : null;
208
+ }
147
209
  //#endregion
148
- export { readWebRecord as a, webDir as c, webProfileDir as d, webSupervisorLogFile as f, removeSingletonFiles as g, liveProfileHolder as h, clearWebRecord as i, webFacts as l, chromeProcessState as m, browserLogFile as n, updateWebRecord as o, writeWebRecord as p, cdpEndpoint as r, webClaimRoot as s, CDP_PORT_LABEL as t, webLogFile as u };
210
+ export { removeSingletonFiles as _, readWebPage as a, webClaimRoot as c, webLogFile as d, webProfileDir as f, liveProfileHolder as g, chromeProcessState as h, clearWebRecord as i, webDir as l, writeWebRecord as m, browserLogFile as n, readWebRecord as o, webSupervisorLogFile as p, cdpEndpoint as r, updateWebRecord as s, CDP_PORT_LABEL as t, webFacts as u };