@chichurita/shorn
v0.7.3
Published
Binary serialization for the Zod, Valibot, or ArkType schema you already have
Maintainers
Readme
Installation
npm install @chichurita/shorn zodWorks in Node 20 or newer, Bun, Deno, browsers, and workers.
Encode a value
import { z } from "zod";
import { decode, encode } from "@chichurita/shorn";
const Person = z.object({
name: z.string(),
age: z.int().nonnegative(),
sex: z.enum(["M", "F", "X"]),
});
const person = { name: "Grace", age: 45, sex: "F" } as const;
const bytes = encode(Person, person); // Uint8Array(8)
const back = decode(Person, bytes); // typed and validatedshorn runs your validator before it writes the bytes, and again after it reads them back. The same schema written in Zod, Valibot, or ArkType produces the same bytes.
Where the bytes go
JSON spends most of its bytes on things both sides already know: field names, quotes, brackets, and commas. Your schema carries all of that, so shorn leaves it out and writes only the values. An enum member becomes a small index instead of a string. The picture above shows the result, and how it works walks through the eight bytes one at a time.
Store and queue safely
A bare payload does not say which schema wrote it. If the bytes will sit in a database, a queue, or a file, or cross a deployment boundary, add a fingerprint so that a mismatch is caught instead of decoded into a wrong value:
import { compile, fingerprinted } from "@chichurita/shorn";
const PersonWire = fingerprinted(compile(Person), { bytes: 4 });
const bytes = PersonWire.encode(person); // 4-byte fingerprint + payload
PersonWire.decode(bytes); // rejects a different wire shapeUse another validator
Zod 4.2 or newer and ArkType 2.1.28 or newer work as they are: pass the schema.
Valibot 1.x keeps its JSON Schema conversion in a separate package, so pass
toStandardJsonSchema(schema) as the last argument. Under the hood, shorn
reads validation through Standard Schema
and structure through
Standard JSON Schema.
Valibot's wrapper takes no options, so for Date, bigint, Map and Set
use the raw converter together with valibotOverride:
import { toJsonSchema } from "@valibot/to-json-schema";
import { compile, valibotOverride } from "@chichurita/shorn";
const structure = toJsonSchema(schema, { overrideSchema: valibotOverride(toJsonSchema) });
const codec = compile(schema, structure);Scope
Strings, booleans, integers, floats, literals, enums, nullable values, arrays,
tuples, records, recursive schemas, z.any(), and objects, closed or open, with
optional fields. Unions need a literal tag in every branch, or branches that
share no JSON type. Date, bigint, Map, and Set are supported natively.
Not supported: overlapping unions, streaming, and schema migration.
undefined, symbols, RegExp, and class instances have no wire form, so
convert those first.
Against JSON bytes, encoding is up to 6.7× faster and decoding up to 13.0×,
with no compressor involved. The m API is 6.45 KB gzip; compile with
validation is 11.52 KB.
Documentation
Getting started, API reference, byte layout, supported types, rejected shapes, fingerprinting, performance.
MIT licensed.
