/** * Coalescing outbound broadcasts. * * `batch-operations.ts` already batches *inbound* requests — a client * subscribing to twenty channels at once. This is the other direction: * one channel being broadcast to far faster than any browser needs to * render it. * * The case that motivates it is a live price board. Fourteen sources * update the same channel roughly every second, so subscribers receive * fourteen frames a second when the only thing they can act on is the * latest one. The intermediate frames are not merely wasted bandwidth: * each one costs a serialize, a fan-out across every socket, and a render. * * ### Leading edge, then collapse * * The first message on an idle key goes out **immediately**. Adding * latency to an isolated update to protect against a burst that is not * happening is the wrong trade — an idle channel should feel instant. * Messages arriving during the window are held, the latest replacing the * one before it, and a single frame is sent when the window closes. * * So a quiet channel is never delayed, and a hot one is capped at one * frame per window. That is the whole idea. * * ### Why `maxWait` exists * * A channel updating faster than the window would otherwise have its * window pushed back forever if the timer restarted on each message. * It does not — the window is anchored to the first held message — but * `maxWait` is kept as an explicit ceiling so the guarantee is stated in * the configuration rather than implied by the implementation. * * Zero dependencies. */ export declare interface CoalesceOptions { windowMs?: number maxWaitMs?: number key?: (channel: string, event: string) => string } export declare interface CoalescerStats { received: number sent: number dropped: number } export type CoalescedSend = (channel: string, event: string, data: T) => void; export declare class BroadcastCoalescer { constructor(send: CoalescedSend, options?: CoalesceOptions); push(channel: string, event: string, data: T): void; flushAll(): void; close(): void; getStats(): CoalescerStats; resetStats(): void; pendingCount(): number; }