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

@jetio/schema-builder

v1.1.2

Published

Fluent, type-safe JSON Schema builder with spec compliant type inference for TypeScript. Works with @jetio/validator.

Readme

📐 Schema Builder Guide

Write compliant JSON Schema fluently. Get TypeScript types that mirrors runtime expectations and a runtime validator from the same line of code - no second install, no as const.

Most schema tools make you pick a lane: a builder or a validator or type inference. @jetio/schema-builder gives you all three from one .build() and it's actual JSON Schema (Draft 06 → 2020-12), not a lookalike DSL. So you can finally write JSON Schema without it feeling like filling out a tax form.

The Schema Builder provides a fluent, type-safe API for constructing JSON Schemas programmatically. Build complex schemas with autocomplete, validation, and zero boilerplate and get automatic type inference.

Note: This package includes @jetio/validator as a dependency. You get both the builder AND a very fast JSON Schema validator in one install.

Important: Jetio/schema-builder is a JSON Schema spec-compliant tool built on top of @jetio/validator. To utilize it to its fullest potential, it's essential to understand the main validator package. All core documentation about validation rules, error handling, $data references, and advanced features can be found in the Validator Documentation.


📦 Installation

npm install @jetio/schema-builder
# or
yarn add @jetio/schema-builder
# or
pnpm add @jetio/schema-builder
import { SchemaBuilder, RefBuilder } from "@jetio/schema-builder";
import { JetValidator } from "@jetio/schema-builder"; // Re-exported from @jetio/validator

Just need the validator? Install @jetio/validator directly for a smaller bundle.


Type Inference

JetIO Schema Builder includes Json Schema spec compliant automatic TypeScript type inference through Jet.Infer<>. Write your schema once and get both runtime validation AND compile-time types!

import { SchemaBuilder, Jet } from "@jetio/schema-builder";

const userSchema = new SchemaBuilder()
  .object()
  .properties({
    id: (s) => s.number(),
    name: (s) => s.string(),
    email: (s) => s.string().format('email')
  })
  .required(['id', 'name', 'email'])
  .build();

// Automatically infer TypeScript type from schema
type User = Jet.Infer<typeof userSchema>;
/*
{
  id: number;
  name: string;
  email: string;
}
*/

// Type-safe usage
const user: User = {
  id: 1,
  name: "Alice",
  email: "[email protected]"
}; // ✅
const validate = new JetValidator().compile(userSchema);
validate(user); // true
const invalidUser: User = {
  id: 1,
  name: "Bob"
  // ❌ TypeScript Error: Property 'email' is missing
};

One schema. A real type. A compiled validator. They can never fall out of sync.


What you can't do anywhere else

oneOf is actually exclusive

Every other library hands you A | B and lets you mix fields from both branches. We mark the other branch's keys as never automatically — a true discriminated union with no kind tag required.

const paymentSchema = new SchemaBuilder()
  .oneOf(
    (s) => 
      s.object().properties(
        { card: (s) => s.string() }
      )
      .required(["card"]),
    (s) => 
      s.object().properties(
        { paypal: (s) => s.string() }
      )
      .required(["paypal"]),
  )
  .build();

type Payment = Jet.Infer<typeof paymentSchema>;
// {
//   readonly card: string;
//    paypal?: undefined;
// } | {
//    readonly paypal: string;
//    card?: undefined;
// }
const ok: Payment = { card: "4242…" };                     // ✅
const bad: Payment = { card: "4242…", paypal: "[email protected]" }; // ❌ can't mix branches

if / then / elseIf / else inferred

Conditional types from conditional schemas. We push TypeScript to its limit to type the full chain including elseIf, which no other TS schema library supports.

const accountSchema = new SchemaBuilder()
  .object()
  .properties({
    accountType: (s) => s.string(),
    username: (s) => s.string(),
    companyName: (s) => s.string(),
    email: (s) => s.string().format("email"),
  })
  .required(["accountType", "email"])
  .if((s) =>
    s.object().properties({
      accountType: (s) => s.const("personal"),
    }),
  )
  .then((s) => s.object().required(["username"]))
  .elseIf((s) =>
    s.object().properties({
      accountType: (s) => s.const("business"),
    }),
  )
  .then((s) => s.object().required(["companyName"]))
  .end()
  .build();

