Server-Sent Events (SSE) is the text protocol behind streaming HTTP responses — it is how the browser's EventSource, and most LLM streaming APIs, push a sequence of events over one long-lived connection. The catch is that the network delivers that text in arbitrary chunks that rarely line up with event boundaries: you receive half a line, then the rest of it plus two more, then a lone fragment. A frame decoder buffers those chunks and emits whole, parsed events only once each one is complete.
Implement sseFrameDecoder(). It returns an object with a push(chunk) method. Feed it string chunks in any split, and each call returns an array of the events that became complete during that call (often empty).
The wire format: an event is a group of lines terminated by a blank line, and each line is field: value. The fields to handle:
data — the payload. Multiple data lines in one event are joined with a newline.event — the event type. Defaults to message when absent.id — an identifier that sticks: once set, it carries to later events until changed.A line beginning with : is a comment (a keep-alive) and is ignored. Exactly one space after the colon is stripped.
function sseFrameDecoder(): {
push(chunk: string): Array<{ type: string; data: string; id?: string }>;
};
const d = sseFrameDecoder();
d.push('data: hello\n\n');
// → [{ type: 'message', data: 'hello' }]
const d = sseFrameDecoder();
d.push('event: ping\ndata: tick\n'); // → [] (no blank line yet)
d.push('\n'); // → [{ type: 'ping', data: 'tick' }]
data — data: a then data: b in one block become a\nb.id — reuse the last id on events that do not set their own.retry field, byte (Uint8Array) input, and the strict spec rule that an empty-data block is not dispatched.Unlock the solution & editor
Runnable editor + tests
Solve in the browser with instant Jest feedback.
Detailed solutions
Walkthroughs, edge cases, and complexity notes.
Multi-framework variants
React, Vue, Vanilla, Angular — same question, different stacks.