How It Works
value ──▶ validate ──▶ wire plan ──▶ bytes (Standard (Standard Schema) JSON Schema)- Your validator checks the value through Standard Schema, including rules such as
.min(1),.email(), and.refine(). - Standard JSON Schema supplies structure: field names, types, optionality. shorn converts it to a wire plan once and caches it.
- The plan writes values with no keys and no type tags.
- Decode runs in reverse, and validates again with your original library.
A payload that decodes structurally but fails a refinement is a DecodeError, never an accepted value.
Two interfaces, two jobs
Section titled “Two interfaces, two jobs”| Interface | Supplies | Used for |
|---|---|---|
| Standard Schema | validate(value) | correctness, both directions |
| Standard JSON Schema | jsonSchema.input() / .output() | structure |
Both interfaces are vendor-neutral, so shorn does not need validator-specific code. This also creates its main limitation: shorn cannot encode anything JSON Schema cannot describe. See Date, BigInt, Map, Set.
The wire plan
Section titled “The wire plan”The JSON Schema becomes a WireShape — a small closed union:
boolean | float64 | int | string | uint| { array } | { tuple } | { object, rejectUnknown }| { enum } | { literal } | { nullable }Two details drive the choices that matter:
type: "integer"withminimum >= 0becomesuint(plain varint); without it,int(ZigZag), which crosses every size boundary at half the value.additionalPropertiesdetermines how extra fields are handled.falsemeans the validator handles them. If the option is absent, shorn refuses extras during encoding.trueor a schema makes the object open, which shorn does not support.
Both jsonSchema.input() and .output() are converted and compared; a schema whose two sides differ needs a bidirectional codec and is refused.
Compiled, then signed
Section titled “Compiled, then signed”The WireShape becomes a tree of Schema objects — the same objects the m API builds by hand, which is why the two produce identical bytes.
Each node carries a _minWidth: the fewest bytes any value of that shape can occupy. That is what lets an array refuse an impossible element count before allocating. See Hostile Input.
shorn also stores a canonical string signature: the WireShape as JSON without rejectUnknown. fingerprinted() hashes this signature. Removing rejectUnknown lets equivalent Zod and ArkType schemas agree even though they handle extra properties differently. The m API has no signature, so fingerprinted() refuses codecs built with m.
Caching
Section titled “Caching”encode and decode cache the plan in a WeakMap keyed by schema identity, so conversion runs once per schema. See Compilation and Caching.