MishkaCmsCore.UiKit.Contract (Mishka CMS Core v0.0.1)

Copy Markdown View Source

What a component in a mishka.ui_kit.bundle.v3 file has to look like before this CMS will install it.

A bundle is a THIRD PARTY's JSON — Chelekom's today, a headless kit's or somebody's own tomorrow — and until this module the only check was that components was a list. Everything past that was trusted, so a component missing its template, or shipping attrs as an object instead of an array, travelled all the way into Ash.create! and raised there. From the installer that is a failed install; from the Studio's Install button it took the editor's LiveView down with it.

The contract is deliberately about SHAPE, not content: names, types, and which fields have to be present for the rest of the pipeline (UiKitCatalog, Inspector, PaletteEntry, the runtime compiler) to mean anything. A kit is free to add fields this CMS has never heard of — UiKitHandler drops those at the Ash boundary — and free to leave optional ones out.

What happens to a component that fails

It is skipped and named, exactly as an unreadable bundle file already is. One malformed component in a kit of 136 should cost that component, not the kit.

Summary

Functions

Checks one component from a bundle.

A sample %MishkaCmsCore.UiKit.Contract{} populated from defaults + type-based fallbacks.

The fields the contract governs, for anyone documenting or testing the boundary.

Splits a bundle's components into the ones this CMS can install and the ones it cannot.

Types

t()

@type t() :: %MishkaCmsCore.UiKit.Contract{
  attrs: list() | nil,
  dependencies: list() | nil,
  extra: map() | nil,
  helpers: list() | nil,
  name: String.t() | nil,
  precompile: boolean() | nil,
  required: boolean() | nil,
  slots: list() | nil,
  stateful: boolean() | nil,
  template: String.t() | nil,
  type: String.t() | nil
}

Functions

builder(attrs_or_input, error \\ false)

check(component)

@spec check(map()) :: :ok | {:error, term()}

Checks one component from a bundle.

Returns the reason as a short atom-or-tuple rather than GuardedStruct's own error shape, because the caller writes it into an installer log line, not a form.

enforce_keys()

enforce_keys(field)

example()

A sample %MishkaCmsCore.UiKit.Contract{} populated from defaults + type-based fallbacks.

fields()

@spec fields() :: [atom()]

The fields the contract governs, for anyone documenting or testing the boundary.

keys()

keys(field)

partition(components)

@spec partition([map()]) :: {[map()], [{String.t() | nil, term()}]}

Splits a bundle's components into the ones this CMS can install and the ones it cannot.

Returns {kept, rejected} where rejected is [{name_or_nil, reason}] — enough for the caller to say which components were skipped and why.