A2UICore.Resolution.DataContext (A2UICore v0.1.1)

Copy Markdown View Source

A data-binding scope: the absolute path relative bindings resolve against, the collection-template iteration index, and — when built for a surface — what is needed to evaluate dynamic values.

The root scope is "/". A component rendered for item 2 of a /items template runs in the nested scope "/items/2/", so its relative path name resolves to /items/2/name.

Path resolution

Follows the reference:

  • an absolute path (starting with /) is returned unchanged,

  • "" and "." resolve to the scope itself,

  • any other path is joined to the scope, ignoring trailing slashes on the scope.

    iex> ctx = A2UICore.Resolution.DataContext.new("/user") iex> A2UICore.Resolution.DataContext.resolve_path(ctx, "name") "/user/name" iex> A2UICore.Resolution.DataContext.resolve_path(ctx, "/other") "/other" iex> A2UICore.Resolution.DataContext.resolve_path(ctx, ".") "/user"

Dynamic values

resolve_dynamic_value/2 evaluates a literal, a data binding ({"path": …} in v0.9, {"@path": …} in v1.0) or a function call ({"call": …, "args": …} / {"@call": …, "args": …}, optionally with a catalogId) against the data model, recursing through arrays and objects. It never raises: what the reference reports through the surface comes back as effects next to the value —

  • {:warning, %{code: "MISSING_DATA_BINDING", path: …, message: …}} when a bound path is absent (once per path per call),
  • {:error, %{code: "EXPRESSION_ERROR", message: …, expression: …}} when a function call fails; the call's value is then nil.

A function can ask for more through its effects — the MCP catalog's functions do (A2UICore.Functions.MCP):

  • {:write, %{path: path, value: value}} — write the data model;
  • {:messages, %{messages: messages, source: :resource | :inline}} — apply A2UI messages.

They come back in evaluation order. A renderer applies them when they come from an action (A2UICore.Resolution.NodeResolver.invoke/5); rendering ignores them.

Agent functions

From v1.0 a function that the catalog a call resolves to does not declare, or declares without an implementation, may be the agent's: a renderer sends it to the agent as a callAgentFunction message. A scope built with the :agent_calls option routes such calls instead of failing them. The option holds what the renderer knows of the calls it sent, keyed by {name, catalog_id, args} (catalog_id is the one the call names, else nil; args are resolved):

  • :pending — sent, no response yet; the call's value is nil;
  • {:ok, value} — the agent's response; the call's value is value;
  • {:error, payload} — the call failed; its value is nil.

Every routed call comes back as an {:agent_call, %{key: key, call: call}} effect, where call is the function call object to send ("@call", resolved "args", and the "catalogId" the call named), so that the renderer sends the calls it has not sent yet. With agent_calls: {:send, calls}, the scope of a function-call action, the call the action makes is always routed without a lookup, and its effect also carries action: true; the calls in its arguments are looked up in calls. A call that names a catalog the scope does not reach is still a catalog error.

Who may call

A function the renderer runs is refused, with an EXPRESSION_ERROR, when its catalog reserves it for the agent (allowedCallers: "agentOnly"), and when it requires a user gesture (requiresUserActivation: true, as openUrl does) and the scope was not built for one with user_activated: true. A renderer builds its action scopes that way (A2UICore.Resolution.NodeResolver.invoke/5); property bindings and check conditions never are.

v1.0 objects follow the reserved-key rule (A2UICore.Validation.ReservedKeys): @@x is the literal key @x, any other single-@ key is an INVALID_RESERVED_KEY error.

Summary

Types

What a renderer knows of an agent function call it sent.

The agent function calls a scope knows, by {name, catalog_id, args}; nil when the scope does not route calls to the agent.

A warning or error produced while resolving, what a function asks for, or a call routed to the agent.

t()

Functions

Creates the root scope for a A2UICore.State.Surface: its data model, its default catalog and the catalogs it can reach.

The active collection index: the nearest scope's recorded index, or the numeric last segment of its path, walking outward; nil outside a collection.

Creates a child scope for relative_path (a leading / is ignored), optionally recording the iteration index of a collection template.

