/** * Discriminated-union check protocol used by the system check engine. * * `CheckState` is the per-check result. `CheckRow` carries the kind, * the state, and a narrowed payload (eg. mbps on the connection row, * a photo on the camera row). Consumers branch on `state.kind` and * `state.code` — never on the human `detail` string. * * Replaces the teq-ts pattern of `{ key, status, detail }` where * `detail` was a translation key OR a raw string and consumers had to * `detail.includes("iOS")` to branch. Here every branch is a typed * code. */ import { type BrowserInfo } from "./browser-detect.js"; import type { ConnectionQualityResult } from "./connection-quality.js"; /** Machine-readable failure codes. Consumers branch on these, never on `detail`. */ export type FailCode = "unsupported-browser" | "outdated-browser" | "ios-device" | "low-memory" | "incompatible-device" | "bad-layout" | "external-monitor" | "offline" | "slow-connection" | "speed-test-failed" | "permission-denied" | "permission-required" | "device-disconnected" | "no-device-found" | "no-face" | "multiple-faces" | "deep-check-failed" | "screen-share-declined" | "screen-share-wrong-surface" | "screen-share-not-supported"; /** * Failure codes a "Retry" can plausibly clear — the user changes something * in their environment (rotate/resize, unplug a display, reconnect the * network, free up bandwidth) and the same check re-runs in the same tab. * * Everything NOT in this set is deterministic for the page load: browser and * device failures derive from the userAgent / hardware and will fail * identically on every retry. For those, the UI should point the user at the * only real fix (switch browser/device) rather than offer a dead-end retry. */ export declare const RETRYABLE_FAIL_CODES: ReadonlySet; /** True when a "Retry" could plausibly clear this failure without a reload. */ export declare const isRetryableFailCode: (code: FailCode) => boolean; /** Per-check state. Each row stores its own state plus any kind-specific payload. */ export type CheckState = { kind: "pending"; } | { kind: "checking"; message?: string; } | { kind: "pass"; detail: string; } | { kind: "fail"; code: FailCode; detail: string; } | { kind: "skipped"; }; /** Built-in check identifiers. */ export type CheckKind = "browser" | "device" | "layout" | "monitor" | "connection" | "microphone" | "speaker" | "camera" | "screen-share"; /** * One row in the engine's check sequence. `kind` discriminates; the * camera row carries an optional photo payload populated by the deep * camera check. */ export interface CheckRow { kind: CheckKind; state: CheckState; /** JPEG data URL of the photo captured during the deep camera check. Camera row only. */ photo?: string; /** Legacy measured downlink speed in Mbps. */ mbps?: number; /** Application-path upload speed in Mbps. Connection row only. */ uploadMbps?: number; /** Median idle round-trip latency in milliseconds. Connection row only. */ latencyMs?: number; /** Median variation between latency samples in milliseconds. Connection row only. */ jitterMs?: number; /** Server-selected recording upload mode verified by the readiness canary. */ recordingUploadMode?: "direct" | "segments" | "post"; /** Representative readiness payload size. Internal telemetry only. */ readinessPayloadBytes?: number; /** Median representative upload completion time. Internal telemetry only. */ readinessMedianMs?: number; /** Workload-derived completion window. Internal telemetry only. */ readinessWindowMs?: number; /** Number of successful representative readiness samples. Internal telemetry only. */ readinessSampleCount?: number; /** * Plain-language quality bucket for a PASSING connection row (Fair / Good / * Excellent), relative to the configured floor. Absent on fail rows and on * every non-connection row. Consumers render a localised label from this * rather than the raw Mbps `detail`. */ band?: ConnectionBand; } /** Overrides accepted by every individual check. Used by stories + tests. */ export interface CheckOverrides { browser?: BrowserInfo; isIOS?: boolean; isLowMemory?: boolean; isMobile?: boolean; online?: boolean; /** Force a specific mbps reading; pass null to simulate speed-test failure. */ speed?: number | null; badLandscape?: boolean; /** Pass `true` to allow Safari/iOS through the browser check. */ enableSafari?: boolean; hasExternalMonitor?: boolean; /** Override browser support for `getDisplayMedia`. */ screenShareSupported?: boolean; } /** Default reference floor for downlink quality telemetry in megabits/sec. */ export declare const MIN_SPEED_MBPS = 2; /** * Plain-language quality bucket for measured connection telemetry. A value * below the reference floor has no band; measured Mbps never decides candidate * eligibility. */ export type ConnectionBand = "fair" | "good" | "excellent"; /** * Bucket a measured speed into a {@link ConnectionBand} relative to the * reference floor. Returns null below the floor or for invalid inputs. */ export declare const connectionQualityBand: (mbps: number, floorMbps: number) => ConnectionBand | null; /** Supported browsers, surfaced on the wizard's "your browser isn't supported" alert. */ export declare const SUPPORTED_BROWSERS: readonly [{ readonly name: "Google Chrome"; readonly link: "https://www.google.com/chrome/"; }, { readonly name: "Microsoft Edge"; readonly link: "https://www.microsoft.com/en-us/edge"; }, { readonly name: "Safari 16.4+"; readonly link: "https://support.apple.com/safari"; }, { readonly name: "Brave"; readonly link: "https://brave.com/"; }, { readonly name: "Opera"; readonly link: "https://www.opera.com/"; }]; /** Browser-version check. Side-effect free. */ export declare const checkBrowser: (overrides?: CheckOverrides) => CheckRow; /** * Device check. Four independent fail conditions in priority order: * * 1. iOS device + iOS-not-enabled → `ios-device` * 2. Mobile/tablet + mobile-not-allowed → `incompatible-device` * 3. Required screen sharing without `getDisplayMedia` → `screen-share-not-supported` * 4. Low-memory (any platform) → `low-memory` * * Everything else passes -- desktop support is the bare minimum. * * `allowMobile` defaults to false. Pre-existing customers got * desktop-only behaviour for iOS by accident (we blocked iOS but * not Android); the explicit default makes the policy uniform. */ export declare const checkDevice: (overrides?: CheckOverrides, allowMobile?: boolean, requireScreenShareSupport?: boolean) => CheckRow; /** * Screen-layout check. Fails when a mobile device is in landscape with * a very wide aspect ratio (>1.66:1) — the candidate UI is built for * tall, narrow screens. Desktops pass regardless. */ export declare const checkScreenLayout: (overrides?: CheckOverrides) => CheckRow; /** * Best-effort external-monitor detection via `screen.isExtended` (Chromium- * only — Safari/Firefox leave the property undefined and we treat that as * "single display"). Spoofable; a deterrent rather than enforcement. * * `developmentMode` lets test code and dev environments force-pass even * when the dev's actual machine has a second monitor attached. Stories * still exercise the failure path via `overrides.hasExternalMonitor`. * * `allowExternalMonitor` is the *policy* knob (vs `developmentMode`'s * "I'm just a dev"): if true, an extended display still detects but * passes, with the detail string surfacing the fact for the audit * trail. Use this when the exam explicitly permits multi-monitor setups. */ export declare const checkExternalMonitor: (overrides?: CheckOverrides, developmentMode?: boolean, allowExternalMonitor?: boolean) => CheckRow; /** * Runs a 5-ping latency warmup and four sequential downloads of 1/5/10/25 * MB. Returns the average successful-download Mbps, rounded to one decimal, * or null if every download failed. */ export declare const measureSpeed: () => Promise; /** * Online-state check. Returns a pass/fail row directly when offline OR * when a speed override is supplied; otherwise returns null to signal * "the caller should now run an async speed measurement." * * Splitting the sync vs async paths keeps overrides instant (stories + * tests get deterministic output) while still letting the engine show * a "Testing speed..." intermediate state for real measurements. */ export declare const checkConnection: (overrides?: CheckOverrides) => CheckRow | null; /** * Build the connection row from a measured speed (or null on measurement * failure). Numeric speed is retained as telemetry, but never gates the * candidate because short browser probes are dominated by request latency. */ export declare const applySpeedToResult: (speed: number | null, minMbps?: number) => CheckRow; /** * Preserve application-path measurements as internal diagnostics. A completed * measurement passes regardless of the estimated Mbps; candidate eligibility * is decided separately by the workload-aware recording-storage canary. */ export declare const applyConnectionQualityToResult: (result: ConnectionQualityResult, minDownloadMbps?: number, minUploadMbps?: number) => CheckRow; //# sourceMappingURL=checks.d.ts.map