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

@ytrynot/schvalid

v0.3.13

Published

JSON Schema 2020-12 validator with compiled standalone JS function using DNA bytecodes. OpenAPI 3.1 discriminator support. Close to AJV's speed.

Downloads

1,454

Readme

CI npm version Bundle size TypeScript License: MIT

@ytrynot/schvalid

Looking for testers! This package is actively seeking early users and feedback. If you try it out, please share your experience — issues, suggestions, or ideas are all welcome.

npm: https://www.npmjs.com/package/@ytrynot/schvalid · GitHub: https://github.com/linqFR/ytn/tree/main/packages/schvalid

JSON Schema 2020-12 validation with compiled standalone JS functions.

Important: This package only supports and validates JSON Schema 2020-12 with internal references. External $ref (HTTP URIs, URNs, or external files) are not supported.

Table of Contents

Overview

@ytrynot/schvalid provides JSON Schema to DNA bytecode conversion and validation using the high-performance DNA engine from @ytrynot/dna. It serves as the primary interface for JSON Schema validation in the ytrynot ecosystem.

Installation

npm install @ytrynot/schvalid

Agent Skills

Install the ytn agent skill so your AI coding agent knows how to use this package:

npx skills add linqFR/ytn

Limitations

External URIs: This package does not currently handle external JSON Schema references ($ref pointing to external files or HTTP URIs). Only internal references within the same schema document are supported.

Comparison with AJV

@ytrynot/schvalid covers all core JSON Schema 2020-12 keywords with full parity — types, object/array constraints, const/enum, allOf/anyOf/oneOf, if/then/else, not, patternProperties, dependentRequired/Schemas, internal $ref, $id, $defs, discriminator. It does not aim to replace AJV in all use cases. Key differences:

  • schvalid adds: DNA bytecode intermediate representation (IR), parseFast hybrid mode, three-mode compilation API, parser output construction, standalone JS via toJS(), faster compilation and validation than AJV.
  • schvalid lacks: external $ref, custom formats, user-defined keywords, async validation, $data, type coercion, default injection, removeAdditional, vocabularies, schema registry, multi-draft support.

Full feature-by-feature comparison: docs/ajv-comparison.md.

Usage

Converting JSON Schema to DNA

import { jschemaToDna } from "@ytrynot/schvalid";

const schema = {
  type: "object",
  properties: {
    name: { type: "string", minLength: 3 },
    age: { type: "number", minimum: 0 },
  },
};

const dna = jschemaToDna(schema);
// Returns DNA bytecode array

Compile Once, Validate Many

For performance-critical scenarios, use the schvalid() builder API to compile a schema once and reuse the validation function:

import { schvalid } from "@ytrynot/schvalid";

const schema = {
  type: "object",
  properties: {
    name: { type: "string", minLength: 3 },
    age: { type: "number", minimum: 0 },
  },
};

// Compile once
const compiler = schvalid("validation");
const validate = compiler.compile(schema);

// Validate many times efficiently
validate({ name: "John", age: 30 }); // true
validate({ name: "Jo", age: -1 }); // false

The schvalid() function accepts four modes:

  • "validation": Returns a boolean validator function (fail-fast)
  • "parser": Returns a parser function with error collection
  • "fast": Returns a hybrid parser — validates first, only re-runs the full parser on failure (see trade-offs below)
  • "all": Returns an object with validate, parse, and parseFast functions (compiled once, shared instances)
import { schvalid } from "@ytrynot/schvalid";

// Get validator, parser, and the fast hybrid parser
const compiler = schvalid("all");
const { validate, parse, parseFast } = compiler.compile(schema);

validate(data); // boolean
parse(data); // { success: true, data: ... } | { success: false, errors: [...] }
parseFast(data); // same shape as parse(), but data===input on the happy path (no fresh copy)

Fast Hybrid Parsing

schvalid("fast") (and parseFast from schvalid("all")) provides a hybrid parser that validates first (cheap, fail-fast) and only re-runs the full parser if validation fails:

import { schvalid } from "@ytrynot/schvalid";

const parseFast = schvalid("fast").compile(schema);

const result = parseFast({ name: "John", age: 30 });
// { success: true, data: { name: "John", age: 30 } }

Trade-off: on success, parseFast's data is the same reference as the input (data === input) — no fresh copy is built, unlike schvalid("parser")'s parse(), which always returns a newly constructed output object. Both agree on validity (constraints like additionalProperties: false are checked identically), so there's no discrepancy in pass/fail decisions — only in whether data is a fresh object or the original reference.