Creates a root scope at path (default "/"). The stored path always ends with a slash.

Resolves an action's dynamic parts: an event's context and userMessage, a plain action object, or a function call.

Evaluates a dynamic value; see the module documentation.

Resolves path against the scope to an absolute pointer.

Whether bindings use the v1.0 @path/@call spelling, decided by the scope's protocol version (that of its surface, which outlives a missing default catalog).

Types

agent_call_state()

(since 0.1.0)
@type agent_call_state() :: :pending | {:ok, term()} | {:error, map()}

What a renderer knows of an agent function call it sent.

agent_calls()

(since 0.1.0)
@type agent_calls() ::
  nil
  | %{optional(term()) => agent_call_state()}
  | {:send, %{optional(term()) => agent_call_state()}}

The agent function calls a scope knows, by {name, catalog_id, args}; nil when the scope does not route calls to the agent.

effect()

(since 0.1.0)
@type effect() ::
  {:warning, map()}
  | {:error, map()}
  | {:write, map()}
  | {:messages, map()}
  | {:agent_call, map()}

A warning or error produced while resolving, what a function asks for, or a call routed to the agent.

t()

(since 0.1.0)
@type t() :: %A2UICore.Resolution.DataContext{
  agent_calls: agent_calls(),
  catalog: A2UICore.Catalog.t() | nil,
  catalogs: %{optional(String.t()) => A2UICore.Catalog.t()},
  data_model: A2UICore.State.DataModel.t() | nil,
  index: non_neg_integer() | nil,
  locale: String.t() | nil,
  parent: t() | nil,
  path: String.t(),
  protocol_version: String.t() | nil,
  surface_id: String.t() | nil,
  user_activated: boolean()
}

Functions

for_surface(surface, options \\ [])

(since 0.1.0)
@spec for_surface(A2UICore.State.Surface.t(), keyword()) :: t()

Creates the root scope for a A2UICore.State.Surface: its data model, its default catalog and the catalogs it can reach.

Options: :path (default "/"), :locale (default: the surface's), :agent_calls and :user_activated.

index(data_context)

(since 0.1.0)
@spec index(t()) :: non_neg_integer() | nil

The active collection index: the nearest scope's recorded index, or the numeric last segment of its path, walking outward; nil outside a collection.

iex> root = A2UICore.Resolution.DataContext.new()
iex> item = A2UICore.Resolution.DataContext.nested(root, "items/3")
iex> A2UICore.Resolution.DataContext.index(item)
3
iex> A2UICore.Resolution.DataContext.index(root)
nil

nested(context, relative_path, index \\ nil)

(since 0.1.0)
@spec nested(t(), String.t(), non_neg_integer() | nil) :: t()

Creates a child scope for relative_path (a leading / is ignored), optionally recording the iteration index of a collection template.

new(path \\ "/", options \\ [])

(since 0.1.0)
@spec new(String.t(), keyword()) :: t()

Creates a root scope at path (default "/"). The stored path always ends with a slash.

Options: :data_model, :catalog (the default catalog, if any), :catalogs (%{catalog_id => catalog} reachable by catalogId), :protocol_version (default: the default catalog's), :surface_id (for messages), :locale, :agent_calls (see Agent functions), :user_activated (see Who may call; default false).

resolve_action(context, action)

(since 0.1.0)
@spec resolve_action(t(), term()) :: {term(), [effect()]}

Resolves an action's dynamic parts: an event's context and userMessage, a plain action object, or a function call.

resolve_dynamic_value(context, value)

(since 0.1.0)
@spec resolve_dynamic_value(t(), term()) :: {term(), [effect()]}

Evaluates a dynamic value; see the module documentation.

resolve_path(data_context, absolute)

(since 0.1.0)
@spec resolve_path(t(), String.t()) :: String.t()

Resolves path against the scope to an absolute pointer.

v1?(data_context)

(since 0.1.0)
@spec v1?(t()) :: boolean()

Whether bindings use the v1.0 @path/@call spelling, decided by the scope's protocol version (that of its surface, which outlives a missing default catalog).