npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@amritk/generate-validators

v0.11.6

Published

Generate TypeScript validation functions from JSON Schemas.

Readme

@amritk/generate-validators

Programmatic API for generating predicate-style TypeScript validators from JSON Schemas.

status  version  license  JSON Schema  node  vibe coded


Overview

@amritk/generate-validators produces lightweight runtime validators from a JSON Schema. Where @amritk/generate-parsers coerces and parses unknown input into a typed value, this package emits cheaper predicate-style functions that simply tell you whether a value matches a schema (and where it doesn't).

Each generated file exports:

  • A TypeScript type definition for the schema
  • A validateFoo(input: unknown, _path?: string): ValidationResult function

A shared validation-result.ts template and an index.ts barrel are emitted alongside the generated files.


Installation

npm install @amritk/generate-validators
# or
pnpm add @amritk/generate-validators
# or
yarn add @amritk/generate-validators
# or
bun add @amritk/generate-validators

Usage

import { buildValidatorSchema } from '@amritk/generate-validators'
import type { JSONSchema } from 'json-schema-typed/draft-2020-12'

const schema: JSONSchema = {
  type: 'object',
  properties: {
    info: { $ref: '#/$defs/info' },
  },
  $defs: {
    info: {
      type: 'object',
      properties: { title: { type: 'string' } },
      required: ['title'],
    },
  },
}

const files = await buildValidatorSchema(schema, 'Document')
// → [{ filename: 'document.ts', content: '...' }, { filename: 'info.ts', ... }, { filename: 'validation-result.ts', ... }, { filename: 'index.ts', ... }]

Write the resulting files to disk and import the validators where you need them:

import { validateDocument } from './generated'

const result = validateDocument(input)
if (!result.valid) {
  console.error(result.errors)
}

API

buildValidatorSchema(rootSchema, rootTypeName)

| Parameter | Type | Description | |:---|:---|:---| | rootSchema | JSONSchema | The root schema to traverse. $ref and $dynamicRef are resolved recursively. Draft-07 schemas are upgraded to 2020-12 automatically. | | rootTypeName | string | Name used for the root type (e.g. "Document"). |

Returns: Promise<GeneratedFile[]> where GeneratedFile = { filename: string; content: string }.


Semantics

Generated validators track the @amritk/runtime-validators interpreter. Array items are validated in full — an item's type, $ref, nested properties / required, and scalar constraints (minLength, minimum, …) are all enforced, recursing to any depth — and the boolean guard (isX) reaches the identical verdict. Validating array item contents costs throughput proportional to the per-item work (a bare string[] is free; a closed object with several fields is meaningfully slower), which is why array-heavy schemas validate more slowly than scalar/object ones.

One divergence is worth calling out: NaN satisfies a constrained number. Because the numeric bound checks are the exact negation of the error condition (e.g. !(x < minimum)), and every comparison against NaN is false, a NaN passes minimum/maximum/exclusive*/multipleOf. This matches the interpreter but differs from validators (e.g. Ajv) that reject NaN for type: "number". NaN never appears in parsed JSON; guard against it upstream if your values can be non-JSON.


Benchmarks

Generated validators are straight-line, monomorphic TypeScript with no generic dispatch. The exported validateX is split into a hot and a cold half: on the happy path it runs a single allocation-free boolean guard — a pure && chain of typeof checks (plus an Object.keys().length count when an object is closed with additionalProperties: false) — and return trues straight away, only calling a separate error-collecting function when something is actually wrong. Keeping the hot function tiny lets V8 optimise it aggressively, so a valid-input check beats every other library measured — including the build-time transformer typia — while still emitting full JSON-Pointer errors for invalid input, and emitting the validator stays far cheaper than compiling a schema at startup. Measured on Bun 1.3 (Linux x64), validating valid input at steady state:

| schema | mjst (generated) | typia (transformed) | ajv (compiled) | typebox (compiled) | zod | |:--|--:|--:|--:|--:|--:| | small (4 fields) | ~22M ops/s | ~4.2M ops/s | ~7.0M ops/s | ~4.0M ops/s | ~1.8M ops/s | | order (nested + array) | ~6.9M ops/s | ~1.7M ops/s | ~2.5M ops/s | ~1.7M ops/s | ~0.4M ops/s | | assert-loose | ~110M ops/s | ~100M ops/s | ~31M ops/s | ~41M ops/s | ~3.2M ops/s | | assert-strict | ~98M ops/s | ~82M ops/s | ~13M ops/s | ~28M ops/s | ~1.1M ops/s |

The assert-loose / assert-strict rows are the exact shape used by moltar/typescript-runtime-type-benchmarks (seven scalar roots plus a nested object); the boolean guard lets mjst edge past typia on both, with and without additionalProperties: false. (typia and TypeBox still win the invalid path, where they bail on the first error rather than collecting a full error list.)

Preparing a validator costs ~0.1 ms for mjst codegen and ~0.05–0.12 ms for a TypeBox TypeCompiler compile, versus ~8–10 ms for an Ajv compile. Every library agrees on every verdict; parity is asserted before timing (TypeBox is given uuid/email format checkers so every library does the same work). Each library is timed in an isolated process over a pool of distinct inputs, reporting the median of many trials — so the optimiser can't hoist or eliminate the work and the numbers stay reproducible. Micro-benchmark figures vary by machine and runtime — reproduce with:

bun run bench

Related packages


License

MIT