# `CaravelaSvelte.Protocol`
[🔗](https://github.com/rsousacode/caravela_svelte/blob/v0.1.0/lib/caravela_svelte/protocol.ex#L1)

Inertia-2.x-compatible request/response shape for
`CaravelaSvelte.Rest`.

Pure functions only — no SSR, no controller logic. Read a
`%Plug.Conn{}`, emit response data; the caller decides how to
send it.

## Protocol summary

A non-Inertia GET request receives a full HTML document with an
embedded page object:

    <div id="app" data-page='{"component":"BookIndex",...}'></div>

Subsequent navigations from the Inertia client arrive with an
`x-inertia: true` request header and a `x-inertia-version: <digest>`.
The server returns JSON with the page object:

    {"component":"BookIndex","props":{...},"url":"/library/books","version":"<digest>"}

Response carries `x-inertia: true` and `vary: X-Inertia`.

### Version mismatch

If the client's `x-inertia-version` differs from the server's
current version, the server returns **409 Conflict** with an
`x-inertia-location: <url>` header. The client reacts by doing
a hard page reload (which bootstraps the new asset bundle).

### Scope

Phase B.2 ships the minimum protocol surface that covers CRUD:

  * first-load full-document response
  * SPA navigation (JSON)
  * version header handling + 409 mismatch

**Not implemented** (deferred — see
[phase_b2_rest_renderer.md](../../../caravela_plan/phoenix/render_modes/phase_b2_rest_renderer.md)
for the non-goals):

  * partial reloads (`x-inertia-partial-data` / `-component`)
  * lazy / async / deferred props
  * history encryption / clearHistory

# `page_object`

```elixir
@type page_object() :: %{
  component: String.t(),
  props: map(),
  url: String.t(),
  version: String.t()
}
```

# `request_kind`

```elixir
@type request_kind() :: :full_document | :navigation
```

# `asset_version`

```elixir
@spec asset_version() :: String.t()
```

Server-side asset version. Defaults to the md5 of
`priv/static/manifest.json` if the file exists, otherwise to the
release hash, otherwise to `"dev"`.

Override per-app:

    config :caravela_svelte, :asset_version, fn -> MyApp.asset_version() end

The config value may be a string or a zero-arity function.

# `client_version`

```elixir
@spec client_version(Plug.Conn.t()) :: String.t() | nil
```

Extract the client's declared asset version, or `nil` when the
header is absent.

# `data_page_attribute`

```elixir
@spec data_page_attribute(page_object()) :: String.t()
```

Serialise the page object as the `data-page` attribute for a
first-load full document. Caller is responsible for the
surrounding HTML; `render_root_html/2` is a convenience.

# `navigation_body`

```elixir
@spec navigation_body(page_object()) :: map()
```

Build the response body for a navigation response. The caller
wraps it with JSON encoding + headers.

# `page_object`

```elixir
@spec page_object(String.t(), map(), String.t(), String.t()) :: page_object()
```

Build the page object sent to the client (both in full-document
and navigation responses).

# `put_inertia_headers`

```elixir
@spec put_inertia_headers(Plug.Conn.t()) :: Plug.Conn.t()
```

Mark a response conn as Inertia-aware (sets the `x-inertia` and
`vary` headers). Used by both navigation responses and the
mismatch fallback.

# `render_root_html`

```elixir
@spec render_root_html(
  page_object(),
  keyword()
) :: iolist()
```

Render the root `<div>` Inertia clients bootstrap from. Not a
full HTML document — callers typically embed this inside a
Phoenix layout.

# `request_kind`

```elixir
@spec request_kind(Plug.Conn.t()) :: request_kind()
```

Classify an incoming request. Returns `:navigation` when the
Inertia client header is present, `:full_document` otherwise.

# `version_matches?`

```elixir
@spec version_matches?(Plug.Conn.t(), String.t()) :: boolean()
```

Return `true` when the client's declared version matches the
server's. First-load requests (no version header) are treated as
matching so we don't bounce them.

# `version_mismatch`

```elixir
@spec version_mismatch(Plug.Conn.t(), String.t()) :: Plug.Conn.t()
```

Build a 409 version-mismatch response. The caller sends it and
halts.

---

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