Docs

Packages

Server

HTTP stream helpers from @anvia/server.

Import from @anvia/server.

Types

type EventStreamFormat = "jsonl" | "sse";

type EventStreamErrorEvent = {
  type: "error";
  error: unknown;
};

type CreateEventStreamOptions<TEvent> = {
  format?: EventStreamFormat;
  headers?: HeadersInit;
  status?: number;
  statusText?: string;
  resumable?: CreateResumableStreamOptions<TEvent>;
  jsonl?: JsonlStreamOptions<TEvent>;
  sse?: SseStreamOptions<TEvent>;
};

type JsonlStreamOptions<TEvent> = {
  serialize?: (event: TEvent | EventStreamErrorEvent) => string;
};

type SseStreamOptions<TEvent> = {
  eventName?: string | ((event: TEvent | EventStreamErrorEvent) => string | undefined);
  serialize?: (event: TEvent | EventStreamErrorEvent) => string;
  retry?: number;
};

type ResumableStreamFinalStatus = "completed" | "error";
type ResumableStreamStatus = "running" | ResumableStreamFinalStatus | "missing";

type ResumableStreamState = {
  status: ResumableStreamStatus;
  lastEventId: number;
};

type ResumableStreamRecord<TEvent> = {
  streamId: string;
  eventId: number;
  event: TEvent | EventStreamErrorEvent;
  createdAt?: Date;
};

type ResumableStreamEnvelope<TEvent> =
  | { type: "stream_start"; streamId: string; eventId: 0 }
  | { type: "stream_event"; streamId: string; eventId: number; event: TEvent | EventStreamErrorEvent }
  | { type: "stream_end"; streamId: string; eventId: number; status: ResumableStreamStatus };

type CreateResumableStreamOptions<TEvent> = {
  id: string;
  store: ResumableStreamStore<TEvent>;
};

type ResumeStreamEventsOptions<TEvent> = {
  id: string;
  after?: number;
  store: ResumableStreamStore<TEvent>;
};

type ResumableStreamOpenInput = { streamId: string };
type ResumableStreamAppendInput<TEvent> = {
  streamId: string;
  event: TEvent | EventStreamErrorEvent;
};
type ResumableStreamSubscribeInput = { streamId: string; after?: number };
type ResumableStreamStatusInput = { streamId: string };
type ResumableStreamCloseInput = { streamId: string; status: ResumableStreamFinalStatus };

interface ResumableStreamStore<TEvent = unknown> {
  open(input: ResumableStreamOpenInput): Promise<ResumableStreamState>;
  append(input: ResumableStreamAppendInput<TEvent>): Promise<ResumableStreamRecord<TEvent>>;
  subscribe(input: ResumableStreamSubscribeInput): AsyncIterable<ResumableStreamRecord<TEvent>>;
  status(input: ResumableStreamStatusInput): Promise<ResumableStreamState>;
  close(input: ResumableStreamCloseInput): Promise<ResumableStreamState>;
}

createEventStream

function createEventStream<TEvent>(
  events: AsyncIterable<TEvent>,
  options?: {
    format?: "jsonl" | "sse";
    headers?: HeadersInit;
    status?: number;
    statusText?: string;
    resumable?: CreateResumableStreamOptions<TEvent>;
    jsonl?: JsonlStreamOptions<TEvent>;
    sse?: SseStreamOptions<TEvent>;
  },
): Response;

Purpose: convert an async iterable of events into an HTTP Response.

Default behavior: writes JSONL with content-type: application/x-ndjson; charset=utf-8, cache-control: no-cache, no-transform, connection: keep-alive, and x-accel-buffering: no.

Use format: "sse" to emit text/event-stream.

Pass resumable: { id, store } to wrap events in stream_start, stream_event, and stream_end envelopes and persist ordered events for later replay.

createResumableStream

function createResumableStream<TEvent>(
  events: AsyncIterable<TEvent>,
  options: CreateResumableStreamOptions<TEvent>,
): AsyncIterable<ResumableStreamEnvelope<TEvent>>;

Purpose: wrap an event iterable in resumable envelopes and persist events to a ResumableStreamStore.

resumeStreamEvents

function resumeStreamEvents<TEvent>(
  options: ResumeStreamEventsOptions<TEvent>,
): AsyncIterable<ResumableStreamEnvelope<TEvent>>;

Purpose: replay stored events after after, then tail live events until the stream closes.

Use it from the same route that starts a stream:

if (body.resume !== undefined) {
  return createEventStream(
    resumeStreamEvents({
      id: body.resume.streamId,
      after: body.resume.after,
      store,
    }),
  );
}

createMemoryResumableStreamStore

function createMemoryResumableStreamStore<TEvent = unknown>(): ResumableStreamStore<TEvent>;

Purpose: create an in-memory resumable stream store for examples, tests, and single-process development. It is not durable and is not safe for multi-worker production replay.

createUIStreamResponse

function createUIStreamResponse(
  events: AsyncIterable<UIStreamEvent>,
  options?: CreateEventStreamOptions<UIStreamEvent>,
): Response;

Purpose: convert a standard UIStreamEvent iterable into an HTTP response for @anvia/react hooks.

Default behavior: uses the same JSONL response format and headers as createEventStream(...) unless format: "sse" is passed.

createJsonlStream

function createJsonlStream<TEvent>(
  events: AsyncIterable<TEvent>,
  options?: {
    serialize?: (event: TEvent | { type: "error"; error: unknown }) => string;
  },
): ReadableStream<Uint8Array>;

Purpose: encode each event as one JSON line.

Error behavior: if the iterable throws, the stream emits { type: "error", error } and closes.

createSseStream

function createSseStream<TEvent>(
  events: AsyncIterable<TEvent>,
  options?: {
    eventName?: string | ((event: TEvent | { type: "error"; error: unknown }) => string | undefined);
    serialize?: (event: TEvent | { type: "error"; error: unknown }) => string;
    retry?: number;
  },
): ReadableStream<Uint8Array>;

Purpose: encode each event as a Server-Sent Event with JSON in data: fields.

Validation behavior: retry must be a finite non-negative integer, and event names must not contain null bytes or line breaks.

For workflow guidance, see Readable Streams.