/** * Idle heartbeat for the realtime WebSocket connection (subscription-v2.ts). * * A socket whose server has stopped delivering frames looks exactly like a * quiet one: the browser reports it open and nothing arrives. The client only * learns the difference by asking. Once nothing has been received for * HEARTBEAT_IDLE_MS the heartbeat asks the connection to send a ping; if * nothing at all arrives within HEARTBEAT_TIMEOUT_MS after that, the socket is * declared dead and the connection is reconnected. Any inbound frame counts as * proof of life, so a busy socket is never pinged and a pong is just another * frame. The connection may decline to ping (it already has a request in * flight with its own timeout); the idle wait then simply starts over. * * The timeout sits above both budgets a pong can wait behind: the connection's * own 30 s request budget (the server handles one frame per socket at a time) * and the object's 30 s CPU slice, which another socket's handler can hold * while every pong waits. A healthy socket must never be declared dead * within either. * * Pure: timers come from the clock passed in, so tests can drive it. */ export declare const HEARTBEAT_IDLE_MS = 25000; export declare const HEARTBEAT_TIMEOUT_MS = 45000; export interface HeartbeatClock { setTimeout(callback: () => void, ms: number): unknown; clearTimeout(handle: unknown): void; } export declare class SocketHeartbeat { /** Send a ping now; return false to skip this round (the idle wait restarts). */ private readonly sendPing; private readonly onDead; private readonly clock; private readonly idleMs; private readonly timeoutMs; private idleTimer; private timeoutTimer; private stopped; constructor( /** Send a ping now; return false to skip this round (the idle wait restarts). */ sendPing: () => boolean, onDead: () => void, clock?: HeartbeatClock, idleMs?: number, timeoutMs?: number); start(): void; /** An inbound frame arrived: the socket is alive, so the idle wait starts over. */ noteActivity(): void; stop(): void; private armIdle; private clearTimers; }