/** * Copyright (c) 2026 AzerothJS. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ export interface SseSendOptions { /** * The `event:` name (the client's addEventListener key). Omit for the default channel. * A line break is not representable in a field value and is replaced with a space. */ event?: string; /** The `id:` field - the client's Last-Event-ID resume cursor. Line breaks as above. */ id?: string; } /** What a producer drives. All methods are safe after close (they become no-ops). */ export interface SseConnection { /** Emits one event. Objects are JSON-stringified; multi-line strings frame correctly. */ send(data: string | object, options?: SseSendOptions): void; /** * Emits a `:` comment line (invisible to consumers; useful for custom keep-alives). * Line breaks in `text` are replaced with spaces - a comment is exactly one line. */ comment(text: string): void; /** Ends the stream (with the `[DONE]` terminator unless disabled at creation). */ close(): void; /** Fires when the connection ends - client disconnect or close(). The producer's teardown hook. */ readonly signal: AbortSignal; /** The client's Last-Event-ID header, for resuming after a reconnect. */ readonly lastEventId: string | null; } /** * Options for {@link sse}: the comment heartbeat that keeps proxies from idling the stream, * the client's reconnect delay hint, the `[DONE]` terminator (what the frontend `stream` * keyword's parser stops on), and the buffered-bytes cap backpressure enforces. */ export interface SseOptions { /** Comment-heartbeat interval in ms; 0 disables (default 15000). */ heartbeatMs?: number; /** Emitted as the `retry:` prologue - the client's reconnect delay hint. */ retryMs?: number; /** Emit `data: [DONE]` when close() ends the stream (default true - the frontend parser's terminator). */ doneMarker?: boolean; /** * Slow-client cutoff: when this many bytes sit unread in the stream's queue, the next * send DROPS the connection (default 1 MiB). Without a cap, a producer outpacing a * stalled client buffers unbounded - the transport's socket backpressure protects the * socket, not this queue. Dropping is the correct SSE semantic: EventSource reconnects * automatically and resumes via Last-Event-ID, and erroring the stream frees the * backlog immediately instead of holding it for a client that may never drain. */ maxBufferedBytes?: number; /** * Where a producer's failure goes. The status line is long gone by the time a producer * throws, so the stream cannot become a 500: it ENDS, without the `[DONE]` terminator, * and the error arrives here (wire the app's error observer to it). Unset, the error is * rethrown on a fresh microtask so the runtime's unhandled-exception path reports it - * silently discarding it would let a client read a truncated stream as a complete one. */ onError?: (error: unknown) => void; } /** * Builds the event-stream Response. The producer runs as soon as the transport starts * reading the body; its throw ends the stream with no `[DONE]` terminator and reports to * {@link SseOptions.onError} - an SSE stream that already sent bytes cannot change its * status line, so mid-stream errors END, not 500. * * The producer's own async chain keeps the request scope, and `connection.signal` * teardown re-enters it. A callback subscribed to an EXTERNAL emitter is the one shape * that does not: it runs in the emitter's context, so capture what the callback needs * before subscribing, or wrap the callback in a work-unit root (`runInWorkUnit`). */ export declare function sse(request: Request, producer: (connection: SseConnection) => void | Promise, options?: SseOptions): Response;