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

@voidhaus/monoschema

v0.0.5

Published

A minimal, type-safe schema validation library for TypeScript projects. Provides a set of composable constraints and utilities for validating data structures, with a focus on developer ergonomics and type inference.

Readme

monoschema

A minimal, type-safe schema validation library for TypeScript projects. Provides a set of composable constraints and utilities for validating data structures, with a focus on developer ergonomics and type inference.

Features

  • Type-safe schema definitions
  • Composable constraints for numbers, strings, arrays, objects, and more
  • Built-in validators for common formats (email, URL, IP, UUID, MAC, base64, etc.)
  • Custom constraint support
  • Type inference for validated data
  • Conditional validation with $when rules and discriminated unions
  • Zero dependencies

Installation

pnpm add @voidhaus/monoschema
# or
yarn add @voidhaus/monoschema
# or
npm install @voidhaus/monoschema

Extensions

Check out monoschema-mongo and monoschema-transformer

Usage

Basic Schema Validation

import { configureMonoSchema, type InferTypeFromMonoSchema } from '@voidhaus/monoschema';
import { min, minLength, max, email } from '@voidhaus/monoschema/constraints';

const userSchema = {
  $type: Object,
  $properties: {
    name: { $type: String, $constraints: [minLength(2)] },
    age: { $type: Number, $constraints: [min(1), max(120)] },
    email: { $type: String, $constraints: [email()], $optional: true },
  }
} as const;

// Infer the TypeScript type from the schema
type User = InferTypeFromMonoSchema<typeof userSchema>;
// Result: { name: string; age: number; email?: string }

const monoSchema = configureMonoSchema();
const validate = monoSchema.validate(userSchema);

// Valid data
const validUser = {
  name: 'Alice',
  age: 30,
  email: '[email protected]'
};

const result = await validate(validUser);
console.log(result.valid); // true
console.log(result.data); // { name: 'Alice', age: 30, email: '[email protected]' }

// Invalid data
const invalidUser = {
  name: 'A', // too short
  age: 200,  // too high
  email: 'not-an-email' // invalid email
};

const invalidResult = await validate(invalidUser);
console.log(invalidResult.valid); // false
console.log(invalidResult.errors);
// [
//   { path: 'name', message: 'String length 1 is less than minimum 2', ... },
//   { path: 'age', message: 'Value 200 is greater than maximum 120', ... },
//   { path: 'email', message: 'String not-an-email...', ... }
// ]

Custom Constraints

import { Constraint } from '@voidhaus/monoschema/constraints';

const startsWithA: Constraint = {
  validate: (value) => typeof value === 'string' && value.startsWith('A'),
  message: (value) => `Value ${value} does not start with 'A'`,
};

Type Inference with Conditional Validation

MonoSchema provides type-safe inference even with conditional validation:

import { type InferTypeFromMonoSchema } from '@voidhaus/monoschema';

const eventSchema = {
  $type: Object,
  $discriminant: {
    property: "type",
    mapping: {
      "email": {
        $type: Object,
        $properties: {
          type: { $type: String },
          to: { $type: String },
          subject: { $type: String },
          body: { $type: String }
        }
      },
      "webhook": {
        $type: Object,
        $properties: {
          type: { $type: String },
          url: { $type: String },
          method: { $type: String }
        }
      }
    }
  }
} as const;

// TypeScript automatically infers the discriminated union type
type Event = InferTypeFromMonoSchema<typeof eventSchema>;
// Result:
// type Event = 
//   | { type: "email"; to: string; subject: string; body: string }
//   | { type: "webhook"; url: string; method: string }

const emailEvent: Event = {
  type: "email",
  to: "[email protected]", 
  subject: "Welcome!",
  body: "Thanks for signing up"
};

const webhookEvent: Event = {
  type: "webhook",
  url: "https://api.example.com/webhook",
  method: "POST"
};

Conditional Validation

MonoSchema supports powerful conditional validation using $when rules and discriminated unions.

Conditional Required Fields

Make fields required based on other property values:

import { configureMonoSchema } from '@voidhaus/monoschema';

