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

A versioned set of component and function definitions: the model of a
catalog document.

`from_json/2` reads a document into the model and `to_json/2` writes it
back, so `from_json(document) |> to_json()` equals the document (apart
from components and functions its `anyComponent` / `anyFunction`
unions leave out). Each component and function keeps its schema as
written, with its `allOf` mixins and references; the catalog keeps the
document's top-level fields. Two derived views are computed when a
catalog is built:

  * `validation_schema/1` (also the `:schema` field) — the
    self-contained JSON Schema messages are validated against
    (`A2UICore.Catalog.ValidationSchema`): components flattened,
    common types bundled, every reference local;
  * `resolved_component_schema/2` and `resolved_function_schema/2` —
    an entry's schema with references to the catalog's own `$defs` and
    to peer catalogs written inline, references to common types kept.

## Peer catalogs

A catalog may reuse definitions of a catalog it holds, its peer,
through an external `$ref` such as
`simplified_catalog_v1_0.json#/components/Text`. `with_peer_catalogs/2`
returns a copy linked to peers, and `link_catalogs/2` links a set of
catalogs to one another; a reference that names no peer stays as
written, so validating against it fails.

## Loading rules

  * a `catalogId` must be known (option or document); the protocol
    version defaults to the document's `protocolVersion`, else `0.9`;
  * `components` maps names to schema objects; `functions` is a map of
    name to definition or a list of `%{"name" => …}` definitions;
  * `$defs.anyComponent` / `$defs.anyFunction`, when they list
    `#/components/X` / `#/functions/X` references, restrict the
    catalog to those names;
  * from v1.0 on, component, property, function and argument names
    must be UAX #31 identifiers (no `@` prefix: that namespace belongs
    to system functions);
  * the theme is read from a top-level `theme`, else `$defs.theme`.

Failures are `{:error, %A2UICore.Error{category: :catalog}}`.

# `t`
*since 0.1.0* 

```elixir
@type t() :: %A2UICore.Catalog{
  catalog_id: String.t(),
  common_types_uri: String.t(),
  component_order: [String.t()],
  components: %{optional(String.t()) =&gt; A2UICore.Catalog.Component.t()},
  declared_protocol_version: String.t() | nil,
  declares_catalog_id: boolean(),
  declares_components: boolean(),
  defs: map(),
  description: String.t() | nil,
  document_order: Jason.OrderedObject.t() | nil,
  extras: map(),
  function_order: [String.t()],
  functions: %{optional(String.t()) =&gt; A2UICore.Catalog.Function.t()},
  functions_form: :map | :list | nil,
  instructions: String.t() | nil,
  layout: map(),
  linked_from: t() | nil,
  peers: [t()],
  protocol_version: String.t(),
  ref_specs: %{optional(String.t()) =&gt; A2UICore.Catalog.RefSpec.t()},
  resolved: map(),
  schema: map(),
  schema_dialect: String.t() | nil,
  schema_id: String.t() | nil,
  source: t() | nil,
  theme: map(),
  theme_location: String.t() | nil,
  title: String.t() | nil,
  union_defs: %{optional(String.t()) =&gt; map() | nil} | nil
}
```

# `common_definitions`
*since 0.1.0* 

```elixir
@spec common_definitions(String.t()) :: map()
```

The `$defs` of the protocol version's `common_types.json`, with
cross-document references made local. Empty for an unsupported
version.

# `copy_with`
*since 0.1.0* 

```elixir
@spec copy_with(t(), keyword()) :: t()
```

A copy of `catalog` for catalog transformers. Options: `:components`
and `:functions` (the copy's entries; default: this catalog's) and
`:defs` (its own `$defs`; default: this catalog's, without those only
dropped entries referenced). An authored `anyComponent` /
`anyFunction` union no longer describes changed entries, so it is
listed from them instead.

# `cross_document?`
*since 0.1.0* 

```elixir
@spec cross_document?(t()) :: boolean()
```

Whether the catalog document has a `$ref` that may name a peer
catalog.

# `derive`
*since 0.1.0* 

```elixir
@spec derive(t(), String.t(), keyword()) :: t()
```

Returns a copy of `catalog` under another id, optionally without its
functions (`functions: false`).

# `from_json`
*since 0.1.0* 

```elixir
@spec from_json(map() | Jason.OrderedObject.t(), keyword()) ::
  {:ok, t()} | {:error, A2UICore.Error.t()}
```

Builds a catalog from a catalog document: a map, or a
`Jason.OrderedObject` (`Jason.decode(text, objects: :ordered_objects)`),
whose key order then orders the components and functions, as the
`anyComponent` / `anyFunction` unions of the validation schema list
them. A map's entries are ordered by name.

Options: `:catalog_id` and `:protocol_version` take precedence over
the document's `catalogId` / `protocolVersion`.

# `from_json!`
*since 0.1.0* 

```elixir
@spec from_json!(map() | Jason.OrderedObject.t(), keyword()) :: t()
```

