# `Lightning.Adaptors.Store`
[🔗](https://github.com/OpenFn/lightning/blob/main/lib/lightning/adaptors/store.ex#L1)

Cached reads over `Lightning.Adaptors.Catalogue`.

Every read checks the instance's Cachex first and falls back to the
catalogue table. Reads never fill the catalogue, so a row with no
schema means the source has none and `schema/2` answers `"{}"`, while
an unknown name returns `{:error, :not_found}`. `icon/3` returns a path on disk,
fetching the bytes from the strategy on the first miss. `catalogue/1`
caches the picker payload already rendered, together with the ETag
stamp that describes it.

## The first load

Reads never wait for the first load. A read that comes back empty or
not-found asks whether the catalogue has ever loaded, and answers
`{:error, :not_ready}` when it has not. An empty answer from a catalogue
that has loaded is a real answer and is returned as-is. That includes the
empty catalogue a source with no adaptors leaves behind, which the
Scheduler reports as loaded despite there being no row to find.

Waiting for the first load is opt-in, through `ensure_loaded/2`.

# `catalogue`

```elixir
@type catalogue() :: {{DateTime.t() | nil, non_neg_integer()}, [rendered_entry()]}
```

# `icon_meta`

```elixir
@type icon_meta() :: %{
  icon_square_ext: String.t() | nil,
  icon_rectangle_ext: String.t() | nil,
  icon_square_sha256: binary() | nil,
  icon_rectangle_sha256: binary() | nil
}
```

# `package_meta`

```elixir
@type package_meta() :: Lightning.Adaptors.Catalogue.package_meta()
```

# `rendered_entry`

```elixir
@type rendered_entry() :: %{
  name: String.t(),
  latest_version: String.t(),
  versions: [String.t()],
  repository: String.t() | nil,
  icon_urls: %{square: String.t() | nil, rectangle: String.t() | nil}
}
```

One `t:Lightning.Adaptors.Catalogue.catalogue_entry/0` with its icon
fields rendered to URLs, as the catalogue endpoint serves it.

# `sup`

```elixir
@type sup() :: atom()
```

# `catalogue`

```elixir
@spec catalogue(sup()) :: {:ok, catalogue()} | {:error, term()}
```

Returns the picker catalogue for the active source as
`{stamp, rendered_entries}`.

The ETag stamp and the payload it describes are cached as one entry so
a 304 can be answered without re-reading the projection, and so the two
can never drift apart.

# `ensure_loaded`

```elixir
@spec ensure_loaded(sup(), keyword()) ::
  :ok | {:error, :timeout | :unavailable | :not_ready}
```

Waits until the catalogue has loaded at least once, triggering the first
load if needed. `:timeout` bounds the wait, defaulting to
`Lightning.Adaptors.Config.first_load_timeout/0`.

Returns `:ok`, `{:error, :timeout}` if the load did not finish in time,
`{:error, :unavailable}` if no Scheduler is reachable, or
`{:error, :not_ready}` if the load ran and could neither write a row nor
report a complete cycle.

# `icon`

```elixir
@spec icon(sup(), String.t(), :square | :rectangle) ::
  {:ok, Path.t()} | {:error, :not_found | term()}
```

Returns the on-disk path of one icon shape for the adaptor, or
`{:error, :not_found}` when the adaptor row has no such icon.

# `icon_meta`

```elixir
@spec icon_meta(sup(), String.t()) ::
  {:ok, icon_meta()} | {:error, :not_found | :not_ready}
```

Returns the extension and sha256 of each icon shape for the adaptor,
without touching disk, or `{:error, :not_found}` for an unknown name.

# `packages`

```elixir
@spec packages(sup()) :: {:ok, [package_meta()]} | {:error, term()}
```

Returns every adaptor for the active source, without the `schema_data`,
`dependencies` and `peer_dependencies` columns.

# `schema`

```elixir
@spec schema(sup(), String.t()) :: {:ok, String.t()} | {:error, term()}
```

Returns the adaptor's credential schema as a JSON binary, not decoded.
An adaptor with no schema yields `"{}"`; an unknown name
`{:error, :not_found}`.

# `warm_from_repo`

```elixir
@spec warm_from_repo(sup()) :: :ok
```

Overwrites the cached package list, icon metadata and catalogue from the
database. An empty catalogue is left uncached, as `catalogue/1` does.

---

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