/** * @file * * Pure helpers for recognizing a **wedged** Appium server and deciding what to * do about it. Kept separate from the integration-only `transport-factory` so * the classification, the remedy decision and the message stay unit-testable * (the factory itself needs a real device and is excluded from unit tests). * * A wedged server is one that is still **live** — it listens on its port and * answers `/status` with `ready: true` — but is no longer **usable**: its * `appium-adb` can no longer enumerate devices, so every session creation dies * with `Could not find a connected Android device in ms` while the host's own * `adb devices` lists the device instantly. Liveness is not readiness, and the * error names the wrong subject: it blames a device that is demonstrably * present, which sends whoever reads it to `adb devices` — the one diagnostic * that actively misleads here. * * The wedge develops over time in a long-lived server (one auto-started by an * earlier run and left listening), so the preflight cannot see it coming: it can * only be recognized from the failed session, cross-checked against the host's * adb. */ /** * Parameters for {@link buildWedgedAppiumServerMessage}. */ export interface BuildWedgedAppiumServerMessageParams { /** Origin of the Appium server that could not see the device (e.g. `http://localhost:4723`). */ readonly appiumOrigin: string; /** The device the host's adb can see but Appium cannot. */ readonly deviceId: string; /** Why this run is reporting rather than restarting. */ readonly reason: WedgedAppiumServerReportReason; /** How long the server has been running, when known from its marker. */ readonly serverAgeInMilliseconds?: number | undefined; /** PID of the server, when known from its marker. */ readonly serverPid?: number | undefined; } /** * The server is not to blame: either the failure was a different error, or the * host's adb cannot see the device either. The original error is rethrown. */ export interface NotWedgedVerdict { /** Which of the two honest-failure cases this is. */ readonly reason: 'device-absent' | 'other-error'; /** Discriminant. */ readonly remedy: 'not-wedged'; } /** * The server is wedged but must not be touched by this run. */ export interface ReportServerVerdict { /** Why this run is reporting rather than restarting. */ readonly reason: WedgedAppiumServerReportReason; /** Discriminant. */ readonly remedy: 'report'; } /** * Parameters for {@link resolveWedgedAppiumServerRemedy}. */ export interface ResolveWedgedAppiumServerRemedyParams { /** Device IDs the **host's** adb currently lists (the cross-check that convicts the server). */ readonly connectedDeviceIds: readonly string[]; /** The device the session was requested against. */ readonly deviceId: string; /** The error the session attempt failed with. */ readonly error: unknown; /** Whether the server was already listening and was adopted, rather than started by this run. */ readonly isAdoptedServer: boolean; /** Whether this run may start an Appium server (`shouldAutoStartAppium` is not `false`). */ readonly isAutoStartAllowed: boolean; /** Whether the adopted server's marker proves an earlier run of this harness started it. */ readonly isHarnessOwnedServer: boolean; } /** * The server is wedged and an earlier run of this harness started it, so this * run may replace it. */ export interface RestartServerVerdict { /** Always the harness-owned case. */ readonly reason: 'harness-owned'; /** Discriminant. */ readonly remedy: 'restart'; } /** * The classification of a failed session attempt. * * `'device-absent'` and `'other-error'` are the not-wedged verdicts: the * original error was honest and is rethrown untouched. */ export type WedgedAppiumServerReason = 'device-absent' | 'harness-owned' | 'other-error' | WedgedAppiumServerReportReason; /** * What the factory should do with a failed session attempt. */ export type WedgedAppiumServerRemedy = 'not-wedged' | 'report' | 'restart'; /** * Why a wedged server is only reported rather than restarted. Each maps to its * own remedy sentence — the whole point of the rewrite is that the message says * what *this* run can and cannot do about the server it found. */ export type WedgedAppiumServerReportReason = 'auto-start-disabled' | 'foreign-server' | 'freshly-started' | 'restart-did-not-help'; /** * The verdict on a failed session attempt, discriminated by `remedy` so a * `'report'` verdict carries a reason {@link buildWedgedAppiumServerMessage} can * turn into advice. */ export type WedgedAppiumServerVerdict = NotWedgedVerdict | ReportServerVerdict | RestartServerVerdict; /** * Builds the error message that replaces `Could not find a connected Android * device`, naming the **server** and stating the evidence — the host's adb can * see the device, so the server is the thing that is stale. * * @param params - The server, the device, and why this run is not restarting it. * @returns The message. */ export declare function buildWedgedAppiumServerMessage(params: BuildWedgedAppiumServerMessageParams): string; /** * Checks whether a failed session attempt is `appium-adb`'s device-enumeration * failure. The whole `cause` chain is searched, so WebdriverIO's wrapping of the * server's error does not hide it. * * @param error - The error the session attempt failed with. * @returns `true` when it is the device-not-found failure. */ export declare function checkIsAppiumDeviceNotFoundError(error: unknown): boolean; /** * Checks whether an Appium `/status` body reports a server that is accepting new * sessions. * * Deliberately **tolerant**: only an explicit `ready: false` (Appium sets it * while shutting down) is treated as not-ready. A body that cannot be parsed, or * that omits the flag, reads as ready — an unrecognized shape must not make a * healthy server look unreachable. * * This does not catch the wedge (a wedged server still answers `ready: true`); * it closes the adjacent hole of adopting a server that is on its way down. * * @param statusBody - The raw `/status` response body. * @returns `false` only when the server explicitly reports itself not ready. */ export declare function checkIsAppiumStatusReady(statusBody: string): boolean; /** * Decides what to do with a failed session attempt. * * The device-not-found error is only evidence against the server when the host's * adb *can* still see the device; otherwise the error was honest and is left * alone. A server this harness started in an earlier run is restarted (the * confirmed remedy); every other wedged server is reported rather than killed, * because terminating a process this run does not own is not its call to make. * * @param params - The failed attempt and what is known about the server. * @returns The verdict. */ export declare function resolveWedgedAppiumServerRemedy(params: ResolveWedgedAppiumServerRemedyParams): WedgedAppiumServerVerdict;