# `MishkaCmsBuilder.EditorSession.Keeper`
[🔗](https://github.com/mishka-group/mishka_cms_builder/blob/v0.0.1-alpha.2/lib/mishka_cms_builder/editor_session/keeper.ex#L5)

Writes the edits an editing session holds and no draft does — on every change, on the way out of
the editor, and before a reopened session is moved off its draft.

## Why the session, and not the LiveView

An edit lands in the session first: `MishkaCmsBuilder.EditorSession` stores it in ETS, where it
outlives the LiveView that made it. It reaches a draft row a moment later, when the chrome writes
it — and that moment used to belong to the LiveView. The write ran in a task linked to it, and
what the write settled (the draft it made, the revision it carried) was recorded by the LiveView
when the answer came back. A tab closed, a page left or a socket dropped inside that moment took
the write down with the LiveView, and the next visit detached the session from its draft and threw
the unwritten edit away with the tree it replaced. A drop followed by closing the tab at once was
enough.

So a write here:

  * runs in a process of its own under `MishkaCmsCore.Runtime.TaskSupervisor`, linked to nobody,
    so the caller exiting does not stop it;
  * records what it wrote on the session itself — `EditorSession.mark_saved/2` with the draft and
    the revision it serialized — so the session knows its draft even when no LiveView is left to
    hear the answer;
  * waits its turn: one write per session at a time, and each reads the session as stored when its
    turn comes. A write asked for on the way out therefore follows the one already running and
    writes only what that one did not carry, and an older document never lands over a newer one.

A session with nothing unwritten is left alone, so asking costs one ETS read.

# `outcome`

```elixir
@type outcome() ::
  {:ok, struct(), MishkaCmsBuilder.EditorSession.t()}
  | {:clean, MishkaCmsBuilder.EditorSession.t()}
  | :running
  | :error
  | term()
```

What a keep did: wrote, found nothing to write, or did not write.

# `keep`

```elixir
@spec keep(
  String.t(),
  (MishkaCmsBuilder.EditorSession.t() -&gt; {:ok, struct()} | term()),
  timeout()
) ::
  outcome()
```

Writes `token`'s session with `write` when it holds edits no draft has, and marks it saved as of
the revision written.

`write` receives the session as stored when this write's turn comes, and answers `{:ok, draft}` for
a draft it wrote; anything else is a write that did not happen and is answered as it came.

Answers `{:ok, draft, session}` with the session as marked, `{:clean, session}` when there was
nothing to write, `:error` when there is no such session, `{:error, {:exit, reason}}` when the write
crashed, and `:running` when `timeout` passed first — the write then goes on without the caller,
and still records what it wrote.

---

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