@fotros/schema v2.4 — Recursive Types and Performance Improvements
This release brings recursive type support, a 40% parse performance improvement for large payloads, and a new strict mode that rejects unknown keys by default.
We're thrilled to announce @fotros/schema v2.4.0, our biggest release of the year. This release focuses on three pillars: recursive type support, parse performance, and a tighter strict mode API.
Recursive Type Support
Until now, defining recursive schemas — like a tree node that references itself — required awkward workarounds using lazy evaluation or manual type casting. Starting in v2.4, you can define recursive schemas natively using the new ref() primitive.
import { createParser, ref } from '@fotros/schema';const TreeNode = createParser({value: String,
children: ref(() => [TreeNode]).optional(),
});
// Fully typed — TypeScript infers the recursive structure
type TreeNode = typeof TreeNode._output;
The ref() call wraps the self-referencing schema in a lazy thunk, which is resolved at parse time rather than at definition time. This avoids the circular reference problem that previously caused runtime errors.
40% Parse Performance Improvement
We profiled the hot path for large object validation and found that string key lookups in the schema registry were the dominant cost. We switched from a Map-based lookup to a flat array with binary search for small schemas (≤16 keys) and a pre-hashed lookup table for larger schemas.
"The new parse engine processes 2M objects/sec on a standard M3 MacBook Pro, up from 1.4M in v2.3."
— Internal benchmark, fotros-schema-bench@0.2.0
These changes are entirely backwards-compatible — your existing schema definitions don't need to change. The performance gains apply automatically.
Strict Mode is Now the Default
In previous versions, strict mode (rejecting unknown keys) had to be opted into explicitly. After user feedback showing that most production applications want this behavior, we've flipped the default.
// Before v2.4 — strict was opt-in
const schema = createParser({ name: String }, { strict: true });// v2.4+ — strict is the default, opt out if needed
const schema = createParser({ name: String });const lenient = createParser({ name: String }, { strict: false });Migration Guide
If your codebase relies on schemas passing through unknown keys silently, you will need to add { strict: false } to those specific parsers. The upgrade is otherwise drop-in. Run the provided codemod to scan your project:
npm exec @fotros/codemod -- schema-strict-migration ./src
Full Changelog
- feat: recursive schema support via ref()
- perf: 40% parse throughput improvement on large objects
- breaking: strict mode is now enabled by default
- feat: new .describe() method for attaching metadata to fields
- fix: union types now correctly narrow in TypeScript 5.4+
- fix: date coercion handles ISO 8601 strings with timezone offsets
- docs: new cookbook section on recursive data structures