# `Lotus.Middleware`
[🔗](https://github.com/typhoonworks/lotus/blob/v0.16.6/lib/lotus/middleware.ex#L1)

Generic middleware pipeline for query execution and schema discovery hooks.

Each middleware module implements `init/1` and `call/2`, following
the standard Plug pattern:

    defmodule MyApp.AuditPlug do
      def init(opts), do: opts

      def call(payload, opts) do
        {:cont, payload}   # continue to next middleware
        # or
        {:halt, "reason"}  # stop pipeline, Lotus returns {:error, reason}
      end
    end

## Pipeline Events

| Event | Triggered |
|-------|-----------|
| `:before_query` | After preflight visibility check, before SQL execution |
| `:after_query` | After execution, before result returned to caller |
| `:after_list_schemas` | After schema discovery and visibility filtering |
| `:after_list_tables` | After table discovery and visibility filtering |
| `:after_get_table_schema` | After table schema introspection and column visibility |
| `:after_list_relations` | After relation discovery and visibility filtering |

## Configuration

    config :lotus,
      middleware: %{
        before_query: [
          {MyApp.AccessControlPlug, []},
          {MyApp.QueryAuditPlug, [repo: MyApp.AuditRepo]}
        ],
        after_query: [
          {MyApp.QueryAuditPlug, [repo: MyApp.AuditRepo]}
        ],
        after_list_tables: [
          {MyApp.TableFilterPlug, []}
        ]
      }

## Context

A `:context` key carries opaque user data (e.g. the current user) through
to middleware. Lotus never inspects this value.

# `compiled_entry`

```elixir
@type compiled_entry() :: {module(), term()}
```

# `event`

```elixir
@type event() ::
  :before_query
  | :after_query
  | :after_list_schemas
  | :after_list_tables
  | :after_get_table_schema
  | :after_list_relations
```

# `middleware_spec`

```elixir
@type middleware_spec() :: {module(), keyword()}
```

# `pipeline_result`

```elixir
@type pipeline_result() :: {:cont, map()} | {:halt, term()}
```

# `compile`

```elixir
@spec compile(map()) :: :ok
```

Compiles all middleware by calling `init/1` on each module and stores
the result in `:persistent_term` for fast runtime access.

# `run`

```elixir
@spec run(event(), map()) :: {:cont, map()} | {:halt, term()}
```

Runs the middleware pipeline for the given event.

Returns `{:cont, payload}` if all middleware passed, or
`{:halt, reason}` if any middleware halted the pipeline.

When no middleware is configured for the event, returns `{:cont, payload}`
with zero overhead beyond the map lookup.

---

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