/** * @file * * Pure helpers for tracking how far Obsidian Mobile has got through its startup * after `location.reload()`, and for describing where it stalled when a budget * runs out. * * Startup is watched as a **ladder of milestones**, not a single boolean, * because the two halves of it cost wildly different amounts of time and only * one of them is Obsidian's fault. Reaching `globalThis.app` is the app cold * start — the WebView reloading, the guest still churning — and on a cold or * contended emulator it can take minutes. Everything after it is Obsidian * opening the vault and loading plugins, which **L19** measured at ~1s (≤8.4s * under 12-core + disk + memory stress). Charging both to one 90s wall clock is * what made a first run after a machine restart fail by design, so the transport * runs them as two budgets and uses these helpers to tell them apart. * * Kept separate from the integration-only `transport-appium` (excluded from unit * tests) so the classification and message formatting stay unit-testable — the * polling itself needs a real device. */ /** * How far Obsidian has got through its startup, as seen from one WebView probe. * * Ordered from least to most advanced; {@link compareAppStartupMilestones} * relies on that order. */ export type AppStartupMilestone = 'layout-ready' | 'no-app' | 'no-webview' | 'no-workspace' | 'workspace-not-ready'; /** * Which of the two startup budgets was being spent when it ran out. */ export type AppStartupPhase = 'app-start' | 'layout-ready'; /** * Parameters for {@link buildStartupTimeoutMessage}. */ export interface BuildStartupTimeoutMessageParams { /** How long this phase's budget had been spent when it ran out. */ readonly elapsedInMilliseconds: number; /** The furthest milestone reached before the budget ran out. */ readonly milestone: AppStartupMilestone; /** The phase whose budget ran out. */ readonly phase: AppStartupPhase; /** How many probes were made during the phase. */ readonly pollCount: number; /** The slowest single probe round-trip observed during the phase. */ readonly slowestRoundTripInMilliseconds: number; /** The budget that ran out. */ readonly timeoutInMilliseconds: number; } /** * The raw shape a WebView startup probe reports back. * * Mirrors what the injected probe function can observe without importing * anything: whether the global `app` object exists yet, whether its `workspace` * does, and whether that workspace has finished laying out. */ export interface StartupProbeResult { /** Whether `globalThis.app` is defined. */ readonly hasApp: boolean; /** Whether `app.workspace` is defined. */ readonly hasWorkspace: boolean; /** Whether `app.workspace.layoutReady` is truthy. */ readonly isLayoutReady: boolean; } /** * Builds the diagnostic message for a startup budget that ran out. * * It names the milestone actually reached, the poll count and the slowest probe * round-trip, because those three together separate the failure modes **L19** * could not tell apart from the old message: a genuine Obsidian slowdown shows * many fast polls stalled at one milestone, whereas a contended guest shows a * handful of polls each taking tens of seconds. * * @param params - The phase, the milestone reached, and the timing evidence. * @returns A human-readable error message. */ export declare function buildStartupTimeoutMessage(params: BuildStartupTimeoutMessageParams): string; /** * Reports whether the app-start phase is satisfied at this milestone. * * @param milestone - The milestone the latest probe reported. * @returns `true` once `globalThis.app` exists, so the layout-ready budget may start. */ export declare function checkAppStarted(milestone: AppStartupMilestone): boolean; /** * Reports whether the layout-ready phase is satisfied at this milestone. * * @param milestone - The milestone the latest probe reported. * @returns `true` once the workspace has finished laying out. */ export declare function checkLayoutReady(milestone: AppStartupMilestone): boolean; /** * Turns a WebView probe into a milestone. * * A probe that could not run at all — `undefined`, because `browser.execute()` * threw while the page was mid-reload — is the least advanced milestone rather * than an error: during a reload it is the expected reading. * * @param probeResult - What the probe reported, or `undefined` when it threw. * @returns The milestone that reading represents. */ export declare function classifyAppStartupProbe(probeResult: StartupProbeResult | undefined): AppStartupMilestone; /** * Orders two milestones on the startup ladder. * * @param left - The first milestone. * @param right - The second milestone. * @returns A negative number when `left` is less advanced, `0` when equal, a positive number when more advanced. */ export declare function compareAppStartupMilestones(left: AppStartupMilestone, right: AppStartupMilestone): number;