March Docs

Islands

Islands — hydration wrappers, registry, and dataflow helpers for Bastion WASM islands.

Islands are interactive WASM components hydrated on the client. Each island wrapper div carries: data-march-island — module name data-march-hydrate — when to load the WASM module (see HydrateOn) data-march-dataflow — "client" | "server" (see DataflowMode) data-march-state — initial state JSON data-march-island-css — optional scoped CSS (injected into <head> on hydration) data-march-channel — optional channel topic (Client mode, last-write-wins sync) data-march-parent — parent island instance ID (child islands only) data-march-on-event — event handler name on parent (child islands only)

Dataflow modes (spec: specs/islands-data-flow.md):

Client — island owns its own state, handles events locally via WASM.
         Optionally syncs to a channel topic (last-write-wins).
         No channel = purely local state.

Server — server owns state, client is a read-only projection.
         The JS runtime does NOT attach event listeners.
         State arrives only via the wired channel.

Parent-child binding:

Child islands carry data-march-parent (parent instance ID) and
data-march-on-event (handler name). Children dispatch events upward
through the JS runtime's tree-walk. There is no global send bus.
Parents implement handle_child_event to receive dispatched events.

Types

typeHydrateOnHydrateOn =#
typeDataflowModeDataflowMode = ServerOwned | ClientOwned#
typeDataFlowDataFlow = Server | Client#
typeIslandEventIslandEvent = IslandEvent(String, String)#
typeFormConfigFormConfig = FormConfig(String, String)#
typeDescriptorDescriptor = Descriptor(String, HydrateOn, String)#
typeRegistryRegistry = Registry(List(Descriptor))#

Interfaces

interfaceIslandIsland(s)#

Functions

fnbootstrap_scriptbootstrap_script(base_url : String) : String#

Generate the <script type="module"> tag that loads the island client runtime.

The runtime discovers data-march-island elements, opens the WebSocket, and
manages island lifecycle (hydration, event dispatch, DOM morphing).

Parameters:
  base_url — URL prefix where island JS assets are served (e.g. "/_march/islands")
fnbootstrap_script_with_noncebootstrap_script_with_nonce(base_url : String, nonce : String) : String#

Generate the <script type="module"> tag for the island runtime, with a CSP nonce attribute. Use this overload when Bastion.CSP is in the pipeline.

Parameters:
  base_url — URL prefix for island JS assets
  nonce    — CSP nonce from BastionCSP.nonce(conn)

Example:
  Islands.bootstrap_script_with_nonce("/_march/islands", BastionCSP.nonce(conn))
fnclient_modeclient_mode() do ClientOwned end#

Return the Client dataflow mode.

fnclient_onlyclient_only(name : String, strat, state_json : String) : String#

Client-only island placeholder: no server-rendered inner HTML.

The island div is present in the DOM for hydration, but the visible content
is entirely produced by the client-side WASM render function.
fndataflow_mode_strdataflow_mode_str(df : DataFlow) : String#

Convert a DataFlow value to the data-march-dataflow attribute string.

fndataflow_strdataflow_str(mode : DataflowMode) : String#

Convert a dataflow mode value to the data-march-dataflow attribute string.

Accepts either DataflowMode (ServerOwned/ClientOwned) or DataFlow (Server/Client).
fndescriptor_hydratedescriptor_hydrate(d : Descriptor) : HydrateOn#

Return the hydration strategy from a descriptor.

fndescriptor_namedescriptor_name(d : Descriptor) : String#

Return the module name from a descriptor.

fndescriptor_wasm_pathdescriptor_wasm_path(d : Descriptor) : String#

Return the WASM file path from a descriptor.

fndispatch_jsondispatch_json(source_id : String, event : String, payload : String) : String#

Build a dispatch JSON message with source island info.

fneagereager() do Eager end#

Return the Eager hydration strategy.

fnempty_registryempty_registry() : Registry#

Create an empty island registry.

fnevent_nameevent_name(evt : IslandEvent) : String#

Get the event name from an IslandEvent.

fnevent_payloadevent_payload(evt : IslandEvent) : String#

Get the JSON payload from an IslandEvent.

