March Docs

Bastion.IslandServer

lib/island_server.march — Bastion.IslandServer: server-push helpers for islands.

Provides the application-code API for pushing state updates to connected Server-mode islands and triggering re-renders across all subscribers.

Under the hood this calls Bastion.PubSub.broadcast/4. Every island instance that was rendered with Islands.wrap_with_channel(..., ServerOwned, ...) and whose JS runtime has called init registers a PubSub subscription on that topic. A push here fans out to every connected instance simultaneously.

── Typical usage ──────────────────────────────────────────────────────────────

-- In a Channel handler when scores change:
fn handle_in(cc, "score_update", payload_json) do
  let new_scores = Scores.apply_update(Scores.current(), payload_json)
  let _ = Bastion.IslandServer.push("scoreboard:main", Json.encode(new_scores))
  Bastion.Channel.push(cc, "ok", "{}")
end

-- In a page layout, render the Server island with a channel wired up:
Islands.wrap_with_channel(
  "ScoreBoard", Islands.eager(), Islands.server_mode(),
  Scores.to_json(initial_scores), "",
  ScoreBoard.render(initial_scores),
  "scoreboard:main"
)

── How it works ──────────────────────────────────────────────────────────────

  1. The JS runtime sends an init message that includes a channel field.
  2. IslandSocket subscribes the island instance to the PubSub topic, storing
   a send_fn that captures the WS socket, island ID, and render function.
3. Calling IslandServer.push/2 broadcasts to that topic.
4. Each subscriber's send_fn fires, sending `render` + `state` WS frames to
   the relevant browser connection.  No iteration over connections required.

Functions

fnhas_subscribershas_subscribers(topic : String) : Bool#

Return true if at least one island instance is connected on this topic.

    if Bastion.IslandServer.has_subscribers("scoreboard:main") do
      Bastion.IslandServer.push("scoreboard:main", state_json)
    else () end
fnpushpush(topic : String, state_json : String)#

Push a state update to every Server-mode island connected on the given channel topic. Each instance will re-render using its registered render function and send render + state WebSocket frames to the browser.

topic      — the channel topic matching the `data-march-channel` attribute
state_json — new state as a JSON string

    Bastion.IslandServer.push("scoreboard:main", Json.encode(new_scores))
    Bastion.IslandServer.push("dashboard:#{user_id}", Dashboard.encode(data))
fnpush_frompush_from(exclude_sub : String, topic : String, state_json : String)#

Push a state update to all subscribers except one.

Use this when the initiating client already has the new state (e.g. it just
sent it via channel_push) and does not need to receive it back.

exclude_sub — the PubSub sub_id to skip.  For islands the format is
              "conn:<conn_id>:<island_id>", but callers can pass any
              sub_id that was registered via Bastion.PubSub.subscribe/3.
fnsubscriber_countsubscriber_count(topic : String) : Int#

Return the number of island instances currently subscribed to a topic. Useful in tests or admin dashboards to verify islands are connected.

    let count = Bastion.IslandServer.subscriber_count("scoreboard:main")