# `DrinkMeAlice.FrameGuard`

Bounded WebSocket framing guard — production adaptation of the accepted
BF-02A `SurrealTransport.FrameGuard`.

Mint.WebSocket 1.0.6 has no bound on `buffer` (raw bytes of an
incomplete frame) or `fragment` (reassembled message payload). This pure
state machine walks raw socket bytes *before* they reach
`Mint.WebSocket.decode/2`:

* parses each frame header itself (14-byte maximum, held in
  `header_acc`) and rejects a declared payload length above
  `:max_message` before Mint can start buffering the body;
* accumulates the payload total of a fragmented message (data frame +
  continuations) and rejects when the aggregate exceeds `:max_message`
  — the 1 MiB contract message bound, enforced before unbounded decode;
* enforces the RFC 6455 control-frame rules Mint leaves unchecked
  (payload <= 125 bytes, never fragmented);
* counts *payload* bytes only — a message of exactly `:max_message`
  payload bytes is accepted; frame headers never consume the allowance.

The guard retains at most a partial 14-byte header between `feed/2`
calls; everything else is streamed through position arithmetic, not
copies.

# `t`

```elixir
@type t() :: %DrinkMeAlice.FrameGuard{
  header_acc: term(),
  max_message: term(),
  msg_bytes: term(),
  msg_open: term(),
  skip: term()
}
```

# `feed`

```elixir
@spec feed(t(), binary()) :: {:ok, t()} | {:error, term(), t()}
```

Validate a chunk of raw inbound bytes. Returns `{:ok, guard}` or
`{:error, reason, guard}`; on error the caller must close the transport.

# `message_bytes`

Payload bytes accumulated for the open fragmented message.

# `new`

```elixir
@spec new(keyword()) :: t()
```

---

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