# `CDPEx.Connection`
[🔗](https://github.com/patrols/cdp_ex/blob/v0.9.0/lib/cdp_ex/connection.ex#L1)

A GenServer owning a single CDP WebSocket connection.

This is the heart of `cdp_ex`'s OTP model. It connects and upgrades a
`Mint.WebSocket` in `init/1`, then runs in `:active` mode so every inbound
frame arrives as a process message in `handle_info/2`. The GenServer:

  * matches command **replies** back to the caller by JSON-RPC `id`
    (concurrent callers are fine — each `call/4` blocks only its own caller),
  * routes unsolicited **events** to subscribers and one-shot waiters,
  * answers WebSocket **pings** with pongs,
  * fails every pending caller with `{:error, {:ws_closed, reason}}` and stops
    if the socket drops — no caller is left hanging.

One connection backs one socket: the browser endpoint, or a page endpoint at
`/devtools/page/<targetId>`. A single connection may carry both untagged
browser/page frames and many flattened sessions' frames, demultiplexed by
`sessionId`. `CDPEx.Browser` starts and monitors these.

# `call_error`

```elixir
@type call_error() ::
  {:cdp_error, String.t(), term()}
  | {:timeout, String.t()}
  | {:ws_closed, term()}
  | :noproc
```

Error reasons from `call/5` — and from every `Network`/`Page` op layered on it.
Precisely specced (not `term()`) so Dialyzer flags drift at the source.

# `t`

```elixir
@type t() :: %CDPEx.Connection{
  all_subscribers: term(),
  conn: term(),
  monitors: term(),
  next_id: term(),
  pending: term(),
  ref: term(),
  subscribers: term(),
  waiters: term(),
  websocket: term(),
  ws_send_error: term()
}
```

# `await_event`

```elixir
@spec await_event(GenServer.server(), (map() -&gt; boolean()), timeout(), keyword()) ::
  {:ok, map()}
  | {:error, {:timeout, :await_event} | :noproc | {:ws_closed, term()}}
```

Blocks until an event for which `matcher.(params)` returns true, or `timeout`.

`matcher` receives the event params map. Returns `{:ok, params}` (the matched
event's params) on a match, or `{:error, reason}` where reason is
`{:timeout, :await_event}` (no matching event in time) or `:noproc` /
`{:ws_closed, _}` (the connection itself went away) — callers must be able to
tell those apart.

Pass `opts` with `session_id: sid` to only match events from that session.

Keep `matcher` fast and side-effect-free: it runs inside the connection process
for each candidate event, so a slow or blocking matcher stalls the socket — and,
on a `:session`-transport connection, every page sharing it.

# `call`

```elixir
@spec call(GenServer.server(), String.t(), map(), timeout(), keyword()) ::
  {:ok, map()} | {:error, call_error()}
```

Sends a CDP command and blocks until its reply (or `timeout`).

Returns `{:ok, result}`, `{:error, {:cdp_error, method, error}}` on a protocol
error, `{:error, {:timeout, method}}`, or `{:error, {:ws_closed, reason}}` /
`{:error, :noproc}` if the connection drops or is already gone.

Pass `opts` with `session_id: sid` to address a flattened CDP session.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `close`

```elixir
@spec close(GenServer.server()) :: :ok
```

Closes the WebSocket and stops the connection.

# `start_link`

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

Starts a connection to the given `ws://host:port/path` URL.

Options: `:upgrade_timeout` (ms, default 15_000) and `:name` (registers the
GenServer). Returns `{:ok, pid}` once the WebSocket handshake completes.

# `subscribe`

```elixir
@spec subscribe(GenServer.server(), String.t() | :all, timeout(), keyword()) :: :ok
```

Subscribes the calling process to a CDP event method (e.g.
`"Page.lifecycleEvent"`) or to `:all` events. Delivered as
`{:cdp_event, conn_pid, method, params, session_id}` — `session_id` is `nil`
for browser/page-level events.

The subscription is removed automatically if the subscribing process exits, so
a crashed subscriber can't accumulate in the connection.

`timeout` bounds the registration call so a caller can fold it into an overall
deadline (defaults to the standard call timeout).

Pass `opts` with `session_id: sid` to receive only events from that session —
the matching that `await_event/4` already does for waiters. The default (`nil`)
receives every session's events (current behaviour); on a `:dedicated`
connection, where every event is for the one page, the two are equivalent.

# `unsubscribe`

```elixir
@spec unsubscribe(GenServer.server(), String.t() | :all) :: :ok
```

Removes a subscription created with `subscribe/4`.

---

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