This package contains the code for compiling highly optimized schemas.
This technique is sometimes referred to as "jit compiling", or generating "jit-compiled" schemas, although the name is a bit of a misnomer since we're actually generating these ahead of time. /p>
This package contains the code for installing "JIT compiled" validators to your schemas.
Users can consume this package in one of several ways:
To install the .compile
method on all schemas, simply import @traversable/schema-compiler/install
.
Once you do, all schemas come with a .compile
method you can use.
To compile a single schema, import Jit
from @traversable/schema-jit-compiler/recursive
, and pass the
schema you'd like to compile to Jit.fromSchema
.
Depending on your use case, you'll probably want to use the @traversable/schema-compiler
package in 1 of 2 ways:
This is definitely the easier of the two options.
#### Example
```typescript
// app.ts
import { t } from '@traversable/schema'
import { Compiler } from '@traversable/schema-compiler'
const UserSchema = t.object({
firstName: t.string,
lastName: t.optional(t.string),
address: t.optional(
t.object({
street: t.tuple(t.string, t.optional(t.string)),
postalCode: t.optional(t.string),
state: t.enum('AK', 'AL', 'AZ', /* ... */),
})
)
})
const CompiledUser = Compiler.generate(UserSchema)
CompiledUser({ firstName: null })
// => 🚫 fails, yay
CompiledUser({ firstName: 'Mark', address: { street: ['123 Main St'], state: 'AZ' }})
// => ✅ succeeds, yay
```
Note: As of May 2025,
Compiler.generate
works with Cloudflare workers 🎉
to compile predicate functions (as strings) and write them to disc (codegen)
Compiling predicates is generally a good option when your schemas change rarely, or when your build pipeline is already set up to consume some kind of static artifact (like an OpenAPI document) to generate code.
If not, you'll probably want to set that up, otherwise you might run into bugs when the contract changes and you or someone on your team inevitably forgets to regenerate.
///////////////
// main.js
import { t } from '@traversable/schema'
import { Compiler } from '@traversable/schema-compiler'
import * as fs from 'node:fs'
import * as path from 'node:path'
const User = t.object({ /* ... */ })
const GeneratedSchema = Compiler.generate(User)
fs.writeFileSync(
path.join(path.resolve(), 'user.generated.js'),
'export ' + GeneratedSchema
)
/////////////////////////
// user.generated.js
function check(value) {
return (
!!value && typeof value === "object" && !Array.isArray(value)
&& typeof value.firstName === "string"
&& (value.lastName === undefined || typeof value.lastName === "string")
&& !!value.address && typeof value.address === "object" && !Array.isArray(value.address)
&& (value.address.postalCode === undefined || typeof value.address.postalCode === "string")
&& Array.isArray(value.address.street) && (value.address.street.length === 1 || value.address.street.length === 2)
&& typeof value.address.street[0] === "string"
&& (value.address.street[1] === undefined || typeof value.address.street[1] === "string")
&& (value.address.state === "AK" || value.address.state === "AL" || value.address.state === "AZ")
)
}
////////////////////
// elsewhere.js
import * as User from './user.generated'
User.check({ firstName: null })
// => 🚫 fails, yay
User.check({ firstName: 'Mark', address: { street: ['123 Main St'], state: 'AZ' }})
// => ✅ succeeds, yay
This package uses a well-known trick for optimizing hot-path code.
The idea is pretty simple: rather than have predicates carry around a bunch of code that lives in memory (which will have to be interpreted at runtime), we can instead generate the predicates in string form.
Then, either write the strings to disc, or pass them to new Function(...)
so they can be evaluated
and used.
The nice thing about this trick is that it allows you to pay the cost once, at compile time. Since validation is usually in the hot path, this is usually a good tradeoff.
Because compiled schemas produce strings, internally the library needs a way to run the string as JavaScript.
Since presumably you control your schemas, this isn't actually a security concern in practice.
(Unless of course the schemas come from your users, somehow. Hopefully that's not where they're coming from.
And if that's where they're coming from -- don't use @traversable/schema-compiler
!)