/** * SSE event hub — projects runtime events for the dashboard, keeps a * byte-bounded replay history, and fans serialized frames out to browsers. */ import type { EventEnvelope } from "../shared/protocol.js"; export type SseWriteKind = "live" | "replay" | "resync"; /** Metadata deliberately excludes the serialized event payload. */ export interface SseWriteMetadata { kind: SseWriteKind; seq: number; type: string; frameBytes: number; /** Safe synthesized-barrier classification, never runtime payload data. */ reason?: string; } export interface ReplayDiagnostic { kind: "replay" | "resync"; count: number; bytes: number; fromSeq?: number; toSeq?: number; reason?: string; } /** Return false when this connection can no longer accept frames. */ export interface SseClient { write(chunk: string, metadata?: SseWriteMetadata): boolean | undefined; } export interface EventHubOptions { /** Maximum number of retained frames. */ bufferSize?: number; /** Maximum encoded bytes retained for reconnect replay. */ bufferBytes?: number; /** Maximum encoded bytes written during a single replay. */ replayBytes?: number; /** Largest projected event frame that may be delivered directly. */ eventBytes?: number; } export declare const DEFAULT_BUFFER_SIZE = 2000; export declare const DEFAULT_BUFFER_BYTES: number; /** Kept below the response destruction ceiling in server.ts. */ export declare const DEFAULT_REPLAY_BYTES: number; export declare const DEFAULT_EVENT_BYTES: number; /** * Dashboard-only transport projection. Each removed field is cumulative data * that the dashboard reducer does not read. Unknown event types are returned * exactly as received so extensions and future runtimes remain forward-safe. */ export declare function projectDashboardEvent(event: Record): Record; export declare class EventHub { private seq; private bufferedBytes; private readonly buffer; private readonly clients; private readonly options; private eventProjector?; constructor(options?: number | EventHubOptions); /** Install the server-owned browser projection before frame sizing/replay retention. */ setEventProjector(projector: (key: string, event: Record) => Record): void; /** Publish an event from a runtime; assigns a sequence number and fans out. */ publish(key: string, rawEvent: Record): EventEnvelope; /** * Attach a client. Replays only a complete, bounded range. A gap or replay * over budget receives a recovery frame only on this connection; it never * consumes a global sequence or disturbs healthy clients' ordered stream. */ attach(client: SseClient, lastEventId?: number, onReplay?: (diagnostic: ReplayDiagnostic) => void): () => void; get clientCount(): number; get historyBytes(): number; get historyCount(): number; /** Current sequence, captured without emitting, retaining, or fanning out. */ get currentSequence(): number; private serialize; private retain; private replayAfter; private resyncReason; /** Explicit oversized events are globally unrecoverable, so retain and fan out their barrier. */ private publishResync; /** * A stale reconnect needs the current ordering cursor, not a new global * event. With no events yet, establish sequence 1 so it has a usable cursor. */ private targetedResync; private fanout; private write; } /** Format an envelope as an SSE frame with the sequence number as event id. */ export declare function formatSseFrame(envelope: EventEnvelope): string; /** Observable liveness signal; intentionally unnumbered and never buffered. */ export declare function formatHeartbeatFrame(): string; //# sourceMappingURL=event-hub.d.ts.map