sse_parser 0.1.0
sse_parser: ^0.1.0 copied to clipboard
A minimal, dependency-free, spec-compliant Server-Sent Events (SSE) parser for Dart and Flutter, with push and stream APIs. Built for LLM token streaming.
sse_parser #
A minimal, dependency-free, spec-compliant Server-Sent Events (SSE) parser for Dart and Flutter.
It does one thing well: turn a byte/text stream into a clean stream of typed
events, correctly, across arbitrary chunk boundaries. Built for LLM token
streaming (OpenAI Responses, chat completions, Anthropic, etc.) and any
text/event-stream source.
- Pure Dart. No Flutter dependency — works in apps and on the server.
- Zero runtime dependencies.
- Two layers: a push core (
SseParser.feed) and aStreamAPI (parseSse()). - Correct. Models the WHATWG HTML event-stream algorithm and the TypeScript
eventsource-parser: BOM strip,\n/\r/\r\n(including a CRLF split across two chunks), comment lines, one-space field-value trimming, colon-less fields, multi-linedata, persistent last-event-id (with NUL guard), and ASCII-digitretry.
Stream API #
import 'package:http/http.dart' as http;
import 'package:sse_parser/sse_parser.dart';
final request = http.Request('POST', uri)..body = body;
final response = await http.Client().send(request);
await for (final SseEvent event in response.stream.parseSse()) {
if (event.data == '[DONE]') break;
final json = jsonDecode(event.data);
// event.event -> the `event:` type (or null for the default)
// event.id -> the last `id:` seen so far
}
A Stream<String> works too (stream.parseSse()); the byte variant decodes
UTF-8 for you, tolerating multi-byte sequences split across chunks.
Push API #
When you are not driving a Stream — e.g. feeding raw chunks from a custom
socket — use the core directly:
final parser = SseParser(
onEvent: (event) => print(event.data),
onComment: (text) {}, // optional: `:` keep-alive lines
onRetry: (duration) {}, // optional: `retry:` reconnect hint
);
parser.feed('data: hel');
parser.feed('lo\n\n'); // -> onEvent(SseEvent(data: 'hello'))
parser.close(); // discards any unterminated trailing block
Pass close(dispatchPending: true) (or parseSse(dispatchPendingOnDone: true))
to leniently emit a final event that arrived without its terminating blank line.
Why not an existing package? #
Most Dart SSE packages are EventSource clients (GET + auto-reconnect) or carry
a transport. This is just the parser — the part you actually want to share
across apps and pair with your own POST/streaming transport.
License #
MIT