# `Image.Plug`
[🔗](https://github.com/elixir-image/image_plug/blob/v0.2.1/lib/image/plug.ex#L1)

The library's request entry point.

Mount this plug under whatever path your host application uses (e.g.
`forward "/img", to: Image.Plug, init_opts: [...]` for
`Plug.Router`, or `plug Image.Plug, [...]` from a Phoenix
endpoint).

### Passthrough

A request whose URL the configured provider does not recognise as an
image request is passed through untouched: the plug returns the
connection un-halted and unsent so the host application's remaining
plugs handle it. This makes `plug Image.Plug` safe to mount at the
root of a Phoenix endpoint ahead of your router — only genuine image
URLs are claimed; everything else flows on. A URL that clearly
intends an image request but is malformed still produces an error
response.

When the plug is mounted as the *sole* plug of a standalone server
(for example a Bandit `plug:` entry with nothing downstream), an
unrecognised URL has nowhere to pass through to; add a fallback
route (e.g. a `Plug.Router` `match _` that returns 404) if you need
to serve non-image paths from the same endpoint.

### Runtime configuration for releases

A Phoenix endpoint calls a plug's `init/1` at *compile time*, so
options passed inline to `plug Image.Plug, ...` — including a
provider's `:mount` path — are frozen into the compiled module and
cannot be read from `config/runtime.exs` or environment variables at
boot. To configure the plug at runtime (the usual case for an Elixir
release), pass `otp_app:`:

    # endpoint.ex
    plug Image.Plug, otp_app: :my_app

    # config/runtime.exs
    config :my_app, Image.Plug,
      provider: {Image.Plug.Provider.Cloudflare, mount: System.fetch_env!("IMAGE_MOUNT")},
      source_resolver: {Image.Plug.SourceResolver.HTTP, []}

In this mode `init/1` stores only the app reference; the full
configuration is read from the application environment and validated
on the first request, then memoized. Inline options given alongside
`otp_app:` act as defaults that the application-environment config
overrides per key. Pass `:key` to override the config key (it
defaults to `Image.Plug`).

Alternatively, without any `Image.Plug`-specific option, Phoenix's own
`config :my_app, MyAppWeb.Endpoint, plug_init_mode: :runtime` makes
the endpoint run every plug's `init/1` at boot, after which inline
`System.get_env/1` calls resolve; note that this applies to the whole
endpoint, not just this plug.

### Request lifecycle

Each request flows through:

1. `provider.parse/2` — produces either a fully-formed pipeline +
   source, a variant lookup + source, or a passthrough source.

2. Variant resolution against the configured
   `Image.Plug.VariantStore`. Variant URLs of the form
   `/<account>/<image-id>/<variant-name>` are expanded to the
   stored pipeline; ad-hoc URLs skip this step.

3. `Image.Plug.Pipeline.Normaliser.normalise/1`.

4. `source_resolver.load/2` — opens the source as a
   `Vix.Vips.Image`, preferring streaming decode.

5. `Image.Plug.Pipeline.Interpreter.execute/2`.

6. `Image.Plug.Pipeline.Encoder.encode/3` — produces a streaming
   body when possible.

7. The plug pipes the body to the client via
   `Plug.Conn.send_chunked/2` + `Plug.Conn.chunk/2`. Buffered bytes
   bodies use `Plug.Conn.send_resp/3`.

# `runtime_config`

```elixir
@type runtime_config() ::
  {:runtime, otp_app :: atom(), key :: atom(), defaults :: keyword()}
```

Deferred configuration produced by `init/1` when the `:otp_app` option
is given. The full options are read from the application environment and
validated on the first `call/2`, so that a release can configure the plug
from `config/runtime.exs`.

# `call`

```elixir
@spec call(Plug.Conn.t(), Image.Plug.Options.t() | runtime_config()) :: Plug.Conn.t()
```

Handles a single request end-to-end.

# `default_telemetry_prefix`

```elixir
@spec default_telemetry_prefix() :: [atom(), ...]
```

Returns the default telemetry prefix used by the request plug.

### Returns

* The list of atoms `[:image_plug]`.

### Examples

    iex> Image.Plug.default_telemetry_prefix()
    [:image_plug]

# `delete_variant`

```elixir
@spec delete_variant(
  String.t(),
  keyword()
) :: :ok | {:error, :not_found}
```

Deletes a variant by name.

### Returns

* `:ok` on success.

* `{:error, :not_found}` if the variant does not exist.

# `get_variant`

```elixir
@spec get_variant(
  String.t(),
  keyword()
) :: {:ok, Image.Plug.Variant.t()} | {:error, :not_found}
```

Fetches a variant by name.

Returns `{:ok, variant}` or `{:error, :not_found}`.

### Examples

    iex> case Image.Plug.get_variant("public") do
    ...>   {:ok, variant} -> variant.name
    ...>   {:error, _} -> nil
    ...> end
    "public"

# `init`

```elixir
@spec init(keyword()) :: Image.Plug.Options.t() | runtime_config()
```

Validates configuration and returns an opaque options struct passed
through to every `call/2` invocation.

Raises `ArgumentError` if required configuration is missing or
malformed. Configuration errors are programmer errors and must surface
at boot time, not per-request.

### Runtime configuration

Pass `otp_app: :my_app` to read the configuration from the application
environment (`Application.get_env(:my_app, Image.Plug)`) on the first
request instead of at compile time. This is what lets an Elixir release
configure the plug — for example a runtime `:mount` path — from
`config/runtime.exs`. Any inline options are used as defaults that the
application-environment config overrides per key. An optional `:key`
overrides the config key (defaulting to `Image.Plug`). In this mode
configuration errors surface on the first request rather than at boot.

# `list_variants`

```elixir
@spec list_variants(keyword()) :: {:ok, [Image.Plug.Variant.t()]}
```

Lists every variant in the store.

### Returns

* `{:ok, [variant]}` — the order is store-defined (the default ETS
  store sorts by name).

# `put_variant`

```elixir
@spec put_variant(Image.Plug.Variant.t() | String.t(), term(), keyword()) ::
  {:ok, Image.Plug.Variant.t()} | {:error, term()}
```

Inserts or updates a variant.

### Arguments

* `name_or_variant` is either a variant name string or a complete
  `Image.Plug.Variant` struct.

* When the first argument is a name, the second argument is either
  a Cloudflare-style options string, an `Image.Plug.Pipeline`
  struct, or a `{provider_module, options_string}` tuple.

### Options

* `:store` — `{module, options}` tuple identifying the store.
  Defaults to `{Image.Plug.VariantStore.ETS, []}`.

* `:metadata` — arbitrary metadata map stored on the variant.

* `:never_require_signed_urls?` — boolean, defaults to `false`.

### Returns

* `{:ok, variant}` on success.

* `{:error, reason}` on failure (e.g. invalid options string).

# `version`

```elixir
@spec version() :: String.t()
```

Returns the library version as a string.

### Returns

* A semver-like version string such as `"0.1.0-dev"`.

### Examples

    iex> is_binary(Image.Plug.version())
    true

---

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