# `Musubi.Store`
[🔗](https://github.com/fahchen/musubi/blob/v0.3.0/lib/musubi/store.ex#L1)

Compile-time DSL entrypoint, behaviour contract, and runtime facade for
Musubi store modules.

Stores `use Musubi.Store` and implement `init/1`, `render/1`,
`update/2`, `handle_command/3`, `handle_async/3`, `handle_info/2`,
and `terminate/2`. Root stores opt in with `use Musubi.Store, root: true`
and may also implement `mount/2` to receive client mount params before
`init/1` runs.

`render/1` returns the resolved Elixir-shaped term; wire conversion happens
separately via `Musubi.Wire.to_wire/1`.

## Runtime facade

`use Musubi.Store` blanket-imports this module so every helper below is
available bare inside a store's callbacks. Each helper is a `defdelegate`
to the underlying implementation module (`Musubi.Socket`, `Musubi.Stream`,
`Musubi.Lifecycle`, `Musubi.Child`, `Musubi.DSL.Render`) or a `defmacro`
that lowers to a runtime call (the async lifecycle helpers).

| Surface          | Helpers                                                                                            |
| :--------------- | :------------------------------------------------------------------------------------------------- |
| Socket           | `assign/2,3`, `assign_new/3`, `update/3`, `changed?/2`, `get_private/2,3`, `put_private/3`         |
| Streams          | `stream/3,4`, `stream_configure/3`, `stream_insert/3,4`, `stream_delete/3`, `stream_delete_by_item_key/3` |
| Lifecycle        | `attach_hook/4`, `detach_hook/3`                                                                   |
| Async            | `assign_async/3,4`, `start_async/3,4`, `stream_async/3,4`, `cancel_async/2,3`                      |
| Render builders  | `child/2`, `stream/1`, `async_stream/1`                                                            |

The fully-qualified module forms remain available — the facade is purely
additive — so `Musubi.Socket.assign(...)` keeps working alongside the bare
`assign(...)` form preferred inside store modules.

# `assigns`

```elixir
@type assigns() :: %{optional(Musubi.Socket.assign_key()) =&gt; value()}
```

# `async_name`

```elixir
@type async_name() :: Musubi.Async.name_arg()
```

# `async_result`

```elixir
@type async_result() :: {:ok, value()} | {:exit, value()}
```

# `command_name`

```elixir
@type command_name() :: atom()
```

# `command_payload`

```elixir
@type command_payload() :: %{optional(String.t() | atom()) =&gt; value()}
```

# `command_reply`

```elixir
@type command_reply() :: map()
```

# `message`

```elixir
@type message() :: value()
```

# `rendered`

```elixir
@type rendered() ::
  nil
  | boolean()
  | number()
  | String.t()
  | atom()
  | [rendered()]
  | %{optional(String.t() | atom()) =&gt; rendered()}
```

# `root_params`

```elixir
@type root_params() :: %{optional(String.t()) =&gt; value()}
```

# `terminate_reason`

```elixir
@type terminate_reason() :: :normal | :shutdown | {:shutdown, value()} | value()
```

# `value`

```elixir
@type value() ::
  nil
  | boolean()
  | number()
  | String.t()
  | atom()
  | pid()
  | reference()
  | port()
  | tuple()
  | [value()]
  | %{optional(value()) =&gt; value()}
```

# `handle_async`
*optional* 

```elixir
@callback handle_async(
  name :: async_name(),
  async_fun_result :: async_result(),
  socket :: Musubi.Socket.t()
) :: {:noreply, Musubi.Socket.t()}
```

Handles an async result routed to the current store.

# `handle_command`

```elixir
@callback handle_command(
  name :: command_name(),
  payload :: command_payload(),
  socket :: Musubi.Socket.t()
) ::
  {:noreply, Musubi.Socket.t()} | {:reply, command_reply(), Musubi.Socket.t()}
```

Handles a declared command for the current store.

# `handle_info`
*optional* 

```elixir
@callback handle_info(message :: message(), socket :: Musubi.Socket.t()) ::
  {:noreply, Musubi.Socket.t()}
```

Handles an in-process message routed to the current store.

# `handle_progress`
*optional* 

```elixir
@callback handle_progress(
  name :: atom(),
  entry :: Musubi.Upload.Entry.t(),
  socket :: Musubi.Socket.t()
) :: {:noreply, Musubi.Socket.t()}
```

Optional. Invoked when a chunk has been received (channel mode) or a
client `upload_progress` message arrived (external mode) for an
upload-tracked entry.

# `init`
*optional* 

```elixir
@callback init(socket :: Musubi.Socket.t()) :: {:ok, Musubi.Socket.t()}
```

Initializes a freshly-created store socket.

# `mount`
*optional* 

```elixir
@callback mount(socket :: Musubi.Socket.t()) :: {:ok, Musubi.Socket.t()}
```

Initializes a freshly-mounted store socket.

This callback is kept for compatibility with pre-session stores. Prefer
`init/1` for child stores and `mount/2` for root-only client params.

# `mount`
*optional* 

```elixir
@callback mount(params :: root_params(), socket :: Musubi.Socket.t()) ::
  {:ok, Musubi.Socket.t()}
```

Mounts a root store with client-supplied params.

Only modules declared with `use Musubi.Store, root: true` receive this
callback. Child stores use `init/1`.

# `render`

```elixir
@callback render(socket :: Musubi.Socket.t()) :: rendered()
```

Produces the resolved Elixir-shaped render output for the current store.

The returned term is still in Musubi's Elixir form. The runtime converts it
to wire form later with `Musubi.Wire.to_wire/1`.

# `terminate`
*optional* 

```elixir
@callback terminate(reason :: terminate_reason(), socket :: Musubi.Socket.t()) :: :ok
```

Handles store teardown after the page runtime begins terminating.

# `update`
*optional* 

```elixir
@callback update(assigns :: assigns(), socket :: Musubi.Socket.t()) ::
  {:ok, Musubi.Socket.t()}
```

Updates a mounted store socket from new parent-supplied assigns.

# `upload_external`
*optional* 

```elixir
@callback upload_external(
  name :: atom(),
  entry :: Musubi.Upload.Entry.t(),
  socket :: Musubi.Socket.t()
) :: {:ok, map(), Musubi.Socket.t()}
```

Optional. When defined for an upload name, the preflight switches to
external mode and forwards the returned `meta` map to the client
uploader. Musubi treats `meta` opaquely.

# `assign`

See `Musubi.Socket.assign/2`.

# `assign`

See `Musubi.Socket.assign/3`.

# `assign_async`
*macro* 

See `Musubi.Async.assign_async/3,4`.

# `assign_async`
*macro* 

# `assign_new`

See `Musubi.Socket.assign_new/3`.

# `async_stream`
*macro* 

See `Musubi.DSL.Render.async_stream/1`. Render-time placeholder.

# `attach_hook`

See `Musubi.Lifecycle.attach_hook/4`.

# `cancel_async`

See `Musubi.Async.cancel_async/2,3`.

# `cancel_async`

# `cancel_upload`

Cancels a single upload entry by `ref`, emitting `{op: cancel}`.

# `changed?`

See `Musubi.Socket.changed?/2`.

# `child`

See `Musubi.Child.child/2`.

# `consume_uploaded_entries`

Consumes the completed upload entries for `name` via `fun`.

`fun` receives `(meta, entry)` where `meta` is `%{path: path}` in
channel mode or `%{external: meta}` in external mode. Returns
`{:ok, val}` to consume the entry (removing it from internal state)
or `{:postpone, val}` to leave it in place.

Returns the updated socket and the list of `val`s produced.

Must be called from a command handler.

# `detach_hook`

See `Musubi.Lifecycle.detach_hook/3`.

# `get_private`

See `Musubi.Socket.get_private/3`.

# `put_private`

See `Musubi.Socket.put_private/3`.

# `start_async`
*macro* 

See `Musubi.Async.start_async/3,4`.

# `start_async`
*macro* 

# `stream`
*macro* 

See `Musubi.DSL.Render.stream/1`. Render-time placeholder.

# `stream`

See `Musubi.Stream.stream/4`.

# `stream_async`
*macro* 

See `Musubi.Async.stream_async/3,4`.

# `stream_async`
*macro* 

# `stream_configure`

See `Musubi.Stream.stream_configure/3`.

# `stream_delete`

See `Musubi.Stream.stream_delete/3`.

# `stream_delete_by_item_key`

See `Musubi.Stream.stream_delete_by_item_key/3`.

# `stream_insert`

See `Musubi.Stream.stream_insert/4`.

# `update`

See `Musubi.Socket.update/3`.

# `uploaded_entries`

Returns `{completed, in_progress}` for the upload named `name`.

## Examples

    {completed, _in_progress} = uploaded_entries(socket, :avatar)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
