# `Mob.Event`
[🔗](https://github.com/genericjam/mob/blob/master/lib/mob/event.ex#L1)

The unified event emission API for Mob.

See `guides/event_model.md` for the full event model.

Two responsibilities:

1. **Emit** — given an `%Address{}`, an event atom, and a payload, deliver
   the canonical envelope `{:mob_event, addr, event, payload}` to the right
   pid (resolving in-tree or external targets).

2. **Match** — small helpers (`is_event?/1`, `match_address?/2`) for handler
   code that wants to filter incoming events by address fields.

This module is the **single doorway** between native event sources and
user-level handler code. Every emitter (taps, gestures, lifecycle, custom
components) eventually calls `Mob.Event.emit/4` (or `dispatch/4`, for
pre-resolved pids).

# `envelope`

```elixir
@type envelope() :: {:mob_event, Mob.Event.Address.t(), atom(), term()}
```

The shape of every event message delivered to a handler.

# `dispatch`

```elixir
@spec dispatch(pid(), Mob.Event.Address.t(), atom(), term()) :: :ok
```

Send the event to an already-resolved pid.

Used when the renderer pre-resolved the target at registration time and
passes the pid directly. Skips re-resolution.

Also broadcasts to any `Mob.Event.Trace` subscribers (zero cost when no
tracers are registered).

# `emit`

```elixir
@spec emit(
  Mob.Event.Address.t(),
  atom(),
  term(),
  Mob.Event.Target.spec(),
  Mob.Event.Target.render_scope()
) :: :ok
```

Resolve `target` and deliver the event.

Best-effort delivery: if the target can't be resolved (dead pid, unknown
registered name, component not in the ancestor chain), logs a debug
message and drops the event.

Returns `:ok` always. Errors from resolution are logged, not raised — losing
one event from a stale handle should never crash the BEAM.

# `is_event?`

```elixir
@spec is_event?(term()) :: boolean()
```

True if `msg` matches the canonical event envelope shape.

    iex> Mob.Event.is_event?({:mob_event, %Mob.Event.Address{screen: S, widget: :x, id: :y}, :tap, nil})
    true

    iex> Mob.Event.is_event?({:tap, :something})
    false

# `match_address?`

```elixir
@spec match_address?(
  Mob.Event.Address.t(),
  keyword()
) :: boolean()
```

True if `addr` matches all the given filters.

Filters is a keyword list of address fields and the values they must equal.
Useful for one-off matches in `handle_info/2` clauses where you don't want
to write a full struct pattern.

    iex> addr = %Mob.Event.Address{screen: S, widget: :button, id: :save}
    iex> Mob.Event.match_address?(addr, widget: :button)
    true

    iex> addr = %Mob.Event.Address{screen: S, widget: :button, id: :save}
    iex> Mob.Event.match_address?(addr, widget: :button, id: :cancel)
    false

# `send_test`

```elixir
@spec send_test(
  pid(),
  atom() | pid(),
  atom(),
  Mob.Event.Address.id(),
  atom(),
  term(),
  keyword()
) :: :ok
```

Synthesize an event delivery to `pid`. Useful for tests — bypasses the
native side entirely.

    Mob.Event.send_test(self(), MyScreen, :button, :save, :tap, nil)
    assert_receive {:mob_event, %Address{id: :save}, :tap, nil}

---

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