-
Notifications
You must be signed in to change notification settings - Fork 0
sse
io.sse subscribes to Server-Sent Events as a lazy async iterable of parsed events, with the
reconnect semantics of EventSource — but riding the double-meh pipeline, so it can send
Authorization and custom headers, use POST, and re-run request inspectors on every reconnect
(fresh tokens included). Native EventSource can do none of that.
import io from 'double-meh';
for await (const event of io.sse('https://api.example.com/notifications')) {
console.log(event.event, event.data, event.id);
}Returns an AsyncIterableIterator of {data, event, id}:
-
data— the event payload; multipledata:lines are joined with\n. Parse JSON payloads yourself (JSON.parse(event.data)). -
event— the event type;'message'when the server didn't name one. -
id— the current last-event id (the stream position), orundefined.
The parser implements the SSE wire format: data: / event: / id: / retry: fields, :
comments (keep-alives) are skipped, and \r\n, \n, \r line endings all work. data is the
query for a GET; pass {method: 'POST'} to stream events from a POST endpoint (the common shape
for LLM APIs).
A dropped connection reconnects automatically, EventSource-style:
- The delay is
io.sse.reconnectDelay(3000 ms) by default; a per-call number sets it ({reconnect: 500}), and the server'sretry:hint overrides it. - Each reconnect carries the
Last-Event-IDheader so the server can resume the stream. Seed a resume across process restarts with{lastEventId: savedId}. -
{reconnect: false}reads one connection and ends when the stream closes. - A 204 response ends the subscription — the server's "no more events" signal.
- An abort (
options.signal) stops immediately and never reconnects;breaking out of the loop cancels the stream and stops too.
- A non-2xx is fatal: the streamed error body is read and a
BadStatuswith the parseddatais thrown. - A response whose content type isn't
text/event-streamis fatal (FailedIO) — it usually means the endpoint isn't an SSE endpoint at all. - A mid-stream network failure is not fatal — that's what the reconnect loop is for.
const controller = new AbortController();
try {
for await (const event of io.sse('https://api.example.com/feed', null, {
signal: controller.signal,
lastEventId: localStorage.getItem('feed-position') ?? undefined
})) {
handle(JSON.parse(event.data));
if (event.id) localStorage.setItem('feed-position', event.id);
}
} catch (error) {
if (error.name !== 'AbortError') throw error;
}The article-style push channel for cache invalidation is a natural consumer: subscribe with
io.sse, call io.cache.remove(pattern) per event.
Guides
Concepts
Services
Modules
- fetch transport
- keys & URLs
- helpers
- records
- sse
- storage backends
- compression encoders
- Service Worker integration
Cookbook