# `Snodo.Subscription.Hub`
[🔗](https://github.com/joshrotenberg/snodo/blob/v0.2.0/lib/snodo/subscription/hub.ex#L1)

An opt-in, application-supervised `Snodo.Subscription.Source`.

A hub broadcasts protocol-neutral `Snodo.Subscription.Event` values to every
listener whose accepted filter selects the event. Each listener has its own
bounded queue, so a client that stops reading cannot make the hub's memory
usage grow without limit. The default overflow policy keeps the newest
information by dropping the oldest queued event; applications may instead
configure `overflow: :drop_newest`.

The hub is application state, not router state. Start it under the
application's supervision tree and pass `source/1` as the
`:subscription_source` option of a server's `runtime/1` or of
`Snodo.Server.Runtime.new/1`:

    children = [{Snodo.Subscription.Hub, name: MyApp.SubscriptionHub}]

    runtime =
      MyServer.runtime(
        subscription_source: Snodo.Subscription.Hub.source(MyApp.SubscriptionHub)
      )

Publishers may send an existing event with `publish/2` or use the core event
helpers such as `notify_tools_list_changed/2`. Extensions publish their own
`Snodo.Subscription.Event.extension/4` values through the same `publish/2`
function; the negotiated extension remains responsible for filter admission
and wire shaping.

# `delivery_report`

```elixir
@type delivery_report() :: %{
  matched: non_neg_integer(),
  delivered: non_neg_integer(),
  buffered: non_neg_integer(),
  dropped: non_neg_integer()
}
```

# `overflow_policy`

```elixir
@type overflow_policy() :: :drop_oldest | :drop_newest
```

# `server`

```elixir
@type server() :: GenServer.server()
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `complete`

```elixir
@spec complete(server()) :: :ok
```

Gracefully completes every listener currently attached to the hub.

# `notify_prompts_list_changed`

```elixir
@spec notify_prompts_list_changed(server(), keyword()) ::
  {:ok, delivery_report()} | {:error, {:invalid_event, String.t()}}
```

Publishes a prompt-list change event.

# `notify_resource_updated`

```elixir
@spec notify_resource_updated(server(), String.t(), keyword()) ::
  {:ok, delivery_report()} | {:error, {:invalid_event, String.t()}}
```

Publishes an update event for one absolute resource URI.

# `notify_resources_list_changed`

```elixir
@spec notify_resources_list_changed(server(), keyword()) ::
  {:ok, delivery_report()} | {:error, {:invalid_event, String.t()}}
```

Publishes a resource-list change event.

# `notify_tools_list_changed`

```elixir
@spec notify_tools_list_changed(server(), keyword()) ::
  {:ok, delivery_report()} | {:error, {:invalid_event, String.t()}}
```

Publishes a tool-list change event.

# `publish`

```elixir
@spec publish(server(), Snodo.Subscription.Event.t()) ::
  {:ok, delivery_report()} | {:error, {:invalid_event, String.t()}}
```

Publishes one protocol-neutral event to every matching listener.

The report distinguishes events delivered directly to a blocked pull from
events placed in a listener queue. `:dropped` counts per-listener overflow,
so one publication can be dropped more than once.

# `source`

```elixir
@spec source(server()) :: {module(), server()}
```

Returns a source configuration for the `:subscription_source` runtime option.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts an application-owned subscription hub.

Options are:

  * `:name` - a standard `GenServer` registered name;
  * `:max_buffer` - the positive per-listener queue bound, defaulting to
    `100`;
  * `:overflow` - either `:drop_oldest` (the default) or `:drop_newest`.
  * `:instrumentation` - an optional `Snodo.Instrumentation` sink.

# `stats`

```elixir
@spec stats(server()) :: %{
  subscriptions: non_neg_integer(),
  closing: non_neg_integer(),
  queued: non_neg_integer(),
  dropped: non_neg_integer(),
  max_buffer: pos_integer(),
  overflow: overflow_policy()
}
```

Returns bounded-queue and listener counts for operational inspection.

---

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