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 🙏

© 2025 – Pkg Stats / Ryan Hefner

livr

v2.10.1

Published

Lightweight validator supporting Language Independent Validation Rules Specification

Downloads

40,145

Readme

LIVR Validator

Lightweight, fast, and language-independent validation for JavaScript & TypeScript

npm version npm downloads TypeScript Bundle Size Known Vulnerabilities


LIVR Specification - Full documentation of all validation rules

Design Decisions - Why LIVR works the way it does: schemas as data, security, error codes, and more


Built-in Rules

| Category | Rules | |----------|-------| | Common | required · not_empty · not_empty_list · any_object | | String | string · eq · one_of · max_length · min_length · length_between · length_equal · like | | Numeric | integer · positive_integer · decimal · positive_decimal · max_number · min_number · number_between | | Special | email · url · iso_date · equal_to_field | | Meta | nested_object · variable_object · list_of · list_of_objects · list_of_different_objects · or | | Modifiers | trim · to_lc · to_uc · remove · leave_only · default |

Need more rules? Check out livr-extra-rules for additional validators.


Features

Why LIVR?

  • Zero dependencies - No external runtime dependencies
  • Tiny footprint - Validator core < 1KB, with all rules ~3KB (min+gzip)
  • TypeScript support - Full type inference from validation schemas
  • Isomorphic - Works in Node.js and browsers
  • Sync & async - Both synchronous and asynchronous validation
  • Extensible - Easy to add custom rules and aliases

Validation Capabilities

  • Declarative schemas - Rules are language-independent JSON structures
  • Multiple rules per field - Chain any number of validators
  • Aggregated errors - Returns all errors at once, not just the first
  • Data sanitization - Output contains only validated fields
  • Hierarchical validation - Validate nested objects and arrays
  • Readable error codes - Returns codes like REQUIRED, TOO_SHORT (not messages)
  • Output transformation - Rules can modify output (trim, default, etc.)

Quick Start

import LIVR from 'livr';

const validator = new LIVR.Validator({
    name:      'required',
    email:     ['required', 'email'],
    age:       'positive_integer',
    password:  ['required', { min_length: 8 }],
    password2: { equal_to_field: 'password' }
});

const validData = validator.validate(userData);

if (validData) {
    // Use validated & sanitized data
    saveUser(validData);
} else {
    // Handle validation errors
    console.log(validator.getErrors());
    // { email: 'WRONG_EMAIL', password: 'TOO_SHORT' }
}

Table of Contents


Installation

npm install livr

Browser (without npm)

Pre-built versions are available in the dist folder:

| Build | Description | |-------|-------------| | dist/production/main.js | Minified sync validator | | dist/production-async/main.js | Minified async validator | | dist/development/main.js | Development build with source maps | | dist/development-async/main.js | Async development build |


Usage Guide

Basic Validation

import LIVR from 'livr';

// Enable auto-trim globally (optional)
LIVR.Validator.defaultAutoTrim(true);

const validator = new LIVR.Validator({
    name:     'required',
    email:    ['required', 'email'],
    gender:   { one_of: ['male', 'female'] },
    phone:    { max_length: 10 },
    password: ['required', { min_length: 10 }],
    password2: { equal_to_field: 'password' }
});

const validData = validator.validate(userData);

if (validData) {
    saveUser(validData);
} else {
    console.log(validator.getErrors());
}

Note: Rule names support both snake_case and camelCase. Use one_of or oneOf, min_length or minLength - they're equivalent.

TypeScript with Type Inference

LIVR can automatically infer TypeScript types from your validation schema:

import LIVR from 'livr';
import type { InferFromSchema } from 'livr/types';

const userSchema = {
    name: ['required', 'string'],
    email: ['required', 'email'],
    age: 'positive_integer',
    role: { one_of: ['admin', 'user'] as const },
} as const;

// Automatically infer type from schema
type User = InferFromSchema<typeof userSchema>;
// Result: { name: string; email: string; age?: number; role?: 'admin' | 'user' }

const validator = new LIVR.Validator<User>(userSchema);

// Validate data from external source (API request, form submission, etc.)
const input = getUserInput();
const validData = validator.validate(input);

if (validData) {
    // validData is typed as User
    console.log(validData.name);  // string
    console.log(validData.age);   // number | undefined
}

Important: Use as const after your schema to enable proper type inference.

For comprehensive TypeScript documentation including nested objects, lists, unions, and custom rule types, see TypeScript Type Inference Guide.

Async Validation

For rules that require async operations (database lookups, API calls):

import LIVR from 'livr/async';

const validator = new LIVR.AsyncValidator({
    username: ['required', 'unique_username'], // custom async rule
    email:    ['required', 'email'],
});

try {
    const validData = await validator.validate(userData);
    saveUser(validData);
} catch (errors) {
    console.log(errors);
}

Key differences from sync validator:

  • Import from 'livr/async'
  • Use AsyncValidator instead of Validator
  • validate() returns a Promise - use await or .then()
  • On error, rejects with errors object (no getErrors() method)
  • Fields validate in parallel; rules per field run sequentially

Using Modifiers

Modifiers transform data during validation:

const validator = new LIVR.Validator({
    email: ['required', 'trim', 'email', 'to_lc'],  // trim, validate, lowercase
    age:   ['positive_integer', { default: 18 }],   // default value if empty
});

Available modifiers: trim, to_lc, to_uc, default, remove, leave_only

Custom Rules

Using Aliases (Recommended)

Create reusable rules by combining existing ones:

const validator = new LIVR.Validator({
    password: ['required', 'strong_password'],
    age:      ['required', 'adult_age'],
});

validator.registerAliasedRule({
    name: 'strong_password',
    rules: { min_length: 8 },
    error: 'WEAK_PASSWORD'
});