Use parseFast for validation-heavy workloads where a fresh, isolated data object isn't required on the happy path. Use the regular parser() when downstream code needs its own copy of the validated data.

// Get validate + parse + parseFast in one compile pass (single validate/parse compilation,
// shared between parse() and parseFast() — see @ytrynot/schvalid AGENTS.md for the invariant)
const { validate, parse, parseFast } = schvalid("all").compile(schema);

Discriminator Support

DNA Schema supports the OpenAPI 3.1 discriminator keyword for optimized validation of polymorphic schemas:

import { schvalid } from "@ytrynot/schvalid";

const schema = {
  type: "object",
  discriminator: {
    propertyName: "type",
  },
  required: ["type", "name"],
  oneOf: [
    {
      type: "object",
      properties: {
        type: { const: "cat" },
        name: { type: "string" },
        meows: { type: "boolean" },
      },
    },
    {
      type: "object",
      properties: {
        type: { const: "dog" },
        name: { type: "string" },
        barks: { type: "boolean" },
      },
    },
  ],
};

const { validate, parse } = schvalid("all").compile(schema);

validate({ type: "cat", name: "Whiskers", meows: true }); // true
validate({ type: "bird", name: "Tweety" }); // false

const result = parse({ type: "cat", name: "Whiskers", meows: true });
// Returns: { success: true, data: { type: "cat", name: "Whiskers", meows: true } }

The discriminator is optimized with a switch statement in the generated JavaScript code for efficient dispatching to the correct sub-schema based on the discriminator property value.

additionalProperties (and especially additionalProperties: false) defined on the root schema is inherited by each oneOf branch so that unknown properties are rejected while the discriminator property itself is still allowed.

Performance

Benchmark Results (vs AJV 2020 — run npm run bench to reproduce on your machine):

  • Compilation: faster than AJV Minimal (~4x on the reference schema).
  • Validation (valid data): faster than AJV Minimal.
  • parseFast (valid data, no error): faster than AJV Minimal. Returns { success: true, data } (same reference as input — no copy). On invalid data it is slower than AJV AllErrors because it runs the fast validator first, then falls back to the full parser to collect detailed errors — a deliberate trade-off for the common case where most inputs are valid.
  • Parser mode: not directly comparable to AJV — AJV is validation-only (returns boolean), while parser constructs a fresh Object.create(null) output object with validated properties, like Zod's parse(). The generated function is ~30% smaller than AJV's, but the benchmark is slower because it does strictly more work (allocation + copy + reconstruction). This is a different contract, not a speed regression.

Benchmark results vary across machines and runs. Run npm run bench yourself to get numbers for your environment.

Which mode should I use?

  • Use validation for plain fail-fast boolean checks.
  • Use parseFast when you need detailed errors on failure but don’t need a fresh output object on success. parseFast runs the cheap fail-fast validator first; if the input is invalid, it falls back to the full parser to collect all errors. It is the fastest rich-error path and the one most users want.
  • Use parser only when you explicitly need a fresh, Object.create(null) output object with the original unknown properties preserved (the same contract as Zod parse()). It is slower than all above, because it is a parse+transform operation, not just a validator: it allocates an Object.create(null) object, copies the input, rebuilds arrays, and returns { success, data }. That reconstruction is why it is slower than AJV on the reference benchmark.

Development

Build

npm run build

Testing

# Run JSON Schema test suite plus discriminator and edge-cases tests
npm test

# Run all correctness tests
npm run test:full

# Run all benchmarks (standalone tsx, not vitest; `bench` is an alias of `perf`)
npm run bench
# or
npm run perf

Test Coverage of JSON validation Suite: 1243 passing per mode, 44 skipped.

  • The 44 skipped tests are from the JSON Schema Test Suite and involve external references ($ref to HTTP URIs, URNs, or external files), which are explicitly out of scope for DNA Schema (only internal references are supported).

The full test suite includes:

  • JSON Schema Test Suite: Comprehensive validation against official JSON Schema 2020-12 test cases. For more information, read JSON Schema Validation Suite. Skipped: refRemote.json, dynamicRef.json, content.json, vocabulary.json.
  • Discriminator Tests: Full coverage of OpenAPI 3.1 discriminator keyword with validator and parser modes.
  • Performance Benchmarks: Comparative benchmarks against AJV for compilation and validation speed.

Peer Dependencies

  • zod: ^4.4.3

Dependencies

  • @ytrynot/dna: * (workspace dependency)

License

MIT

Author

linqFR