first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
# schemas/messages
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
This module declares the Zod boundary schemas for
`@suaveplan/broadcast-channel`. `ChannelMessageSchema` validates every inbound
frame produced by the underlying `ChannelLike` transport before it is allowed
into the per-type handler registry. `ChannelConfigSchema` validates the
construction-time config object so consumers see a meaningful Zod issue list
instead of an opaque runtime failure when a field is missing or non-numeric.
Boundary validation is the suaveplan-mandated fix for the enterprise gap "No
Zod validation on incoming `ChannelMessage` — a rogue tab could send
garbage." A peer that posts a frame missing `id` or with a negative
`timestamp` no longer reaches user code; the channel drops the frame,
increments `channel.message.dropped`, and warns through the injected
`LoggerLike`.
## Features
- Two schemas: `ChannelMessageSchema`, `ChannelConfigSchema`.
- Default-import Zod (`import z from "zod"`) to satisfy the suaveplan Bun-safe rule.
- `payload` is intentionally `z.unknown()` so callers can layer their own per-type validation downstream.
- Stable required-field set: `{ type, payload, id, senderId, timestamp }`.
- Inferred input types via `z.infer`.
## Usage: Basic
```ts
import { ChannelMessageSchema } from "@suaveplan/broadcast-channel";
const parsed = ChannelMessageSchema.safeParse(incoming);
if (!parsed.success) {
return; // drop
}
```
## Usage: Advanced
```ts
import z from "zod";
import { ChannelMessageSchema } from "@suaveplan/broadcast-channel";
// Layer a per-type payload validator over the boundary schema.
const UserSignedInPayload = z.object({ userId: z.string().min(1) });
const incoming: unknown = receiveOffWire();
const env = ChannelMessageSchema.safeParse(incoming);
if (!env.success) return;
if (env.data.type === "user:signed-in") {
const payload = UserSignedInPayload.safeParse(env.data.payload);
if (!payload.success) return;
// payload.data.userId is typed and validated
}
```
## API Reference
- `const ChannelMessageSchema` — `z.object({ type: z.string().min(1), payload: z.unknown(), id: z.string().min(1), senderId: z.string().min(1), timestamp: z.number().int().nonnegative() })`.
- `const ChannelConfigSchema` — `z.object({ name: z.string().min(1), heartbeatInterval: z.number().int().positive(), tabTimeout: z.number().int().positive(), maxMessageBytes: z.number().int().positive() })`.
- `type ChannelMessageInput = z.infer<typeof ChannelMessageSchema>`.
- `type ChannelConfigInput = z.infer<typeof ChannelConfigSchema>`.
## Implementation Notes
`payload` is left as `z.unknown()` because the channel is type-agnostic on
the wire: per-type validation belongs in the application layer, not in the
transport boundary. Forcing a discriminated union here would push every
caller into either casting or extending the schema, and would force a major
version bump every time an application added a new message type — exactly the
opposite of the loose-coupling story the channel is meant to provide. The
`.min(1)` constraints on `type` / `id` / `senderId` catch the realistic
failure mode (empty strings produced by a malformed encoder) without the
cost of regex matching. `timestamp` uses `.int().nonnegative()` so frames
produced by clocks at zero (a fresh `FakeClock`) still validate while frames
with garbage negative values are rejected.