A2UIPhoenix.Plug.SSE (A2UIPhoenix v0.1.1)

Copy Markdown View Source

Streams A2UI messages to a renderer outside LiveView as server-sent events.

Each message is one event whose data is the message's JSON, sent as soon as the enumerable yields it, in order:

data: {"version":"v1.0","createSurface":{"surfaceId":"s1","catalogId":"…"}}

data: {"version":"v1.0","updateComponents":{"surfaceId":"s1","components":[…]}}

From a controller:

def events(conn, params), do: A2UIPhoenix.Plug.SSE.stream(conn, MyAgent.messages(params))

or mounted, with a source that is called with the conn (and any further arguments) on each request:

forward "/a2ui/events", A2UIPhoenix.Plug.SSE, source: {MyAgent, :messages, []}

The enumerable is consumed lazily, and no further once the client has gone. The response ends when the enumerable does; since an EventSource reconnects when a response ends, a client using one closes it once its stream is complete.

Resuming a stream

A message given as {id, message} is sent with an id: line. A browser's EventSource that loses the connection reconnects and sends the last id it received in the Last-Event-ID header, which last_event_id/1 reads, so the source can continue after it:

def messages(conn) do
  after_id = A2UIPhoenix.Plug.SSE.last_event_id(conn)
  MyAgent.messages_since(after_id) |> Stream.map(&{&1.seq, &1.message})
end

The :retry option tells the client how long to wait before it reconnects.

Options:

  • :source — for the plug, {module, function, args}: called as apply(module, function, [conn | args]), it returns the enumerable of messages.
  • :event — the event type of every message (default: none, which a browser delivers as a message event).
  • :retry — the reconnection delay in milliseconds, sent first.

Summary

Functions

One event of the stream: its event: line if the :event option is given, its id: line for a {id, message} pair, its data lines and the blank line that ends it.

The id of the last event a reconnecting client received (its Last-Event-ID header), or nil.

Sends messages as an event stream and returns the conn.

Functions

event(message, options \\ [])

(since 0.1.0)
@spec event(map() | list() | String.t() | {String.t() | integer(), term()}, keyword()) ::
  iodata()

One event of the stream: its event: line if the :event option is given, its id: line for a {id, message} pair, its data lines and the blank line that ends it.

last_event_id(conn)

(since 0.1.0)
@spec last_event_id(Plug.Conn.t()) :: String.t() | nil

The id of the last event a reconnecting client received (its Last-Event-ID header), or nil.

stream(conn, messages, options \\ [])

(since 0.1.0)
@spec stream(Plug.Conn.t(), Enumerable.t(), keyword()) :: Plug.Conn.t()

Sends messages as an event stream and returns the conn.

A message is a map (or list) to encode as JSON, or JSON text already encoded; text that spans lines is sent as one data line per line, as the event stream format requires. A {id, message} pair is sent with its id. Options: :event and :retry.