Read a fetch response body as a stream of Server-Sent Events, invoking onEvent with each event's data: payload — every data: line in the event joined with \n per the SSE spec, trimmed. Does NOT parse the payload itself (JSON or otherwise): that's entirely up to the caller, since the payload shape is whatever the specific backend protocol defines. This function only implements the generic SSE framing every such backend shares:

  • CRLF / bare-CR → LF normalization, since servers vary in line-ending choice and a single \n\n split is only authoritative once normalized.
  • Blank-line event-boundary splitting, carrying a partial (not yet terminated) tail event over to the next chunk instead of dropping it.
  • A final flush for a trailing event that was never terminated by a blank line — some servers omit the trailing separator on the last event before closing the connection.
  • Skipping SSE comment / keep-alive lines (e.g. : ping) and any event that carries no data: field at all, per the SSE spec's "ignore events that dispatch no data" rule.
  • Skipping the conventional data: [DONE] end-of-stream sentinel used by many LLM/agent streaming APIs (not part of the SSE spec itself, but common enough across backends to handle once here).

Cancellation is the caller's responsibility: abort the underlying fetch (e.g. via the AbortSignal passed to it) and reader.read() will reject, propagating out of this function same as any other stream error.

  • Parameters

    • body: ReadableStream<Uint8Array<ArrayBufferLike>>
    • onEvent: (data: string) => void

    Returns Promise<void>