Socket struct and assign helpers for Musubi runtimes.
Summary
Types
Keys written into the assigns map.
Application-level denial reasons returned by socket callbacks.
Return shape for Musubi socket lifecycle callbacks.
Phoenix connect_info data captured when the Musubi socket connects.
Parameters supplied when the transport socket connects.
Parameters supplied when a Musubi connection joins.
Path segments identifying a store's parent path.
The private bookkeeping map carried by the socket.
Client params supplied for the current root store mount.
Root store modules that may be mounted through this socket.
Session data shared by all root stores on one Musubi socket.
Runtime identity of a store node — array of local ids from root.
Functions
Declares a Musubi socket module.
Returns whether any assign key is marked as changed since the last render cycle.
Assigns many keys from a keyword list or map.
Assigns one key on the socket and records the change in __changed__.
Assigns a value to key only if key is not already present in the socket's
assigns. fun is invoked lazily and receives no arguments.
Returns whether the given assign key is marked as changed.
Returns Phoenix connect_info data captured when the Musubi socket connected.
Returns whether any consumed key appears in the socket's __changed__ map.
Fetches a declared root store module by its client module string.
Reads a private runtime value.
Copies shared Musubi context from one socket to another.
Stores Phoenix connect_info data on the Musubi socket.
Writes a private runtime value.
Stores the current root store mount params in socket private context.
Stores session data shared by all root stores on one Musubi socket.
Clears the LiveView-style __changed__ bookkeeping after a render cycle.
Returns the params supplied when the current root store was mounted.
Returns the session data shared by all root stores on one Musubi socket.
Returns the runtime identity (store_id) of the store node owning this socket.
Updates one assign by applying fun to the current value.
Types
@type assign_key() :: term()
Keys written into the assigns map.
@type callback_error_reason() ::
:unauthorized | :not_found | :invalid_params | :forbidden
Application-level denial reasons returned by socket callbacks.
@type callback_result() :: {:ok, t()} | :error | {:error, callback_error_reason()}
Return shape for Musubi socket lifecycle callbacks.
@type connect_info() :: map()
Phoenix connect_info data captured when the Musubi socket connects.
@type connect_params() :: map()
Parameters supplied when the transport socket connects.
@type join_params() :: map()
Parameters supplied when a Musubi connection joins.
Path segments identifying a store's parent path.
@type private_key() :: term()
The private bookkeeping map carried by the socket.
@type root_params() :: map()
Client params supplied for the current root store mount.
@type roots() :: [module()]
Root store modules that may be mounted through this socket.
@type session() :: map()
Session data shared by all root stores on one Musubi socket.
@type store_id() :: [String.t()]
Runtime identity of a store node — array of local ids from root.
Callbacks
@callback handle_connect(params :: connect_params(), socket :: t()) :: callback_result()
Runs once when the transport socket connects.
Use this callback for connection-level authentication and assigns that should be visible to every Musubi connection and mounted root store.
@callback handle_join(params :: join_params(), socket :: t()) :: callback_result()
Runs once when a Musubi connection joins.
Use this callback for connection-level scope checks such as workspace or tenant authorization. It does not receive root store module or id data; those are validated later when the root store is mounted.
Functions
Declares a Musubi socket module.
The generated module is a Phoenix socket adapter internally, but application code implements only Musubi callbacks.
Examples
defmodule MyAppWeb.UserSocket do
use Musubi.Socket,
roots: [
MyApp.Stores.DashboardStore,
MyApp.Stores.PollRoomStore
]
@impl Musubi.Socket
def handle_connect(%{"token" => token}, socket) do
{:ok, Musubi.Socket.assign(socket, :token, token)}
end
@impl Musubi.Socket
def handle_join(_params, socket), do: {:ok, socket}
end
Returns whether any assign key is marked as changed since the last render cycle.
Examples
iex> Musubi.Socket.any_changed?(%Musubi.Socket{})
false
iex> socket = Musubi.Socket.assign(%Musubi.Socket{}, :title, "Inbox")
iex> Musubi.Socket.any_changed?(socket)
true
Assigns many keys from a keyword list or map.
Examples
iex> socket = %Musubi.Socket{}
iex> socket = Musubi.Socket.assign(socket, %{title: "Inbox", count: 2})
iex> socket.assigns.title
"Inbox"
iex> socket.assigns.count
2
@spec assign(t(), assign_key(), term()) :: t()
Assigns one key on the socket and records the change in __changed__.
Examples
iex> socket = %Musubi.Socket{}
iex> socket = Musubi.Socket.assign(socket, :title, "Inbox")
iex> socket.assigns.title
"Inbox"
iex> socket.assigns.__changed__
%{title: true}
@spec assign_new(t(), assign_key(), (-> term())) :: t()
Assigns a value to key only if key is not already present in the socket's
assigns. fun is invoked lazily and receives no arguments.
Useful for setting defaults that should not overwrite parent-supplied values.
When key is already present the socket is returned unchanged and the
__changed__ map is not touched.
Examples
iex> socket = Musubi.Socket.assign_new(%Musubi.Socket{}, :count, fn -> 0 end)
iex> socket.assigns.count
0
iex> socket = Musubi.Socket.assign_new(socket, :count, fn -> 99 end)
iex> socket.assigns.count
0
@spec changed?(t(), assign_key()) :: boolean()
Returns whether the given assign key is marked as changed.
Examples
iex> socket = Musubi.Socket.assign(%Musubi.Socket{}, :title, "Inbox")
iex> Musubi.Socket.changed?(socket, :title)
true
iex> Musubi.Socket.changed?(socket, :count)
false
@spec connect_info(t()) :: connect_info()
Returns Phoenix connect_info data captured when the Musubi socket connected.
Examples
iex> Musubi.Socket.connect_info(%Musubi.Socket{})
%{}
@spec consumed_keys_changed?(t(), [assign_key()]) :: boolean()
Returns whether any consumed key appears in the socket's __changed__ map.
Examples
iex> socket = Musubi.Socket.assign(%Musubi.Socket{}, :title, "Inbox")
iex> Musubi.Socket.consumed_keys_changed?(socket, [:title, :count])
true
iex> Musubi.Socket.consumed_keys_changed?(socket, [:count])
false
Fetches a declared root store module by its client module string.
The string is compared against modules already declared in the socket; Musubi does not convert arbitrary strings into atoms.
Examples
iex> defmodule SocketFetchRootByModuleDoc do
...> defmodule Store do
...> use Musubi.Store, root: true
...>
...> state do
...> field :ok, boolean()
...> end
...>
...> def render(_socket), do: %{ok: true}
...> def handle_command(_name, _payload, socket), do: {:noreply, socket}
...> end
...>
...> use Musubi.Socket, roots: [Store]
...> end
iex> Musubi.Socket.fetch_root_by_module(SocketFetchRootByModuleDoc, "SocketFetchRootByModuleDoc.Store")
{:ok, SocketFetchRootByModuleDoc.Store}
iex> Musubi.Socket.fetch_root_by_module(SocketFetchRootByModuleDoc, "Missing.Store")
:error
@spec get_private(t(), private_key(), term()) :: term()
Reads a private runtime value.
Examples
iex> socket = %Musubi.Socket{private: %{hooks: %{}}}
iex> Musubi.Socket.get_private(socket, :hooks)
%{}
iex> Musubi.Socket.get_private(socket, :missing, :fallback)
:fallback
Copies shared Musubi context from one socket to another.
The copied context includes session and connect_info. Root params are intentionally per-root and are not copied.
Examples
iex> source = Musubi.Socket.put_session(%Musubi.Socket{}, %{"user_id" => "u1"})
iex> target = Musubi.Socket.inherit_context(source, %Musubi.Socket{})
iex> Musubi.Socket.session(target)
%{"user_id" => "u1"}
@spec put_connect_info(t(), connect_info()) :: t()
Stores Phoenix connect_info data on the Musubi socket.
Examples
iex> socket = Musubi.Socket.put_connect_info(%Musubi.Socket{}, %{peer_data: %{address: {127, 0, 0, 1}}})
iex> Musubi.Socket.connect_info(socket)
%{peer_data: %{address: {127, 0, 0, 1}}}
@spec put_private(t(), private_key(), term()) :: t()
Writes a private runtime value.
Examples
iex> socket = Musubi.Socket.put_private(%Musubi.Socket{}, :hooks, %{})
iex> socket.private.hooks
%{}
@spec put_root_params(t(), root_params()) :: t()
Stores the current root store mount params in socket private context.
Examples
iex> socket = Musubi.Socket.put_root_params(%Musubi.Socket{}, %{"poll_id" => "p1"})
iex> Musubi.Socket.root_params(socket)
%{"poll_id" => "p1"}
Stores session data shared by all root stores on one Musubi socket.
Examples
iex> socket = Musubi.Socket.put_session(%Musubi.Socket{}, %{"user_id" => "u1"})
iex> Musubi.Socket.session(socket)
%{"user_id" => "u1"}
Clears the LiveView-style __changed__ bookkeeping after a render cycle.
Examples
iex> socket = Musubi.Socket.assign(%Musubi.Socket{}, :title, "Inbox")
iex> Musubi.Socket.reset_changed(socket).assigns.__changed__
%{}
@spec root_params(t()) :: root_params()
Returns the params supplied when the current root store was mounted.
Examples
iex> Musubi.Socket.root_params(%Musubi.Socket{})
%{}
Returns the session data shared by all root stores on one Musubi socket.
Examples
iex> Musubi.Socket.session(%Musubi.Socket{})
%{}
Returns the runtime identity (store_id) of the store node owning this socket.
The store_id is the array of local id strings from the root down to this
node. The root has store_id = []. Each non-root node has
store_id = parent_path ++ [id].
Examples
iex> Musubi.Socket.store_id(%Musubi.Socket{parent_path: [], id: ""})
[]
iex> Musubi.Socket.store_id(%Musubi.Socket{parent_path: [], id: "filters"})
["filters"]
iex> Musubi.Socket.store_id(%Musubi.Socket{parent_path: ["filters"], id: "primary"})
["filters", "primary"]
@spec update(t(), assign_key(), (term() -> term())) :: t()
Updates one assign by applying fun to the current value.
Examples
iex> socket = Musubi.Socket.assign(%Musubi.Socket{}, :count, 1)
iex> socket = Musubi.Socket.update(socket, :count, &(&1 + 1))
iex> socket.assigns.count
2