Like `from_json/2`, raising `A2UICore.Error` on failure.

# `get_component`
*since 0.1.0* 

```elixir
@spec get_component(t(), term()) :: A2UICore.Catalog.Component.t() | nil
```

Looks a component up by name.

# `get_function`
*since 0.1.0* 

```elixir
@spec get_function(t(), term()) :: A2UICore.Catalog.Function.t() | nil
```

Looks a function up by name. As in the reference, a name that differs
from a declared one only in the case of its first letter also matches.

# `inline_refs`
*since 0.1.0* 

```elixir
@spec inline_refs(t(), term(), map(), keyword()) :: term()
```

Writes the local references of `node` (resolved in `root`, usually the
catalog's document) and its references into the catalog's peers
inline, keeping references to common types. A prompt uses this to find
the common types a catalog reaches through its peers.

With `ordered: true` the peers' documents are read as
`to_json(peer, ordered: true)`, so that a node read from an ordered
document keeps its key order wherever it is inlined.

# `link_catalogs`
*since 0.1.0* 

```elixir
@spec link_catalogs([t()], [t()]) :: [t()]
```

The catalogs linked to one another for cross-catalog references: each
catalog with a reference into another document is replaced by a copy
whose peers are `extra_peers` followed by the other catalogs; the
others are returned unchanged.

# `new`
*since 0.1.0* 

```elixir
@spec new(String.t(), String.t(), keyword()) ::
  {:ok, t()} | {:error, A2UICore.Error.t()}
```

Assembles a catalog from parts (a catalog defined in code).

Options: `:components` and `:functions` (lists of
`A2UICore.Catalog.Component` / `A2UICore.Catalog.Function`), `:theme`
(a schema), `:instructions`, `:defs` (the catalog's own `$defs`).

# `new!`
*since 0.1.0* 

```elixir
@spec new!(String.t(), String.t(), keyword()) :: t()
```

Like `new/3`, raising `A2UICore.Error` on failure.

# `put_implementations`
*since 0.1.0* 

```elixir
@spec put_implementations(t(), %{
  optional(String.t()) =&gt; A2UICore.Catalog.Function.execute()
}) :: t()
```

Attaches function implementations by name. Names the catalog does
not declare are ignored.

# `put_system_functions`
*since 0.1.0* 

```elixir
@spec put_system_functions(t(), [A2UICore.Catalog.Function.t()]) :: t()
```

Adds functions the runtime supplies (system functions such as
`@index`), replacing any of the same name. They are evaluated like the
catalog's own but are not part of its document or validation schema.

# `ref_spec`
*since 0.1.0* 

```elixir
@spec ref_spec(t(), String.t()) :: A2UICore.Catalog.RefSpec.t() | nil
```

The child-reference analysis of a component, or `nil` for an unknown
one.

# `resolved_component_schema`
*since 0.1.0* 

```elixir
@spec resolved_component_schema(t(), String.t()) :: term()
```

A component's schema with references to the catalog's own `$defs` and
to peers written inline, or `nil` for an unknown component.

# `resolved_function_schema`
*since 0.1.0* 

```elixir
@spec resolved_function_schema(t(), String.t()) :: term()
```

A function's schema with references resolved (see
`resolved_component_schema/2`), or `nil`.

# `resolved_theme_schema`
*since 0.1.0* 

```elixir
@spec resolved_theme_schema(t()) :: map()
```

The theme schema with references resolved.

# `to_json`
*since 0.1.0* 

```elixir
@spec to_json(t(), keyword()) :: map() | Jason.OrderedObject.t()
```

The catalog document, with references left as written: common types
stay external (`common_types.json#/$defs/X`) and nothing is bundled.
For the schema messages validate against, see `validation_schema/1`.

With `ordered: true` the objects of the document are
`Jason.OrderedObject`s, so that `Jason.encode!/1` writes it in the
reference's key order: the top-level fields in a fixed order, and what
was read from a document decoded with `objects: :ordered_objects` in
that document's order.

# `to_schema`
*since 0.1.0* 

```elixir
@spec to_schema(t()) :: map()
```

The self-contained catalog schema document; see `validation_schema/1`.

# `v1?`
*since 0.1.0* 

```elixir
@spec v1?(t()) :: boolean()
```

Whether the catalog's protocol version is v1.0 or later.

# `validation_schema`
*since 0.1.0* 

```elixir
@spec validation_schema(t()) :: map()
```

The self-contained schema messages validate against.

# `with_peer_catalogs`
*since 0.1.0* 

```elixir
@spec with_peer_catalogs(t(), [t()]) :: t()
```

A copy of `catalog` that resolves cross-catalog references into
`peers`, after the peers it already has (without duplicates). For a
peer derived from another catalog by `copy_with/2` (a pruned catalog),
the catalog it was derived from comes first, so references to entries
pruned from it still resolve. Returns `catalog` itself when `peers`
adds none.

---

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