fnevent_to_jsonevent_to_json(evt : IslandEvent) : String#

Encode an IslandEvent as a JSON string.

fnfind_islandfind_island(reg : Registry, name : String) : Option(Descriptor)#

Look up a registered island by module name.

Returns Some(Descriptor) if found, None otherwise.
fnform_channelform_channel(cfg : FormConfig) : String#

Return the channel topic from a FormConfig.

fnform_redirectform_redirect(cfg : FormConfig) : String#

Return the redirect path from a FormConfig.

fnhydrate_attrhydrate_attr(strat) : String#

Convert a HydrateOn strategy to the data-march-hydrate attribute string.

fnlazy_hydratelazy_hydrate() do Lazy end#

Return the Lazy hydration strategy.

fnnew_eventnew_event(name : String, payload : String) do IslandEvent(name, payload) end#

Construct an IslandEvent value.

fnnew_form_confignew_form_config(channel : String, redirect : String) do FormConfig(channel, redirect) end#

Construct a FormConfig value.

fnon_idleon_idle() do OnIdle end#

Return the OnIdle hydration strategy.

fnon_interactionon_interaction() do OnInteraction end#

Return the OnInteraction hydration strategy.

fnon_visibleon_visible() do OnVisible end#

Return the OnVisible hydration strategy.

fnpreload_hintpreload_hint(base_url : String, module_name : String) : String#

Generate a <link rel="modulepreload"> hint for a specific island's WASM file.

Tells the browser to start fetching the WASM file early, before the runtime
requests it. Include one per island that appears above the fold.

Parameters:
  base_url    — URL prefix for island assets
  module_name — island module name (e.g. "Counter")
fnregisterregister(reg : Registry, d : Descriptor) : Registry#

Register an island descriptor in the registry.

Example:
  Islands.empty_registry()
  |> Islands.register(Descriptor("Counter", Eager, "/_bastion/islands/counter.wasm"))
  |> Islands.register(Descriptor("TodoList", Lazy, "/_bastion/islands/todo_list.wasm"))
fnregistry_descriptorsregistry_descriptors(reg : Registry) : List(Descriptor)#

Return the list of all registered island descriptors.

fnregistry_preload_hintsregistry_preload_hints(reg : Registry, base_url : String) : String#

Generate a string of <link rel="modulepreload"> hints for all registered islands.

Returns an empty string if the registry is empty.
fnserver_modeserver_mode() do ServerOwned end#

Return the Server dataflow mode.

fnvalidate_server_htmlvalidate_server_html(html : String) : Result(String, String)#

Validate that server-rendered HTML does not contain client event attributes.

Server islands are read-only projections. The compiler enforces this at
compile time, but this function provides a runtime check for tests and
dynamic render functions.

Returns Ok(html) if the HTML is clean, Err(message) if a forbidden attribute
is found. Forbidden attributes: data-on-click, data-on-input, data-on-change,
data-on-submit, data-on-keydown, data-msg.
fnvalidate_server_islandvalidate_server_island(render_fn : String -> String, state_json : String) : Result(String, String)#

Validate that a render function's output for a given state does not contain client event attributes.

Calls render_fn(state_json) and runs validate_server_html on the result.
Returns Ok(html) or Err(message).
fnwrapwrap(name : String, strat, state_json : String, ssr_html : String) : String#

Wrap server-rendered HTML with island metadata for client hydration.

Defaults to Client dataflow mode. No channel wired (purely local state).

Parameters:
  name       — island module name (e.g. "Counter")
  strat      — when to load the WASM module (HydrateOn)
  state_json — initial state as JSON string
  ssr_html   — server-rendered inner HTML (shown before WASM loads)
fnwrap_childwrap_child(name : String, parent_id : String, strat, state_json : String, css : String, ssr_html : String) : String#

Wrap a child island slotted inside a parent island.

Children always have Server dataflow relative to their parent — they receive
props from the parent and dispatch events upward. The parent must implement
handle_child_event to receive those events.

