Server-Sent Events framing.
Decoding is separated from transport because a frame can arrive split across
two TCP reads: decode/1 returns only the frames that are complete, plus the
remainder to prepend to the next read.
{frames, rest} = ExAgent.SSE.decode(buffer <> incoming)
# keep `rest` as the new bufferA data: [DONE] sentinel decodes to the :done atom, so termination is a
decoding concern rather than something each provider re-implements.
Summary
Functions
Decodes a buffer into complete frames plus the unconsumed remainder.
Extracts and joins the payload of every data: line in one SSE event.
Splits an SSE buffer into complete events plus the trailing partial event.
Types
@type frame() :: map() | :done
Functions
Decodes a buffer into complete frames plus the unconsumed remainder.
Comment and keep-alive events (those with no data: line) and frames whose
payload is not valid JSON are skipped rather than raising - a malformed frame
should not tear down a stream that is otherwise fine.
Examples
iex> ExAgent.SSE.decode(~s(data: {"a":1}\n\ndata: [DONE]\n\n))
{[%{"a" => 1}, :done], ""}
iex> ExAgent.SSE.decode(~s(data: {"a":1}\n\ndata: {"b":))
{[%{"a" => 1}], ~s(data: {"b":)}
Extracts and joins the payload of every data: line in one SSE event.
Returns :done for a [DONE] sentinel and nil when the event carries no
data.
Examples
iex> ExAgent.SSE.event_data("event: message\ndata: hello")
"hello"
iex> ExAgent.SSE.event_data("data: [DONE]")
:done
iex> ExAgent.SSE.event_data(": keep-alive")
nil
Splits an SSE buffer into complete events plus the trailing partial event.
Events are separated by a blank line; the second element is always the (possibly empty) incomplete remainder, held back for the next read.
Examples
iex> ExAgent.SSE.take_events("a\n\nb\n\nc")
{["a", "b"], "c"}