IslandSocket
IslandSocket — WebSocket transport for server-side island state management.
Each island instance is registered with an update, render, and optional handle_child_event function. The socket routes messages between the browser and the registered handlers.
Client messages (JSON):
{"island":"Counter-1","type":"init","module":"Counter","payload":{...}}
Register a new island instance with initial state.
{"island":"Counter-1","type":"msg","payload":"Increment"}
Forward a message to an existing island instance.
{"island":"Counter-1","type":"child_event","event":"item_toggled","payload":{...}}
A child island dispatched an event upward. Route to the parent island's
handle_child_event function. The "island" field here is the PARENT's ID.
{"island":"Counter-1","type":"optimistic","payload":{...}}
Client sends locally-computed state for CRDT reconciliation.
{"island":"Counter-1","type":"destroy"}
Remove an island instance.Server responses (JSON):
{"island":"Counter-1","type":"render","payload":"<html>..."}
Server-rendered HTML after a state change.
{"island":"Counter-1","type":"state","payload":{...}}
State update (sent alongside render for client-side state tracking).
{"island":"Counter-1","type":"merge","payload":{...}}
Merged state (CRDT reconciliation response).
{"island":"child-id","type":"props","payload":{...}}
Parent pushes new props to a child island after its own state changed.Usage:
let registry = IslandSocket.new_registry()
|> IslandSocket.register("Counter", counter_update, counter_render)
HttpServer.new(4000)
|> HttpServer.plug(IslandSocket.plug(IslandSocket.default_config(), registry))
|> HttpServer.listen()See also: island_view.march, island_assets.march, islands.march.
Types
Functions
Return the allowed cross-origin list from a config.
Return the path from a config.
Create a default config listening on /_bastion/ws with same-origin only.
Create an empty island state store. Used to initialise a fresh connection.
Look up a handler by module name. Returns Some(handler) or None.
Return the dataflow mode string from an island handler.
Return true if the registry has a child event handler registered for the given island.
Return true if the store has an island registered with the given instance ID.
Create a config with a custom WebSocket path (same-origin only).
Create a config that allows WebSocket upgrades from an explicit list of cross-origin clients (in addition to same-origin).
Example:
IslandSocket.make_config_with_origins("/_bastion/ws", ["https://admin.example.com"])Build a Conn -> Conn plug that upgrades matching requests to an island WebSocket with server-side rendering.
Rejects WebSocket handshakes with a missing Origin header, or an Origin
that does not match the request Host and is not on the config's
allowed-origin list. This defends against cross-site WebSocket hijacking:
browsers do not enforce the same-origin policy on WebSocket connections,
so the server must validate the Origin itself.Process a single JSON message against the given store and registry, returning the new store and a list of JSON response strings.
This is the pure, side-effect-free equivalent of the live WebSocket loop.
Use it in tests to exercise the full bidirectional message protocol without
a live WebSocket connection.
Example:
let reg = IslandSocket.new_registry()
|> IslandSocket.register("Counter", counter_update, counter_render)
let store = IslandSocket.empty_store()
let init = "{\"island\":\"c-1\",\"type\":\"init\",\"module\":\"Counter\",\"payload\":{\"count\":0}}"
let (store2, responses) = IslandSocket.process_msg_for_test(init, store, reg)
-- responses contains render + state JSON stringsRegister an island module with its update and render functions.
Defaults to Client dataflow mode.
update_fn takes (state_json, msg_json) and returns new state_json.
render_fn takes state_json and returns an HTML string.
Example:
IslandSocket.register(registry, "Counter", counter_update, counter_render)Register an island module with both a merge function and a handle_child_event function.
Register an island module with a handle_child_event function.
child_fn receives (state_json, event_name, payload_json) and returns the new
parent state_json. This is called when a child island dispatches an event
upward via the runtime's parent-child tree-walk.Register an island module with an explicit dataflow mode string.
Use "server" for display-only islands that receive state exclusively
via the channel. Use "client" (the default) for interactive islands.Register an island module with a handle_child_event function (alias for register_with_child_handler).
Used when the parent island needs to receive events dispatched upward by
child islands. child_fn receives (state_json, event_name, payload_json).Register an island module with a custom merge function for CRDT reconciliation.
merge_fn takes (local_state_json, remote_state_json) and returns merged state_json.