# Getting Started

This tutorial builds a small counter root store, exposes it through a Phoenix
socket, and mounts it from a TypeScript client.

## 1. Add Musubi

In the Phoenix app:

```elixir
def deps do
  [
    {:musubi, "~> 0.1.0"}
  ]
end
```

Run:

```sh
mix deps.get
```

If the application has a TypeScript frontend, add the Musubi compiler so the
generated ambient types stay in sync with the server stores:

```elixir
def project do
  [
    app: :my_app,
    compilers: Mix.compilers() ++ [:musubi_ts]
  ]
end
```

Configure the generated type bundle:

```elixir
config :musubi, :ts_codegen_output_path, "assets/src/generated/musubi.d.ts"
```

## 2. Define A Root Store

Root stores opt in with `use Musubi.Store, root: true`. They may implement
`mount/2`, which receives client mount params before `init/1` runs.

```elixir
defmodule MyAppWeb.Stores.CounterStore do
  use Musubi.Store, root: true

  state do
    field :count, integer()
  end

  command :increment do
    payload :amount, integer()
  end

  @impl Musubi.Store
  def mount(params, socket) do
    {:ok, assign(socket, :count, Map.get(params, "count", 0))}
  end

  @impl Musubi.Store
  def render(socket) do
    %{count: socket.assigns.count}
  end

  @impl Musubi.Store
  def handle_command(:increment, %{"amount" => amount}, socket) do
    {:noreply, update(socket, :count, &(&1 + amount))}
  end
end
```

The `state do` block is both a runtime validation contract and the source for
TypeScript generation. `render/1` returns the Elixir-shaped state; Musubi
serializes it for the wire.

## 3. Declare Mountable Roots

A Musubi socket declares the root stores a client may mount. Application code
implements Musubi callbacks; Phoenix socket and channel behaviours are handled
by the adapter.

```elixir
defmodule MyAppWeb.UserSocket do
  use Musubi.Socket,
    roots: [
      MyAppWeb.Stores.CounterStore
    ]

  @impl Musubi.Socket
  def handle_connect(%{"token" => token}, socket) do
    with {:ok, user} <- MyApp.Auth.verify_user_token(token) do
      {:ok, Musubi.Socket.assign(socket, :current_user, user)}
    else
      _error -> :error
    end
  end

  @impl Musubi.Socket
  def handle_join(_params, socket), do: {:ok, socket}
end
```

`handle_connect/2` runs when Phoenix establishes the socket. Use it for
connection-level authentication and assigns shared by every mounted root.
`handle_join/2` runs once when the Musubi connection joins.

## 4. Wire The Phoenix Endpoint

Register the Musubi socket in the Phoenix endpoint:

```elixir
socket "/socket", MyAppWeb.UserSocket,
  websocket: true,
  longpoll: false
```

If the application needs Phoenix session data in Musubi stores, configure
`connect_info`:

```elixir
socket "/socket", MyAppWeb.UserSocket,
  websocket: [connect_info: [session: @session_options]],
  longpoll: false
```

Stores can then read it with `Musubi.Socket.session(socket)`.

## 5. Mount From TypeScript

`@musubi/client` ships inside the Musubi Hex package under
`deps/musubi/packages/client`. Reference it by local path from the
frontend project's `package.json` (adjust the relative path so it points
at `deps/musubi/packages/client` from the JS app root):

```json
{
  "dependencies": {
    "@musubi/client": "file:../deps/musubi/packages/client",
    "phoenix": "file:../deps/phoenix"
  }
}
```

Then install once after `mix deps.get`:

```sh
pnpm install   # or npm install / yarn install
```

Open one connection, then mount one or more declared root stores by module
name and id:

```ts
import { Socket } from "phoenix"
import { connect } from "@musubi/client"

const socket = new Socket("/socket", {
  params: { token: window.userToken },
})

const connection = await connect(socket)

const counter = await connection.mountStore<
  Musubi.Stores,
  "MyAppWeb.Stores.CounterStore"
>({
  module: "MyAppWeb.Stores.CounterStore",
  id: "counter",
  params: { count: 1 },
})

console.log(counter.count)

await counter.dispatchCommand("increment", { amount: 1 })
```

The `id` must be unique within the Musubi connection. A single connection can
mount many root stores as long as each root id is distinct.

## 6. Regenerate Types

When a store's `state do` or `command` declarations change, regenerate the
TypeScript bundle:

```sh
mix compile
```

In CI, check for drift:

```sh
mix compile.musubi_ts --check
```

## Wire Encoding: Atoms Become Strings

Atom-typed fields and atom literals serialize to JSON strings. The TypeScript
codegen emits matching string-literal unions; compare with strings on the
client.

| Elixir field type                            | TypeScript                            | Wire    |
| :------------------------------------------- | :------------------------------------ | :------ |
| `field :winner, :p1 \| :p2 \| :draw \| nil`  | `"p1" \| "p2" \| "draw" \| null`      | `"p1"`  |
| `field :status, atom()`                      | `string`                              | `"on"`  |

The mental model: every atom alternative becomes its string form (the
atom's name verbatim, via `Atom.to_string/1`). Elixir atoms are
lowercase by convention, so `:p1` arrives as `"p1"`; an atom like
`:HTTPError` would arrive as `"HTTPError"`. `nil` serialises to JSON
`null`. The Elixir side keeps the atom shape inside `socket.assigns`;
the conversion happens on the way out through `Musubi.Wire.to_wire/1`.

## Next Steps

- Read `Phoenix Setup` for connection/session details.
- Read `Client and React` for React hooks and root cleanup.
- Read `Testing` for the `Musubi.Testing` store-test harness.
- Read `Client Contract` for the wire and proxy model.
