Skip to main content

lib/bloccs/web/resolver.ex

defmodule Bloccs.Web.Resolver do
  @moduledoc """
  The integration seam between the dashboard and the host application: who is
  looking, what they may see, and which features are enabled.

  A host provides a module implementing this behaviour and passes it to
  `bloccs_dashboard/2` via `:resolver`. Every callback has a default (see
  `Bloccs.Web.Access`), so the simplest mount needs no resolver at all.

  ## The Pro seam

  `c:resolve_features/1` is how the open-core / Pro split is expressed without
  any LiveView change: the free build returns every feature, a future licensed
  build returns a subset, and panels gate themselves with
  `Bloccs.Web.Access.enabled?/2`. The dashboard ships **only the seam** — no
  license logic.
  """

  @typedoc "An opaque user term resolved from the connection (whatever the host uses)."
  @type user :: term()

  @typedoc """
  A coarse access level for the session. The dashboard is observe-only, so the
  meaningful distinction is full access vs. forbidden.
  """
  @type access :: :all | {:forbidden, reason :: term()}

  @typedoc "A feature flag the dashboard consults before rendering gated UI."
  @type feature :: atom()

  @doc "Resolve the current user from the Plug session map. Defaults to `nil`."
  @callback resolve_user(session :: map()) :: user()

  @doc "Resolve the access level for a user. Defaults to `:all`."
  @callback resolve_access(user()) :: access()

  @doc """
  Resolve the set of enabled features for a user. Defaults to `:all` (every
  feature on — the free baseline). A licensed build returns an explicit list.
  """
  @callback resolve_features(user()) :: :all | [feature()]

  @optional_callbacks resolve_user: 1, resolve_access: 1, resolve_features: 1
end