Reading SSE events
Task: data arrives as chunks of bytes. Read events separated by a blank line. Don't assume one network chunk is one message. Handle Polish characters, emoji and different line endings.
Tests: understanding transmission and parsing.
Contract: parseSSE(bytes, maxBufferChars = 64000) is an async generator that, for an AsyncIterable<Uint8Array>, yields events {event, data, id?}. It's a subset of SSE: the data/event/id fields, comments, LF/CRLF/CR, with no reconnection and no retry field. A final event without a separator is dropped. A line or event that's too large throws an error whose message contains too large.
Follow-up: how do you resume from the last ID? That's a later task; first get to know SSE and streaming.
You will practise
- Decode UTF-8 across chunks
- Buffer unfinished lines and events
- Handle LF, CRLF and a lone CR
- Limit memory
The 3 tests
Tests ported from the source material's reference.test.ts (node:test). In the browser they run with the same set of assertions.
SSE: every byte separately, UTF-8, CRLF, comments and multiple data lines
const got = await collect(parseSSE(chunks(': hi\r\nid: 7\r\nevent: token\r\ndata: żółć 😀\r\ndata: line2\r\n\r\ndata: koniec\n\n', 1)));
assert.deepEqual(got, [{ id: '7', event: 'token', data: 'żółć 😀\nline2' }, { id: '7', event: 'message', data: 'koniec' }]);SSE: an unterminated event and the memory limit
assert.deepEqual(await collect(parseSSE(chunks('data: brak separatora', 4))), []);
await assert.rejects(collect(parseSSE(chunks('data: 123456\n\n', 1), 4)), /too large/);SSE: a lone CR, resetting id, and empty data
assert.deepEqual(await collect(parseSSE(chunks('id: 1\rdata: a\r\rid:\rdata:\r\r', 1))), [
{ id: '1', event: 'message', data: 'a' }, { id: '', event: 'message', data: '' }]);Related questions
Documentation
Source: AI & Agents — practice tasks — An exercise written for the study programme, not a confirmed interview task.