sse
stableParse and format Server-Sent Events (SSE) streams according to the W3C specification, including event formatting, stream parsing, keepalives, and HTTP header generation.
use plugin sse Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.
Functions (12)
- parse_event Parse a single SSE event block into a table
- format_event Format data as an SSE event string
- parse_stream Parse a full SSE stream into a table of events
- format_comment Format a comment line for an SSE stream
- format_retry Format a retry directive in milliseconds
- format_keepalive Return the SSE keepalive ping string
- format_data_only Format data-only lines without an event type
- validate_event_type Check whether an event type string is valid
- extract_last_event_id Extract the last event ID from a raw stream
- build_headers Build HTTP request headers for SSE connections
- event_count Count the number of events in a raw stream
- content_type Return the SSE content-type string
From import to result
- 01Import only what you use
- 02Copy the smallest example
- 03Run it with the Zolo CLI
Overview¶
sse is a stateless toolkit for the wire format behind Server-Sent Events: the
plain-text protocol where a server streams field: value lines and clients
listen over a long-lived HTTP connection. Every function is a pure transform on
strings — there is no socket, connection object, or hidden state to manage. You
either parse raw stream text into ordinary tables (parse_event, parse_stream,
extract_last_event_id, event_count) or build spec-compliant output to write
to a response (format_event, format_comment, format_retry,
format_keepalive, format_data_only).
The mental model is two halves that mirror each other: a parsing half that turns
the text/event-stream wire format into Zolo values, and a formatting half that
turns Zolo values back into bytes. Reach for it on the server when emitting a
stream (set the content_type, format each event, send format_keepalive
periodically), and on the client when consuming one (build request headers with
build_headers, then parse incoming chunks and resume from
extract_last_event_id after a drop).
Common patterns¶
Emit a stream: set the content type, then format each event before writing it:
use plugin sse::{content_type, format_event}
print("Content-Type: {content_type()}")
print(format_event("system online", "status", "1"))
print(format_event("user joined", "presence", "2"))Consume a stream: parse every event, then remember where to resume after a drop:
use plugin sse::{parse_stream, extract_last_event_id}
let raw = "event: tick\ndata: 1\nid: 1\n\nevent: tick\ndata: 2\nid: 2\n\n"
let events = parse_stream(raw)
for _, evt in events {
print("{evt["event"]}: {evt["data"]}")
}
print("resume after: {extract_last_event_id(raw)}")Open a client connection that resumes from the last seen id, then keep it warm:
use plugin sse::{build_headers, format_keepalive}
let headers = build_headers("42")
for key, val in headers {
print("{key}: {val}")
}
print("keepalive: {format_keepalive()}")Parse a single SSE event block into a table
Parses a single SSE event block (multiple field: value lines) and returns a table with event, data, id, and retry. The event field defaults to "message" if not specified.
use plugin sse::{parse_event}
let raw = "event: update\ndata: hello world\nid: 42\n"
let evt = parse_event(raw)
print("type={evt["event"]} data={evt["data"]} id={evt["id"]}")A retry: line is parsed into an integer, and a missing event defaults to
"message":
use plugin sse::{parse_event}
let evt = parse_event("data: keepalive\nretry: 3000\n")
print("event={evt["event"]} retry={evt["retry"]}")Format data as an SSE event string
Formats an SSE event string ready to write to an HTTP response. event_type and id are optional. The default event type ("message") is omitted from the output per spec.
use plugin sse::{format_event}
let msg = format_event("hello world", "update", "1")
print(msg)
let simple = format_event("ping", nil, nil)
print(simple)Multi-line data is split into one data: line per source line, which the
client reassembles into a single payload joined by newlines:
use plugin sse::{format_event}
let block = format_event("first line\nsecond line", "log", "7")
print(block)Parse a full SSE stream into a table of events
Parses a complete SSE stream (multiple events separated by blank lines) and returns a table of event tables, each with event, data, id, and retry.
use plugin sse::{parse_stream}
let stream = "data: first\n\ndata: second\nid: 2\n\n"
let events = parse_stream(stream)
for _, evt in events {
print(evt["data"])
}Pair it with event_count to size a stream before walking it:
use plugin sse::{parse_stream, event_count}
let raw = "data: a\n\ndata: b\n\ndata: c\n\n"
print("parsing {event_count(raw)} events")
let events = parse_stream(raw)
print("got {len(events)} tables")Format a comment line for an SSE stream
Formats one or more lines as SSE comments (prefixed with :). Comments are ignored by clients but keep the connection alive.
use plugin sse::{format_comment}
let c = format_comment("server time: 12:00")
print(c)Format a retry directive in milliseconds
Formats a retry: directive telling the client to wait ms milliseconds before reconnecting after a dropped connection.
use plugin sse::{format_retry}
let directive = format_retry(5000)
print(directive)Return the SSE keepalive ping string
Returns the SSE keepalive string (":\n\n"). Send this periodically to prevent proxies and clients from closing an idle connection.
use plugin sse::{format_keepalive}
let ping = format_keepalive()
print(ping)Format data-only lines without an event type
Formats multi-line data as SSE data: lines without setting an event type. Each line of data becomes a separate data: field.
use plugin sse::{format_data_only}
let msg = format_data_only("line one\nline two")
print(msg)Check whether an event type string is valid
Returns true if event_type is a valid SSE event type name (non-empty, no newlines or colons).
use plugin sse::{validate_event_type}
print(validate_event_type("update"))
print(validate_event_type("bad:type"))
print(validate_event_type(""))Extract the last event ID from a raw stream
Scans a raw SSE stream and returns the value of the last id: field found (a string), or nil if none. Use this to resume a stream from where it left off.
use plugin sse::{extract_last_event_id}
let stream = "id: 1\ndata: a\n\nid: 2\ndata: b\n\n"
let last_id = extract_last_event_id(stream)
print("resume from id: {last_id}")Build HTTP request headers for SSE connections
Returns a table of HTTP request headers appropriate for opening an SSE connection: Accept, Cache-Control, and Connection. If last_event_id is non-empty, a Last-Event-ID header is also included for stream resumption.
use plugin sse::{build_headers}
let headers = build_headers("42")
for key, val in headers {
print("{key}: {val}")
}On a fresh connection with no prior id, pass nil to omit Last-Event-ID:
use plugin sse::{build_headers}
let headers = build_headers(nil)
for key, val in headers {
print("{key}: {val}")
}Count the number of events in a raw stream
Counts the number of events in a raw SSE stream by counting non-comment data blocks separated by blank lines.
use plugin sse::{event_count}
let stream = "data: one\n\ndata: two\n\ndata: three\n\n"
print("events: {event_count(stream)}")Return the SSE content-type string
Returns the correct MIME type string for SSE responses: "text/event-stream".
use plugin sse::{content_type}
print(content_type())