/** * Streaming helpers — turn a `Response` body into typed async iterables. * * - `sseStream(response)`: parse `text/event-stream` events. * - `sseStreamReconnecting(misina, path, opts)`: SSE iterator that * reopens the underlying request on disconnect with `Last-Event-ID` * set to the last `id:` seen and a delay derived from the server's * `retry:` field (or backoff fallback). * - `ndjsonStream(response)`: parse `application/x-ndjson` line-delimited JSON. * - `linesOf(response)`: raw line iterator (delimited by \n). * * All four iterators implement `[Symbol.asyncDispose]` so TC39 * explicit resource management (`await using`) works across runtime * baselines. AsyncGenerator's prototype only got native dispose in * Node 24; we ensure-disposable any iterable for Node 22 / Bun / Deno. */ import type { Misina, MisinaRequestInit } from "../types.mjs"; export interface SseEvent { /** Event id (from `id:` field). */ id?: string; /** Event name (from `event:` field). Default: `'message'`. */ event: string; /** Concatenated `data:` payload. */ data: string; /** Retry hint in milliseconds (from `retry:` field). */ retry?: number; } /** * Async-iterate Server-Sent Events from a Response with `text/event-stream`. * Closing the iterator cancels the underlying stream. * * Implements the WHATWG HTML EventStream parser (HTML §9.2): * - UTF-8 BOM at the start of the stream is stripped. * - Lines starting with `:` are comments and ignored. * - Empty `event:` field resets to the default `'message'`. * - `id:` containing NUL is ignored per spec. * - Events are yielded on a blank line; trailing buffer flushed on stream end. */ export declare function sseStream(response: Response): AsyncIterableIterator & AsyncDisposable; export interface SseReconnectOptions { /** Per-request init forwarded to `misina.get(path, init)`. */ init?: MisinaRequestInit; /** * Fallback delay between reconnect attempts when the server hasn't * sent a `retry:` field yet. Default: 3000 ms (HTML §9.2.4 default). */ reconnectDelayMs?: number; /** * Max delay between reconnect attempts. The effective delay is * `min(serverRetry || reconnectDelayMs * 2^failures, max)`. * Default: 60_000 ms. */ maxDelayMs?: number; /** * Stop reconnecting after this many consecutive failures. Default: * Infinity — reconnect forever until disposed or signal aborts. */ maxRetries?: number; /** * Decide whether to keep reconnecting. Receives the failure that * closed the previous connection (Error or undefined for graceful * EOF) and the current failure count. Return false to stop. * Default: always reconnect. */ shouldReconnect?: (error: unknown, attempt: number) => boolean; /** External abort signal — disposes the iterator when fired. */ signal?: AbortSignal; } /** * SSE iterator that reopens the connection across disconnects, honoring * the server's `retry:` field and `Last-Event-ID` header (HTML §9.2.4). * * Each iteration yields the events from the *current* connection; when * the source closes (graceful EOF or stream error) we sleep for the * effective retry delay then reissue the request with `Last-Event-ID` * set to the most recently seen `id:` value. * * Dispose (`await using` or `signal.abort()`) cancels the in-flight * connection and stops the reconnect loop. * * @example * ```ts * const events = sseStreamReconnecting(api, "/v1/notifications", { * reconnectDelayMs: 1000, * }) * for await (const e of events) console.log(e) * ``` */ export declare function sseStreamReconnecting(misina: Misina, path: string, options?: SseReconnectOptions): AsyncIterableIterator & AsyncDisposable; /** * Async-iterate NDJSON / JSON Lines from a Response. Each non-empty line is * `JSON.parse`'d. Errors propagate; iterator closes on first parse failure. */ export declare function ndjsonStream(response: Response): AsyncIterableIterator & AsyncDisposable; /** * Async-iterate raw lines from a Response body. Splits on `\n`; strips * trailing `\r`. Decodes as UTF-8. */ export declare function linesOf(response: Response): AsyncIterableIterator & AsyncDisposable; /** * Generic stream consumer with reducer. Drains the iterable and folds * each chunk into a running accumulator, returning the final value. * * Mirrors `Array.prototype.reduce` for async iterables. Useful as the * building block for provider-specific accumulators below. */ export declare function collect< TIn, TOut >(source: AsyncIterable, reducer: (acc: TOut, chunk: TIn) => TOut | Promise, initial: TOut): Promise; /** * OpenAI Chat Completions streaming format. The model emits SSE events * with `data: { ... }` payloads carrying `choices[].delta` increments. * Tool calls arrive as partial deltas indexed by `index`; `function.name` * is set on the first delta and `function.arguments` is concatenated * across subsequent deltas. */ export interface OpenAIToolCall { id?: string; type?: "function"; function: { name?: string; arguments: string; }; } /** * Drain an OpenAI chat-completion SSE stream and return the accumulated * tool calls. Stops at the `[DONE]` sentinel. JSON parse errors and * non-tool-call deltas are tolerated silently — telemetry shouldn't break * the stream. * * @example * ```ts * const res = await api.post('/v1/chat/completions', body, { responseType: 'stream' }) * const calls = await accumulateOpenAIToolCalls(sseStream(res.raw)) * ``` */ export declare function accumulateOpenAIToolCalls(events: AsyncIterable): Promise; /** * Anthropic Messages streaming format. Named events carry typed payloads: * `message_start`, `content_block_start`, `content_block_delta`, * `content_block_stop`, `message_delta`, `message_stop`. Content blocks * may be `text` (concatenate `text_delta.text`) or `tool_use` * (concatenate `input_json_delta.partial_json` then JSON.parse at end). */ export interface AnthropicContentBlock { type: "text" | "tool_use" | string; text?: string; id?: string; name?: string; input?: unknown; /** Raw partial-JSON for tool_use; populated until content_block_stop. */ partial_json?: string; } export interface AnthropicAccumulatedMessage { id?: string; model?: string; role?: string; content: AnthropicContentBlock[]; stop_reason?: string | null; usage?: { input_tokens?: number; output_tokens?: number; }; } /** * Drain an Anthropic Messages SSE stream and return the accumulated * message. Closes at `message_stop`. Tool-use partial JSON is * concatenated and parsed on `content_block_stop`; if parsing fails the * raw `partial_json` stays accessible. * * @example * ```ts * const res = await api.post('/v1/messages', body, { responseType: 'stream' }) * const message = await accumulateAnthropicMessage(sseStream(res.raw)) * ``` */ export declare function accumulateAnthropicMessage(events: AsyncIterable): Promise;