# `A2UIPhoenix.Plug.SSE`
[🔗](https://github.com/a2ui-elixir/a2ui/blob/v0.1.1/umbrella/apps/a2ui_phoenix/lib/a2ui_phoenix/plug/sse.ex#L1)

Streams A2UI messages to a renderer outside LiveView as
[server-sent events](https://html.spec.whatwg.org/multipage/server-sent-events.html).

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.

# `event`
*since 0.1.0* 

```elixir
@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`
*since 0.1.0* 

```elixir
@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`
*since 0.1.0* 

```elixir
@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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