type Account = Jet.Infer<typeof accountSchema>;
// {
//    accountType: "personal";
//    username: string;
//    email: string;
//    companyName?: string | undefined;
// } | {
//    accountType: "business";
//    companyName: string;
//    email: string;
//    username?: string | undefined;
// } | {
//    accountType: string;
//    email: string;
//    username?: string | undefined;
//    companyName?: string | undefined;
// }

const validate = new JetValidator({ allErrors: true }).compile(accountSchema);

validate({ accountType: "personal", email: "[email protected]", username: "alice" }); // true
validate({ accountType: "personal", email: "[email protected]" });                    // false

Your types mirror runtime expectations

That one schema object gave you the JSON Schema, the type, and the validation all enforcing the exact same rules.

There are many more keywords as well, from anyOf, allOf, prefixItems and so on, check the docs for all.


Schema reuse that types itself

Build a base schema once and .extend() it into variants. admin from user, strict from loose. properties and required merge; everything else overrides; and Jet.Infer<> tracks every change.

Where $ref leaves you with unknown, .extend() keeps full inference.

const baseUser = new SchemaBuilder()
  .object()
  .properties({
    id: (s) => s.number(),
    email: (s) => s.string().format("email"),
  })
  .required(["id", "email"])
  .build();

const adminUser = new SchemaBuilder()
  .extend(baseUser)
  .properties({
    role: (s) => s.const("admin"),
    permissions: (s) => s.array().items((s) => s.string()),
  })
  .required(["role", "permissions"])
  .build();

type AdminUser = Jet.Infer<typeof adminUser>;
// {
//   id: number;            ← from base
//   email: string;         ← from base
//   role: "admin";
//   permissions: string[];
// }

Trim with .remove(), loosen with .optional(), compose traits with allOf all without writing a single interface by hand.

Why teams pick it

  • Validator included. Bundled with @jetio/validator compiles schema to functions, very fast.
  • Spec-compliant inference. Types behave like JSON Schema, not just resemble it. If the validator rejects it, TypeScript rejects it too.
  • Full draft coverage. unevaluatedProperties, prefixItems, $dynamicRef, dependentRequired, patternProperties → template-literal keys, and more (draft 06 → 2020-12).
  • No as const. Literals from .enum() and .const() are inferred for you.
  • Built to work together, not bolted together. The builder, validator, and inference weren't three projects stitched into one, they were designed as a single system from day one. No adapter layers, no glue, no impedance mismatch.
  • Mix builder and raw JSON freely paste existing schemas, build the rest.
import { SchemaBuilder, Jet } from "@jetio/schema-builder";

const productSchema = new SchemaBuilder()
  .object()
  .properties({
    // Builder syntax — fluent, type-inferred
    id: (s) => s.string().format("uuid"),
    name: (s) => s.string().minLength(1),

    // Raw JSON Schema — paste what you already have
    price: { type: "number", minimum: 0 },

    // Mix both inside the same property
    dimensions: (s) =>
      s.object().properties({
        width: { type: "number" },
        height: (s) => s.number(),
      }),
  })
  .required(["id", "name", "price"])
  .build();

type Product = Jet.Infer<typeof productSchema>;
// {
//   id: string;
//   name: string;
//   price: number;
//   dimensions?: { width?: number; height?: number };
// }

🚀 Try it live

No install

Open in StackBlitz

Documentation

The full builder guide, the type-inference deep dive, and the complete API reference live here: docs link
For complete type inference documentation, see Type Inference Guide

Topics covered in the Type Inference guide:

  • Primitives, objects, arrays, and their type inference
  • Pattern properties with template literal types
  • Multiple types and union inference
  • Discriminated unions with oneOf/anyOf
  • Conditional type inference (if/then/else/elseIf)
  • Complex compositions with allOf
  • Required vs optional property splitting
  • Type inference limitations and workarounds -addtionalItems/Properties, unevaluatedProperties/Items, patternProperties.
@jetio/validator documentation at docs

📄 License

MIT © Great Venerable


🔗 Links


Built with ❤️ by The Venerable Supreme