first commit
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# wire.schemas
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Defines the Zod schemas that pin the **wire-format invariants** of the serialize codecs. Both the JSON codec and the MessagePack codec produce structurally identical intermediate values: a recursive tree of JSON-compatible primitives, arrays, plain records, and tagged envelopes (`{ "__t": "<tag>", "v": <encoded> }`). This module defends the public `deserialize` and `decodeBinary` boundaries against accidental or malicious inputs that would otherwise reach the type registry's `decode` functions in an unexpected shape — for example a number where an envelope is expected, or an envelope with `__t` set to a non-string value.
|
||||
|
||||
The schemas are exported as both reusable values (`WireNodeSchema`, `TaggedEnvelopeSchema`) and an ergonomic helper (`validateWireNode`) that wraps `safeParse` and exposes the parse outcome as a discriminated union so call sites can wrap rejection in a `MalformedWireError` with stable code metadata.
|
||||
|
||||
## Features
|
||||
|
||||
- Recursive `WireNodeSchema` covering primitives, arrays, tagged envelopes, and plain records
|
||||
- Strict `TaggedEnvelopeSchema` that rejects extra keys
|
||||
- `validateWireNode` helper returning `{ ok: true; value } | { ok: false; error }`
|
||||
- Used by `deserialize` (JSON) and `decodeBinary` (MessagePack) at the public boundary
|
||||
- Pure, side-effect free; tree-shakes when unused
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { validateWireNode } from "@suaveplan/serialize";
|
||||
|
||||
const result = validateWireNode({ __t: "__Date", v: "2024-01-01T00:00:00.000Z" });
|
||||
if (result.ok) {
|
||||
// safe to feed to the type-registry decode pipeline
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { MalformedWireError, validateWireNode } from "@suaveplan/serialize";
|
||||
|
||||
function strictDeserialize(input: unknown): unknown {
|
||||
const wire = validateWireNode(input);
|
||||
if (!wire.ok) {
|
||||
throw new MalformedWireError(
|
||||
"input does not match the tagged-wire schema",
|
||||
"root",
|
||||
wire.error
|
||||
);
|
||||
}
|
||||
return wire.value;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `WireNodeSchema: z.ZodType<unknown>` — recursive schema for any wire-format node.
|
||||
- `TaggedEnvelopeSchema: z.ZodObject<{ __t: ZodString, v: ZodUnknown }>` — strict envelope schema.
|
||||
- `validateWireNode(value: unknown): { ok: true; value: unknown } | { ok: false; error: ZodError }` — non-throwing validator.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`WireNodeSchema` is defined via `z.lazy()` to support recursion through arrays and records. The envelope schema uses `.strict()` so an envelope with a smuggled-in third key is rejected — this matches the `serialize`/`encodeBinary` output, which only ever emits `__t` + `v`. We intentionally exclude `BigInt`, `Map`, `Set`, and other JS-only constructs from the schema: the wire format never produces them at this layer (BigInts arrive as a `__BigInt` envelope with a string `v`), so admitting them would weaken the invariant.
|
||||
Reference in New Issue
Block a user