# `A2UICore.Resolution.NodeResolver`
[🔗](https://github.com/a2ui-elixir/a2ui/blob/v0.1.1/umbrella/apps/a2ui_core/lib/a2ui_core/resolution/node_resolver.ex#L1)

Resolves a surface's flat component dictionary into a tree of
`A2UICore.Resolution.Node`s.

Child references become nodes, collection templates become one node
per item (each in its item's data scope), components that have not
arrived yet become `:pending` placeholders that are replaced when
their definition does, and a component referenced by one of its own
descendants is cut with a `:cyclic` placeholder.

The resolver is plain data. It does not observe anything: after the
surface changed, hand the new surface to `update/2` and read what
happened from the notifications it returns.

    {resolver, notifications} = NodeResolver.new(surface)
    surface = Surface.put_components(surface, components)
    {resolver, notifications} = NodeResolver.update(resolver, surface)

## Identity

A position is identified by the edge that leads to it (the parent
position, the property, and the component and data path it points at).
While the edge keeps leading to the same kind of node, the node keeps
its `ref`; only its props change. A node is replaced — and the old
one destroyed — when the component behind the edge appears, goes away
or changes type, when a sibling ordinal shifts, or when its parent is
destroyed.

## Notifications

  * `{:node_created, ref}` — a new position is mounted;
  * `{:node_updated, ref}` — a node that was already mounted now has
    different props (reported once per `update/2`, whatever number of
    properties changed);
  * `{:node_destroyed, ref}` — a mounted node is gone; reported once;
  * `{:error | :warning, payload}` — a diagnostic (an unknown component
    type, a cyclic reference, a binding that does not resolve), reported
    when the condition appears and again only after it went away.

`write/4` and `invoke/5` are the interaction half: they say which path
a write reaches and which events and calls an action produces, from
the surface as it is *now*.

## Agent functions

From v1.0 a function call that the surface's catalogs cannot run
locally is the agent's (see
[Agent functions](A2UICore.Resolution.DataContext.html#module-agent-functions)).
The resolver keeps what it knows of those calls in `agent_calls`.
When a property needs a call it has not seen, `update/2` marks the
call pending and reports `{:agent_call, %{key: key, call: call}}`: the
renderer sends `call` to the agent and later hands the outcome to
`settle_agent_call/3`, then calls `update/2` again. Until then the
property is `nil` and its node is `busy`. Settled calls are kept for
the properties and actions that use them again; past 256 calls, the
settled ones no property uses are forgotten.

# `interaction`
*since 0.1.0* 

```elixir
@type interaction() ::
  {:action_event, map()}
  | {:error | :warning, map()}
  | {:write | :messages | :agent_call, map()}
```

# `notification`
*since 0.1.0* 

```elixir
@type notification() ::
  {:node_created, pos_integer()}
  | {:node_updated, pos_integer()}
  | {:node_destroyed, pos_integer()}
  | {:error | :warning, map()}
  | {:agent_call, map()}
```

# `t`
*since 0.1.0* 

```elixir
@type t() :: %A2UICore.Resolution.NodeResolver{
  agent_calls: %{
    optional(term()) =&gt; A2UICore.Resolution.DataContext.agent_call_state()
  },
  disposed?: boolean(),
  edges: %{optional(String.t()) =&gt; pos_integer()},
  next_ref: pos_integer(),
  nodes: %{optional(pos_integer()) =&gt; A2UICore.Resolution.Node.t()},
  reported: MapSet.t(),
  root: pos_integer() | nil,
  surface_id: String.t() | nil
}
```

# `count`
*since 0.1.0* 

```elixir
@spec count(t()) :: non_neg_integer()
```

Number of mounted nodes, placeholders included.

# `dispose`
*since 0.1.0* 

```elixir
@spec dispose(t()) :: {t(), [notification()]}
```

Destroys every node and stops the resolver; further updates do
nothing. Idempotent.

# `invoke`
*since 0.1.0* 

```elixir
@spec invoke(t(), A2UICore.State.Surface.t(), pos_integer(), String.t(), DateTime.t()) ::
  {:ok, [interaction()]} | {:error, A2UICore.Error.t()}
```

Invokes the action in `property` of node `ref`, evaluating its dynamic
parts against `surface` as it is now.

An event action yields `{:action_event, event}` (see
`A2UICore.State.Surface.dispatch_action/4`); a function-call action
runs the catalog function and yields no event. Resolution problems
come back as `{:error, payload}` / `{:warning, payload}` entries, and
what the functions ask for as `{:write, ...}` / `{:messages, ...}`
entries for the renderer to apply (see
`A2UICore.Resolution.DataContext`).

# `new`
*since 0.1.0* 

```elixir
@spec new(A2UICore.State.Surface.t()) :: {t(), [notification()]}
```

Resolves `surface` from scratch.

# `node`
*since 0.1.0* 

```elixir
@spec node(t(), pos_integer()) :: A2UICore.Resolution.Node.t() | nil
```

The mounted node with `ref`, or `nil`.

# `positions`
*since 0.1.0* 

```elixir
@spec positions(t()) :: %{optional(String.t()) =&gt; A2UICore.Resolution.Node.t()}
```

The mounted nodes by position: `"root"`, `"root/child"`,
`"root/children/0"`, `"root/tabs/1/child"`. Positions describe where a
node sits in the tree; they are not identities.

# `root`
*since 0.1.0* 

```elixir
@spec root(t()) :: A2UICore.Resolution.Node.t() | nil
```

The root node, or `nil` while the root component is missing.

# `settle_agent_call`
*since 0.1.0* 

```elixir
@spec settle_agent_call(t(), term(), {:ok, term()} | {:error, map()}) :: t()
```

Records the outcome of the agent function call `key` (from an
`{:agent_call, %{key: key}}` notification): `{:ok, value}` or
`{:error, payload}`. Call `update/2` afterwards to resolve the
properties that use it. A call the resolver no longer knows is
ignored.

# `track_agent_call`
*since 0.1.0* 

```elixir
@spec track_agent_call(t(), term()) :: t()
```

Marks the agent function call `key` as sent, so that properties that
use it wait for its outcome instead of reporting it again; for the
calls an action's interactions ask for (`invoke/5`).

# `update`
*since 0.1.0* 

```elixir
@spec update(t(), A2UICore.State.Surface.t()) :: {t(), [notification()]}
```

Reconciles the tree with `surface`'s current components and data.

Returns the new resolver and the notifications, in the order things
were found (creations and updates, then destructions).

# `write`
*since 0.1.0* 

```elixir
@spec write(t(), pos_integer(), String.t(), term()) ::
  {:ok, String.t(), term()} | {:error, A2UICore.Error.t()}
```

What a write to `property` of node `ref` changes: `{:ok, path, value}`
when the property is a binding to a data path, an error otherwise.
Apply it with `A2UICore.State.Surface.set_data/3` and `update/2`.

---

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