March Docs

Bastion.Cmd

lib/cmd.march — Bastion.Cmd: typed commands from island update functions.

Islands (WASM modules compiled from March) follow the Elm Architecture pattern:

update(state, msg) → (new_state, Cmd(Msg))

A Cmd represents a side-effect — an HTTP request, a timer, a navigation event, a channel message, or a direct JS call — that the runtime (wasm-bridge.js / the Bastion client runtime) executes on behalf of the island after each update.

── Usage in an island ──────────────────────────────────────────────────────────

import Bastion.Cmd as Cmd

type Msg =
  | Increment
  | LoadUser(Int)
  | GotUser(String)

fn update(state, msg) do
  match msg do
  Increment ->
    ({ count: state.count + 1 }, Cmd.none())

  LoadUser(id) ->
    (state, Cmd.http_get("/api/users/" ++ String.from_int(id), fn resp ->
      GotUser(resp.body)
    end))

  GotUser(json) ->
    ({ state | user = json }, Cmd.none())
  end
end

── JS Runtime Support ──────────────────────────────────────────────────────────

The Bastion JS runtime (wasm-bridge.js + march-islands.js) interprets Cmd values returned by island update functions. The WASM module must export march_island_update_cmd() alongside march_island_update() for the runtime to receive both the new state AND the command.

Commands are serialised to a JSON envelope by the March compiler's WASM target; the JS runtime dispatches based on the "tag" field.

JSON envelope schema: { "tag": "None" } { "tag": "Batch", "cmds": [...] } { "tag": "HttpGet", "url": "...", "ref": "<callback_id>" } { "tag": "HttpPost", "url": "...", "body": "...", "ref": "<callback_id>" } { "tag": "After", "ms": 500, "msg": <msg_json> } { "tag": "Every", "ms": 1000, "msg": <msg_json> } { "tag": "Focus", "id": "input-field" } { "tag": "Blur", "id": "input-field" } { "tag": "PushUrl", "path": "/new-page" } { "tag": "ReplaceUrl", "path": "/same-page" } { "tag": "StoreLocal", "key": "prefs", "value": "..." } { "tag": "LoadLocal", "key": "prefs", "ref": "<callback_id>" } { "tag": "RemoveLocal", "key": "prefs" } { "tag": "ChannelPush", "event": "msg", "payload": "..." }

Types

typeCmdCmd(msg) =#

Functions

fnafterafter(delay_ms : Int, msg : a) : Cmd(a)#

Deliver msg after delay_ms milliseconds (one-shot).

    Cmd.after(500, HideNotification)
fnbatchbatch(cmds : List(Cmd(a))) : Cmd(a)#

Execute a list of commands in parallel. All commands in the batch are dispatched; their result messages are delivered back to the island.

    Cmd.batch([http_cmd, timer_cmd])
fnblurblur(element_id : String) : Cmd(a)#

Blur (unfocus) the DOM element with the given id.

    Cmd.blur("dropdown-menu")
fnchannel_pushchannel_push(event : String, payload_json : String) : Cmd(a)#

Push a message to the island's wired channel topic (Client-mode islands only). The event and payload_json are forwarded to the server-side channel handler.

    Cmd.channel_push("new_message", "{\"text\":\"hello\"}")
fneveryevery(interval_ms : Int, msg : a) : Cmd(a)#

Deliver msg every interval_ms milliseconds (repeating). The timer fires until the island is destroyed.

    Cmd.every(5000, RefreshData)
fnfocusfocus(element_id : String) : Cmd(a)#

Focus the DOM element with the given id.

    Cmd.focus("search-input")
fnhttp_gethttp_get(url : String, handler : String -> a) : Cmd(a)#

Perform an HTTP GET request. When the response arrives, handler is called with the response body string and the returned message is dispatched.

    Cmd.http_get("/api/users", fn body -> GotUsers(body) end)
fnhttp_posthttp_post(url : String, body : String, handler : String -> a) : Cmd(a)#

Perform an HTTP POST request with a body string.

    Cmd.http_post("/api/items", "{\"name\":\"Alice\"}", fn body -> Created(body) end)
fnis_noneis_none(cmd : Cmd(a)) : Bool#

Return true if this Cmd is a no-op (CmdNone).

fnload_localload_local(key : String, handler : Option(String) -> a) : Cmd(a)#

Read a value from localStorage. handler receives Some(value) if present or None if the key is absent; the returned message is dispatched.

    Cmd.load_local("theme", fn opt -> LoadedTheme(Option.unwrap_or(opt, "light")) end)
fnmapmap(cmd : Cmd(a), f : a -> b) : Cmd(b)#

Map over the message type of a Cmd.

fnnonenone() : Cmd(a)#

No-op command. Use when update produces no side-effects.

fnpush_urlpush_url(path : String) : Cmd(a)#

Push a new URL onto the browser history stack. Does not trigger a page reload.

    Cmd.push_url("/users/42")
fnremove_localremove_local(key : String) : Cmd(a)#

Remove a key from localStorage.

    Cmd.remove_local("session_draft")
fnreplace_urlreplace_url(path : String) : Cmd(a)#

Replace the current URL in the browser history stack (no new entry).

    Cmd.replace_url("/search?q=alice")
fnstore_localstore_local(key : String, value : String) : Cmd(a)#

Persist a string value to localStorage under the given key.

    Cmd.store_local("theme", "dark")