Parameters:
  name       — child island module name
  parent_id  — parent island's instance ID
  strat      — hydration strategy
  state_json — initial state JSON (derived from parent state)
  css        — scoped CSS (empty string = omit)
  ssr_html   — server-rendered inner HTML
fnwrap_child_with_eventwrap_child_with_event(name : String, parent_id : String, strat, state_json : String, css : String, ssr_html : String, on_event : String) : String#

Wrap a child island with an explicit on_event handler name.

Use this when the parent registers a named event handler via on_event= in
its template. The on_event name must match what the parent's handle_child_event
is registered under.

Parameters:
  name      — child island module name
  parent_id — parent island's instance ID
  strat     — hydration strategy
  state_json — initial state JSON
  css       — scoped CSS (empty string = omit)
  ssr_html  — server-rendered inner HTML
  on_event  — event handler name registered on the parent
fnwrap_child_with_idwrap_child_with_id(name : String, parent_id : String, child_id : String, strat, state_json : String, css : String, ssr_html : String) : String#

Wrap a child island with an explicit instance ID for unique identification.

Parameters:
  name      — child island module name
  parent_id — parent island's instance ID
  child_id  — explicit instance ID for this child island
  strat     — hydration strategy
  state_json — initial state JSON
  css       — scoped CSS (empty string = omit)
  ssr_html  — server-rendered inner HTML
fnwrap_eagerwrap_eager(name : String, state_json : String, ssr_html : String) : String#

Convenience: Client-mode eager wrap.

fnwrap_eager_with_csswrap_eager_with_css(name : String, state_json : String, css : String, ssr_html : String) : String#

Convenience: Client-mode eager wrap with scoped CSS.

fnwrap_formwrap_form(name : String, strat, state_json : String, form_html : String, channel_topic : String, redirect : String) : String#

Wrap a form island with channel and redirect configuration.

Form islands have a built-in submit lifecycle:
  1. User fills fields → UpdateField msgs update local state.
  2. User submits → validate runs client-side.
  3. If valid → push to channel topic.
  4. Server responds → drive state to Success or show validation errors.

Parameters:
  name          — island module name
  strat         — hydration strategy
  state_json    — initial state as JSON string
  form_html     — server-rendered form HTML
  channel_topic — channel topic for form submission (e.g. "contact:submit")
  redirect      — path to redirect to on success (e.g. "/thank-you")
fnwrap_serverwrap_server(name : String, state_json : String, ssr_html : String) : String#

Convenience: Server-mode eager wrap (display-only island, no event listeners).

fnwrap_with_channelwrap_with_channel(name : String, strat, mode, state_json : String, css : String, ssr_html : String, channel_topic : String) : String#

Wrap an island with a channel topic for state sync and an explicit dataflow mode.

When a channel is wired, the JS runtime pushes new state to the channel
topic after each local update (last-write-wins) for Client islands.
Server islands use the channel to receive server-pushed state.

Parameters:
  name          — island module name
  strat         — hydration strategy
  mode          — Client or Server dataflow mode
  state_json    — initial state as JSON string
  css           — scoped CSS (empty string = omit)
  ssr_html      — server-rendered inner HTML
  channel_topic — topic string (e.g. "counter:#{user_id}")
fnwrap_with_csswrap_with_css(name : String, strat, state_json : String, css : String, ssr_html : String) : String#

Wrap with island metadata including scoped CSS.

The CSS string is HTML-escaped and embedded in the data-march-island-css
attribute. The JS runtime creates a <style> tag in <head> on first hydration.

Parameters:
  name       — island module name
  strat      — hydration strategy
  state_json — initial state as JSON string
  css        — scoped CSS (will be escaped for use in HTML attribute)
  ssr_html   — server-rendered inner HTML
fnwrap_with_dataflowwrap_with_dataflow(name : String, strat, mode, state_json : String, css : String, ssr_html : String) : String#

Wrap with an explicit DataflowMode and optional scoped CSS.

Parameters:
  name       — island module name
  strat      — hydration strategy
  mode       — Client or Server dataflow mode (DataflowMode or DataFlow)
  state_json — initial state as JSON string
  css        — scoped CSS string (empty string = omit the attribute)
  ssr_html   — server-rendered inner HTML