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

Raw `raw.githubusercontent.com` client for adaptor icons.

Icons are not published inside npm tarballs. They live in the
`OpenFn/adaptors` monorepo, so this module fetches them from the raw
GitHub content host, one icon per GET.

## URL pattern

    <github_url>/OpenFn/adaptors/<github_ref>/packages/<name-suffix>/assets/<shape>.<ext>

where `<name-suffix>` strips the `@openfn/` scope and, when present, the
`language-` prefix too, so `@openfn/language-common` becomes `common`,
matching the monorepo's `packages/` directory names. Each `(name,
shape)` is probed `png` first then `svg`, the same order
`Lightning.Adaptors.Local` uses.

## Configuration

`:github_url` (default `https://raw.githubusercontent.com`) and
`:github_ref` (default `main`) come from
`Lightning.Adaptors.Config.strategy_opts(Lightning.Adaptors.NPM)`.

# `fetch_all`

```elixir
@spec fetch_all([String.t()], %{
  optional(String.t()) =&gt; %{
    optional(:square) =&gt; String.t(),
    optional(:rectangle) =&gt; String.t()
  }
}) ::
  {:ok,
   %{
     required(String.t()) =&gt; %{
       optional(:square) =&gt;
         %{
           data: binary(),
           ext: String.t(),
           sha256: binary(),
           etag: String.t() | nil
         }
         | :not_modified,
       optional(:rectangle) =&gt;
         %{
           data: binary(),
           ext: String.t(),
           sha256: binary(),
           etag: String.t() | nil
         }
         | :not_modified
     }
   }}
```

Fetch icons for every `(name, shape)` pair across `names`.

Returns `{:ok, map}` keyed by package name, each holding zero, one or
two shape keys. A missing shape is not an error. It means upstream had
no such icon, or the fetch for that pair failed, which is only logged.

`prior_etags` is `%{name => %{shape => etag}}`. Each etag is sent as
`If-None-Match` for its `(name, shape)`, and a 304 comes back as
`:not_modified` in that shape's slot.

# `fetch_one`

```elixir
@spec fetch_one(String.t(), :square | :rectangle) ::
  {:ok, %{data: binary(), ext: String.t(), etag: String.t() | nil}}
  | {:error, :not_found | term()}
```

Fetch a single icon for `(name, shape)`.

Tries `png` then `svg`. No conditional GET, since the Store calls this
when an icon is missing on disk and has no prior etag. Returns:

  * `{:ok, %{data: binary(), ext: String.t(), etag: String.t() | nil}}`
    on success.
  * `{:error, :not_found}` when neither ext yields a 200.
  * `{:error, term()}` on transport-level failure (timeout, nxdomain).

---

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