CaravelaSvelte.Caravela (CaravelaSvelte v0.1.0)

Copy Markdown View Source

Integration surface for Caravela-generated apps.

The generators shipped by the caravela framework stitch controllers, LiveViews, and Svelte components together. The helpers in this module exist so generated code can do the glue in one line instead of re-deriving it in every template:

  • put_field_access/2 — attach the entity's field_access map to a conn or LiveView socket so it lands as a prop on the Svelte component under both :rest and :live modes.
  • errors/1 — translate an Ecto.Changeset into the %{field => [msg, ...]} shape consumed by useForm (REST) and useLiveForm (LiveView).
  • broadcast_patch/2 / entity_topic/2 — publish a JSON-Patch on the conventional topic for a Caravela entity
    • actor pair, for SSE real-time pages.

Caravela's generators are expected to call these helpers at well-known points; end-user code rarely touches the module directly, though it's safe to.

Ecto and Phoenix.LiveView are optional dependencies. Their clauses are compiled out when the module is missing.

Summary

Functions

Publish a JSON-Patch on the conventional topic for an entity

Build the conventional SSE topic string for an entity scoped to an actor. Centralised so Caravela's generators and runtime agree on the wire format.

Translate an Ecto.Changeset into the %{field => [msg, ...]} shape both useForm (REST) and useLiveForm (LiveView) consume.

Attach a field_access map to a Plug.Conn or LiveView socket.

Functions

broadcast_patch(entity, actor_id, ops)

@spec broadcast_patch(atom() | String.t(), term() | nil, list()) ::
  :ok | {:error, term()}

Publish a JSON-Patch on the conventional topic for an entity

Pass nil as the actor to broadcast to every subscriber of the entity — useful for admin dashboards.

entity_topic(entity, actor_id \\ nil)

@spec entity_topic(atom() | String.t(), term() | nil) :: String.t()

Build the conventional SSE topic string for an entity scoped to an actor. Centralised so Caravela's generators and runtime agree on the wire format.

iex> CaravelaSvelte.Caravela.entity_topic("Book", 42)
"caravela:book:actor:42"

iex> CaravelaSvelte.Caravela.entity_topic(:book, nil)
"caravela:book"

Entity names are normalised to lowercase. nil actors drop the :actor:<id> suffix for broadcast-to-all patterns.

errors(changeset)

@spec errors(Ecto.Changeset.t()) :: %{optional(atom()) => [String.t()]}

Translate an Ecto.Changeset into the %{field => [msg, ...]} shape both useForm (REST) and useLiveForm (LiveView) consume.

Merges the changeset's :action before traversal so templates that haven't explicitly marked the changeset as validated still surface errors. Interpolates %{count} placeholders from the error opts.

Requires Ecto at runtime; raises otherwise.

put_field_access(conn, field_access)

@spec put_field_access(Plug.Conn.t(), map()) :: Plug.Conn.t()
@spec put_field_access(Phoenix.LiveView.Socket.t(), map()) ::
  Phoenix.LiveView.Socket.t()

Attach a field_access map to a Plug.Conn or LiveView socket.

Works uniformly in :rest and :live modes:

  • In a Phoenix controller (:rest), call before CaravelaSvelte.render/4:

    conn
    |> CaravelaSvelte.Caravela.put_field_access(field_access)
    |> CaravelaSvelte.render("BookIndex", %{books: books, field_access: field_access})
  • In a LiveView (:live), call during mount/3 or handle_params/3:

    {:ok, CaravelaSvelte.Caravela.put_field_access(socket, field_access)}

The function just assigns :field_access so downstream code can read @field_access in templates and pass it as a prop to <CaravelaSvelte.svelte>.