README.md

exact-mirror-zod

AOT compiler for Zod 4 schemas that generates fast property-stripping functions. Port of exact-mirror from TypeBox to Zod 4.

Install

bash
1bun add exact-mirror-zod

Requires Zod 4.

Usage

typescript
1import { z } from "zod";
2import { createMirror } from "exact-mirror-zod";
3
4const schema = z.object({ name: z.string(), age: z.number() });
5const mirror = createMirror(schema);
6
7mirror({ name: "test", age: 25, password: "leaked" });
8// => { name: "test", age: 25 }

createMirror(schema) compiles a Zod schema into a function that strips unknown properties from objects to match the schema's shape. It does not validate -- it assumes input is already valid and just projects known keys.

Why?

Validate on input (z.parse()), strip on output (createMirror()). Prevents leaking internal fields from API responses without the overhead of full re-parsing. Typically 5-15x faster than z.parse() for stripping.

Supported Types

TypeBehavior
ObjectKeeps only declared keys
ArrayPer-element mirroring (passes through primitive arrays)
TuplePer-index mirroring
RecordObject.keys iteration with value mirroring
Discriminated Unionswitch on discriminator key
UnionCheap typeof / Array.isArray structural checks
IntersectionMerges object shapes
Recursive (z.lazy)Memoized helpers with cycle detection
Wrappersoptional, nullable, default, readonly, branded, pipe -- unwraps and recurses

Options

typescript
1createMirror(schema, {
2 // Sanitization functions applied to every string value
3 sanitize: (v) => v.trim(),
4
5 // Max recursion depth for z.lazy() schemas (default: 8)
6 recursionLimit: 8,
7
8 // Remove values that don't match any union variant (default: false)
9 removeUnknownUnionType: false,
10});

String Sanitization

Pass one or more functions to transform every string value in the output:

typescript
1const mirror = createMirror(schema, {
2 sanitize: [(v) => v.trim(), (v) => v.replace(/</g, "&lt;")],
3});

Development

bash
1bun test # run tests
2bun run bench # run benchmarks
3bun run bench:small # benchmark small schemas
4bun run bench:compare # compare against z.parse()
5bun run typecheck # type check
6bun run fix # lint + format

License

MIT