first commit
This commit is contained in:
78
docs/api-stack/api/api-ws/src/envelope/envelope.md
Normal file
78
docs/api-stack/api/api-ws/src/envelope/envelope.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# `envelope` — wire-frame Zod schema
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`@suaveplan/api-ws/envelope` owns the canonical Zod schema for every
|
||||
WebSocket frame that flows between the host and a plugin. Engines parse
|
||||
incoming frames against this schema before the message reaches plugin code,
|
||||
so unknown top-level keys never leak past the host boundary and every frame
|
||||
is guaranteed to carry the correlation id and discriminator the
|
||||
subscription bus, ack pipeline, and reconnect-resume path require.
|
||||
|
||||
The shape is deliberately minimal — `id`, `type`, optional `topic`,
|
||||
optional `payload`, optional `ts` — so plugins can compose their own Zod
|
||||
schemas on top of `payload` without fighting the wire format.
|
||||
|
||||
## Features
|
||||
|
||||
- Strict object schema — extra keys are a parse failure, not a silent drop.
|
||||
- `isWsEnvelope(value): value is WsEnvelope` predicate for ergonomic
|
||||
narrowing inside engine adapters.
|
||||
- `parseWsEnvelope(value)` throws the underlying `ZodError` for callers
|
||||
that want to convert to a `WsError` at the boundary.
|
||||
- Always uses the default-import form `import z from "zod"` per the
|
||||
project Zod policy.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { isWsEnvelope, parseWsEnvelope } from "@suaveplan/api-ws/envelope";
|
||||
|
||||
const frame = parseWsEnvelope(JSON.parse(rawText));
|
||||
console.log(frame.id, frame.type);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { isWsEnvelope, type WsEnvelope } from "@suaveplan/api-ws/envelope";
|
||||
import { WS_CODES, WsError } from "@suaveplan/api-ws/error-codes";
|
||||
import z from "zod";
|
||||
|
||||
const orderPayload = z.object({ orderId: z.string() });
|
||||
|
||||
function handle(raw: unknown): void {
|
||||
if (!isWsEnvelope(raw)) {
|
||||
throw new WsError(WS_CODES.HANDSHAKE_FAILED, "invalid envelope");
|
||||
}
|
||||
const envelope: WsEnvelope = raw;
|
||||
if (envelope.type === "orders.ack") {
|
||||
const payload = orderPayload.parse(envelope.payload);
|
||||
// narrow + branch...
|
||||
void payload;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `wsEnvelopeSchema: z.ZodSchema<WsEnvelope>` — strict object schema.
|
||||
- `type WsEnvelope = z.infer<typeof wsEnvelopeSchema>`.
|
||||
- `isWsEnvelope(value: unknown): value is WsEnvelope` — type predicate.
|
||||
- `parseWsEnvelope(value: unknown): WsEnvelope` — throwing parser.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `strict()` mode is non-negotiable. Engines that need to allow custom
|
||||
extensions MUST do so by defining their own discriminated union of
|
||||
`wsEnvelopeSchema.extend({ ... })` shapes per `type`, not by relaxing the
|
||||
base schema.
|
||||
- `payload` is `z.unknown()` rather than `z.any()` so downstream code is
|
||||
forced to narrow before use.
|
||||
- The Zod default-import form (`import z from "zod"`) is the only
|
||||
Bun-safe form per the project policy and is enforced by a repo-wide
|
||||
grep test.
|
||||
Reference in New Issue
Block a user