const userSchema = {
  $type: Object,
  $properties: {
    accountType: { $type: String }, // "personal" | "business"
    
    // Only required if accountType is "business"
    companyName: { 
      $type: String,
      $optional: true,
      $when: [{
        property: "accountType",
        condition: { equals: "business" },
        then: { required: true }
      }]
    },
    
    // Only required if accountType is "personal"  
    firstName: {
      $type: String,
      $optional: true,
      $when: [{
        property: "accountType", 
        condition: { equals: "personal" },
        then: { required: true }
      }]
    }
  }
} as const;

const monoSchema = configureMonoSchema();
const validate = monoSchema.validate(userSchema);

// Business account - requires companyName
await validate({
  accountType: "business",
  companyName: "ACME Corp"
}); // ✅ Valid

// Personal account - requires firstName
await validate({
  accountType: "personal", 
  firstName: "John"
}); // ✅ Valid

Discriminated Unions

Handle different schemas based on a discriminant property:

const shapeSchema = {
  $type: Object,
  $discriminant: {
    property: "type",
    mapping: {
      "circle": {
        $type: Object,
        $properties: {
          type: { $type: String },
          radius: { $type: Number }
        }
      },
      "rectangle": {
        $type: Object, 
        $properties: {
          type: { $type: String },
          width: { $type: Number },
          height: { $type: Number }
        }
      }
    }
  }
} as const;

const validate = monoSchema.validate(shapeSchema);

await validate({
  type: "circle",
  radius: 5
}); // ✅ Valid

await validate({
  type: "rectangle",
  width: 10,
  height: 5
}); // ✅ Valid

Complex Conditions

Use advanced condition matching:

const productSchema = {
  $type: Object,
  $properties: {
    category: { $type: String },
    hasVariants: { $type: Boolean },
    
    // Only required for clothing/electronics with variants
    variants: {
      $type: [String],
      $optional: true,
      $when: [{
        property: "category",
        condition: { 
          and: [
            { in: ["clothing", "electronics"] },
            { custom: (_, obj) => (obj as any).hasVariants === true }
          ]
        },
        then: { required: true }
      }]
    }
  }
} as const;

Available Conditions

  • { equals: value } - Exact equality
  • { in: [value1, value2] } - Value in array
  • { not: condition } - Negation
  • { and: [condition1, condition2] } - All conditions must match
  • { or: [condition1, condition2] } - Any condition must match
  • { matches: /regex/ } - Regex pattern matching (strings)
  • { range: { min: 0, max: 100 } } - Numeric range
  • { exists: true } - Property exists (not null/undefined)
  • { custom: (value, fullObject) => boolean } - Custom function

Available Actions

  • { required: true/false } - Make property required/optional
  • { type: NewType } - Change property type
  • { constraints: [...] } - Add additional constraints
  • { limitTo: [values] } - Restrict to specific values
  • { schema: {...} } - Replace entire property schema

API

Core Functions

  • configureMonoSchema(options?) — Create a MonoSchema instance
  • validate(schema) — Create a validation function for a schema

Constraints

  • min(number) / max(number)
  • minLength(number) / maxLength(number)
  • regex(RegExp)
  • email()
  • url()
  • ipv4() / ipv6() / cidrv4() / cidrv6()
  • mac()
  • uuid() / guid()
  • hex()
  • base64()
  • instanceOf(constructor)

Schema Properties

  • $type — The expected type (String, Number, Boolean, Date, Object, Array, or custom)
  • $optional — Whether the property is optional
  • $constraints — Array of constraint functions
  • $properties — For Object types, nested property definitions
  • $when — Conditional validation rules
  • $discriminant — Discriminated union configuration

Types

  • Constraint — Interface for custom constraints
  • ConditionalRule — Interface for conditional validation rules
  • ConditionalCondition — Union of available condition types
  • ConditionalAction — Union of available actions
  • DiscriminantConfig — Configuration for discriminated unions
  • MonoSchemaPropertyPath — Type for property paths
  • InferTypeFromMonoSchema — Type inference for schemas

License

MIT

Related Packages