validator.registerAliasedRule({
    name: 'adult_age',
    rules: ['positive_integer', { min_number: 18 }],
    error: 'MUST_BE_ADULT'
});

Writing Custom Rule Functions

For complex validation logic:

const validator = new LIVR.Validator({
    password: ['required', 'strong_password'],
});

validator.registerRules({
    strong_password() {
        return (value) => {
            // Empty values are handled by 'required' rule
            if (value === undefined || value === null || value === '') return;

            if (!/[A-Z]/.test(value)) return 'MISSING_UPPERCASE';
            if (!/[a-z]/.test(value)) return 'MISSING_LOWERCASE';
            if (!/[0-9]/.test(value)) return 'MISSING_DIGIT';
            if (value.length < 8) return 'TOO_SHORT';
        };
    }
});

Async Custom Rules

import LIVR from 'livr/async';

const validator = new LIVR.AsyncValidator({
    username: ['required', 'unique_username'],
});

validator.registerRules({
    unique_username() {
        return async (value) => {
            if (value === undefined || value === null || value === '') return;

            const exists = await db.users.exists({ username: value });
            if (exists) return 'USERNAME_TAKEN';
        };
    }
});

Registering Rules Globally

// Register for all future validator instances
LIVR.Validator.registerDefaultRules({
    my_rule(arg1, arg2) {
        return (value, allValues, outputArr) => {
            // Return error code on failure, undefined on success
            if (invalid) return 'ERROR_CODE';
        };
    }
});

LIVR.Validator.registerAliasedDefaultRule({
    name: 'valid_address',
    rules: { nested_object: { country: 'required', city: 'required' }}
});

Tree-Shaking (Reduce Bundle Size)

Import only the rules you need:

import Validator from 'livr/lib/Validator';

Validator.registerDefaultRules({
    required:       require('livr/lib/rules/common/required'),
    email:          require('livr/lib/rules/special/email'),
    min_length:     require('livr/lib/rules/string/min_length'),
    max_length:     require('livr/lib/rules/string/max_length'),
    equal_to_field: require('livr/lib/rules/special/equal_to_field'),
});

const validator = new Validator({ /* schema */ });

API Reference

Static Methods

new LIVR.Validator(schema, options?)

Creates a new validator instance.

const validator = new LIVR.Validator(schema, { autoTrim: true });

| Option | Default | Description | |--------|---------|-------------| | autoTrim | false | Trim all string values before validation |

Validator.defaultAutoTrim(boolean)

Enable/disable auto-trim globally for all new instances.

LIVR.Validator.defaultAutoTrim(true);

Validator.registerDefaultRules(rules)

Register custom rules globally.

LIVR.Validator.registerDefaultRules({
    my_rule(arg) {
        return (value) => { /* ... */ };
    }
});

Validator.registerAliasedDefaultRule(alias)

Register a rule alias globally.

LIVR.Validator.registerAliasedDefaultRule({
    name: 'adult_age',
    rules: ['positive_integer', { min_number: 18 }],
    error: 'MUST_BE_ADULT'  // optional custom error
});

Validator.getDefaultRules()

Returns all registered default rules.

Instance Methods

validator.validate(data)

Validates input data against the schema.

Sync Validator:

const result = validator.validate(data);
if (result) {
    // result contains validated data
} else {
    const errors = validator.getErrors();
}

Async Validator:

try {
    const result = await validator.validate(data);
} catch (errors) {
    // errors object
}

validator.getErrors()

Returns the errors object from the last validation (sync only).

// Example output:
{
    email: 'WRONG_EMAIL',
    password: 'TOO_SHORT',
    address: { zip: 'NOT_POSITIVE_INTEGER' }
}

validator.prepare()

Pre-compiles validation rules. Called automatically on first validate(), but can be called manually for warmup.

const validator = new LIVR.Validator(schema).prepare();

validator.registerRules(rules)

Register rules for this instance only.

validator.registerRules({
    custom_rule() { return (value) => { /* ... */ }; }
});

validator.registerAliasedRule(alias)

Register a rule alias for this instance only.

validator.registerAliasedRule({
    name: 'strong_password',
    rules: { min_length: 8 },
    error: 'WEAK_PASSWORD'
});

validator.getRules()

Returns all rules registered for this instance.


TypeScript Type Inference

LIVR automatically infers TypeScript types from your validation schemas:

import LIVR from 'livr';
import type { InferFromSchema } from 'livr/types';

const schema = {
    name: ['required', 'string'],
    age: 'positive_integer',
    role: { one_of: ['admin', 'user'] as const },
} as const;

type User = InferFromSchema<typeof schema>;
// { name: string; age?: number; role?: 'admin' | 'user' }

For complete documentation on type inference including:

  • Required vs optional fields
  • Nested objects and arrays
  • Discriminated unions
  • Custom rule type definitions

See the TypeScript Type Inference Guide.


Performance

LIVR is designed for speed:

  • Reuse validators - Construct once, validate many times. validator.validate() is extremely fast.
  • Lazy compilation - Rules are compiled on first validation (or call prepare() for warmup).
  • Faster than alternatives - 2x faster than Joi, 100x faster rule compilation than fastest-validator.
// Good: Create once, use many times
const validator = new LIVR.Validator(schema);

for (const item of items) {
    const result = validator.validate(item);
}

// Avoid: Creating validator for each validation
for (const item of items) {
    const validator = new LIVR.Validator(schema);  // Slower
    const result = validator.validate(item);
}

Additional Resources


Contributing

Found a bug or have a feature request? Please open an issue on GitHub.


License

MIT License - see LICENSE for details.


Author

Viktor Turskyi (@koorchik)

Contributors

  • eNdiD