Pure SSE frame parser. No IO, no processes, no external dependencies.
An SSE frame is a sequence of field: value lines terminated by a blank line (\n\n).
Recognised fields: event, data, id, retry. Lines starting with : are comments.
Multiple data: lines within one frame are concatenated with "\n".
Summary
Functions
Parse one complete raw frame string (without the trailing "\n\n") into a %Frame{}.
Split a byte buffer on the SSE frame delimiter.
Types
@type t() :: %ReqServerSentEvents.Frame{ comments: [String.t()], data: String.t() | nil, event: String.t() | nil, id: String.t() | nil, retry: non_neg_integer() | nil }
Functions
Parse one complete raw frame string (without the trailing "\n\n") into a %Frame{}.
Frames with no data: field are returned as-is — the caller decides whether to
dispatch or discard them. Unknown field names are silently ignored per the SSE spec.
Examples
iex> ReqServerSentEvents.Frame.parse("event: ping\ndata: {}")
%ReqServerSentEvents.Frame{event: "ping", data: "{}"}
iex> ReqServerSentEvents.Frame.parse(": keepalive")
%ReqServerSentEvents.Frame{comments: ["keepalive"]}
Split a byte buffer on the SSE frame delimiter.
Recognises "\n\n", "\r\n\r\n", and "\r\r" as frame
delimiters per SSE spec §9.2.4. Returned frame strings retain their
original line endings; parse/1 handles all three terminator forms.
A leading UTF-8 byte order mark ("\uFEFF") is stripped per the spec.
Returns {complete_frames, leftover} where complete_frames is a list of raw
frame strings (without the trailing delimiter) and leftover is the remaining
bytes that have not yet formed a complete frame.
Examples
iex> ReqServerSentEvents.Frame.split("data: hello\n\n")
{["data: hello"], ""}
iex> ReqServerSentEvents.Frame.split("data: hello\r\n\r\n")
{["data: hello"], ""}
iex> ReqServerSentEvents.Frame.split("data: partial")
{[], "data: partial"}