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

orange-dragonfly-validator

v1.0.0

Published

Library for input parameters validation

Readme

Orange Dragonfly Validator

A type-safe, zero-dependency validation library for Node.js and TypeScript. Define schemas as plain objects, get validated and strongly-typed data back.

import { parse } from 'orange-dragonfly-validator'

const schema = {
  name: { type: 'string', required: true, min: 1, max: 100 },
  email: { type: 'string', required: true, special: 'email' },
  age: { type: 'integer', min: 0, max: 150 },
} as const

const data = parse(schema, input)
// data: { name: string; email: string; age?: number }

Features

  • Type inference — output types are inferred from as const schemas, no manual typing needed
  • Zero dependencies — nothing to audit, nothing to break
  • Dual format — ships ESM and CommonJS with full TypeScript declarations
  • Structured errors — machine-readable codes, human-readable messages, contextual parameters
  • Nested validation — recursive schemas for objects and arrays of any depth
  • JSON Schema interop — two-way conversion between ODValidator schemas and JSON Schema
  • Built-in format validators — email, URL, UUID, phone, IPv4, date, datetime, hex color
  • Custom transforms — transform values before or instead of validation

Installation

npm install orange-dragonfly-validator

Requires Node.js 18 or later.

Quick Start

parse — validate and throw on failure

import { parse, ODValidatorException } from 'orange-dragonfly-validator'

const schema = {
  name: { type: 'string', required: true, pattern: /^[A-Z][a-z]+$/ },
  position: { type: 'string', required: true },
  term_ends: { type: 'integer', min: 2025 },
} as const

try {
  const data = parse(schema, {
    name: 'Donald',
    position: 'President of the United States',
    term_ends: 2021,
  })
} catch (e) {
  if (e instanceof ODValidatorException) {
    console.error(e.info)
    // { term_ends: ['Minimal value (length) is 2025. 2021 provided'] }
  }
}

safeParse — validate without throwing

import { safeParse } from 'orange-dragonfly-validator'

const result = safeParse(schema, input)
if (result.success) {
  console.log(result.data.name) // typed as string
} else {
  console.log(result.errors) // Record<string, ODValidatorErrorEntry[]>
}

ODValidator — class-based usage

import { ODValidator, ODValidatorRules } from 'orange-dragonfly-validator'

const rules = new ODValidatorRules(schema)
const validator = new ODValidator(rules, {
  strictMode: true,
  exceptionMode: true,
})

validator.validate(input)
console.log(validator.data)
console.log(validator.errors)

Schema Reference

A schema is a plain object mapping field names to rule definitions. No field in a rule definition is required — an empty {} accepts any value.

Rule Properties

| Property | Type | Description | |----------|------|-------------| | type | string \| string[] | Allowed type(s): 'string', 'number', 'integer', 'array', 'object', 'boolean', 'null', 'function' | | required | boolean | If true, field must be present in input | | default | any | Value used when field is missing from input | | in | unknown[] | Whitelist of allowed values | | in:public | unknown[] \| boolean | Controls which in values appear in error messages. true exposes the in list; an array overrides it | | min | number | Minimum value (numbers), length (strings), or item count (arrays) | | max | number | Maximum value (numbers), length (strings), or item count (arrays) | | pattern | RegExp \| string | Regex the value must match (strings and numbers) | | special | string | Built-in format validator name (see Format Validators) | | transform | function | (value: unknown) => unknown — transforms value before validation | | apply_transformed | boolean | If true, the transformed value replaces the original in output | | children | ODValidatorRulesSchema | Nested schema for object properties or array elements | | per_type | object | Type-specific rule overrides (see Per-Type Rules) |

Meta-Keys

Schemas support special meta-keys that control behavior at the schema level:

const schema = {
  '@': { strict: false },       // Schema options
  '#': { type: 'string', pattern: /^[a-z_]+$/ },  // Key name validator
  '*': { type: 'string', max: 255 },               // Wildcard — applies to all values

  // Regular field rules
  name: { type: 'string', required: true },
} as const

| Key | Purpose | |-----|---------| | @ | Schema-level options. Currently supports strict (boolean, default true) — when true, rejects input keys not defined in the schema | | # | Key validator — validates the property names themselves, not values | | * | Wildcard rule — applied to every value in the input |

Nested Validation

Use children to validate nested objects and arrays:

const schema = {
  users: {
    type: 'array',
    required: true,
    min: 1,
    children: {
      '*': {                    // Every array element must match this rule
        type: 'object',
        children: {
          name: { type: 'string', required: true },
          role: { type: 'string', in: ['admin', 'user'] },
        },
      },
    },
  },
  settings: {
    type: 'object',
    children: {
      '@': { strict: false },   // Allow any keys in settings
      '#': { pattern: /^[a-z_]+$/ }, // But key names must be lowercase
      '*': { type: 'string' },  // And all values must be strings
    },
  },
} as const

Per-Type Rules

When a field accepts multiple types, use per_type to apply different constraints per type:

const schema = {
  value: {
    type: ['string', 'number'],
    per_type: {
      string: { min: 1, max: 255 },    // String length 1–255
      number: { min: 0, max: 1000 },    // Numeric value 0–1000
    },
  },
} as const

per_type accepts: in, in:public, min, max, pattern, special, transform, apply_transformed, children.

Schema Builder

Use ODValidatorRules.create() for fluent schema construction with full autocomplete:

import { ODValidatorRules, parse } from 'orange-dragonfly-validator'

const rules = ODValidatorRules.create()
  .property('name', p => p.required().string().min(1).max(100))
  .property('email', p => p.required().string().special('email'))
  .property('age', p => p.integer().min(0).max(150))
  .property('role', p => p.string().in(['admin', 'user']).default('user'))
  .strict()
  .complete()

const validator = new ODValidator(rules)
validator.validate(input)

The builder can also produce a raw schema for use with parse and safeParse:

const schema = ODValidatorRules.create()
  .property('name', p => p.required().string())
  .toSchema()

const data = parse(schema, input)

Nested Schemas with the Builder

Use .children() and .wildcard() for nested validation:

const rules = ODValidatorRules.create()
  .property('users', p => p.required().array().min(1)
    .children(c => c
      .wildcard(p => p.object()
        .children(c => c
          .property('name', p => p.required().string())
          .property('role', p => p.string().in(['admin', 'user']))
        )
      )
    )
  )
  .property('settings', p => p.object()
    .children(c => c
      .strict(false)
      .keyValidator(p => p.pattern(/^[a-z_]+$/))
      .wildcard(p => p.string().max(255))
    )
  )
  .complete()

Multi-Type Fields with the Builder

Use .perType() or chain multiple type methods:

const rules = ODValidatorRules.create()
  .property('value', p => p.string().number()
    .perType('string', p => p.min(1).max(255))
    .perType('number', p => p.min(0).max(1000))
  )
  .complete()

Builder API Reference

ODValidatorSchemaBuilder (returned by ODValidatorRules.create()):

| Method | Description | |--------|-------------| | .property(name, configure) | Add a named field rule | | .wildcard(configure) | Set the * (all values) rule | | .keyValidator(configure) | Set the # (key name) validator | | .strict(value?) | Set strict mode (default true) | | .toSchema() | Return the built ODValidatorRulesSchema | | .complete() | Return an ODValidatorRules instance |

ODValidatorPropertyBuilder (passed to configure callbacks):

| Method | Description | |--------|-------------| | .string(), .number(), .integer(), .boolean(), .array(), .object(), .null() | Add an allowed type | | .type(t) | Add type(s) by name | | .required() | Mark as required | | .default(value) | Set default value | | .min(n), .max(n) | Set min/max constraint | | .in(values) | Set allowed values whitelist | | .inPublic(values) | Control in values in error messages | | .pattern(p) | Set regex pattern | | .special(name) | Set built-in format validator | | .transform(fn) | Set transform function | | .applyTransformed() | Replace original with transformed value | | .children(configure) | Set nested schema (receives ODValidatorSchemaBuilder) | | .perType(type, configure) | Set type-specific overrides |

Type Inference

Schemas declared with as const (or as const satisfies ODValidatorRulesSchema) enable full type inference on validated data:

import { parse, type ODValidatorInfer, type ODValidatorRulesSchema } from 'orange-dragonfly-validator'

const schema = {
  name: { type: 'string', required: true },
  age: { type: 'integer' },
  status: { type: 'string', in: ['active', 'inactive'] },
  tags: {
    type: 'array',
    children: {
      '*': { type: 'string' },
    },
  },
} as const satisfies ODValidatorRulesSchema

// Extract the type without calling parse
type UserInput = ODValidatorInfer<typeof schema>
// {
//   name: string
//   age?: number
//   status?: 'active' | 'inactive'
//   tags?: string[]
// }

// parse() returns the same type
const data = parse(schema, input)
data.name    // string (required)
data.age     // number | undefined (optional)
data.status  // 'active' | 'inactive' | undefined
data.tags    // string[] | undefined

How Inference Works

  • required: true fields become required properties; all others are optional
  • type maps to its TypeScript equivalent ('integer' and 'number' both map to number)
  • in narrows the type to a literal union
  • children recurses for nested objects and arrays

Format Validators

Use the special property to apply built-in format validation:

const schema = {
  email:    { type: 'string', special: 'email' },
  website:  { type: 'string', special: 'url' },
  id:       { type: 'string', special: 'uuid' },
  phone:    { type: 'string', special: 'phone' },
  usPhone:  { type: 'string', special: 'us-phone' },
  ip:       { type: 'string', special: 'ipv4' },
  birthday: { type: 'string', special: 'date' },
  created:  { type: 'string', special: 'datetime' },
  color:    { type: 'string', special: 'hex-color' },
} as const

| Name | Format | Example | |------|--------|---------| | email | RFC 5321 | [email protected] | | phone | E.164 international | +12025551234 | | us-phone | US format (+1 + 10 digits) | +12025551234 | | url | HTTP/HTTPS with optional port/path | https://example.com:8080/path | | uuid | v1–5, case-insensitive | 550e8400-e29b-41d4-a716-446655440000 | | ipv4 | Four octets (0–255) | 192.168.1.1 | | date | ISO 8601 date | 2025-02-16 | | datetime | ISO 8601 with timezone | 2025-02-16T12:34:56Z | | hex-color | Hex (#RGB, #RRGGBB, #RRGGBBAA) | #FF00FF |

The underlying RegExp patterns are also exported individually (EMAIL_PATTERN, URL_PATTERN, etc.) and as the SPECIAL_VALIDATORS map for custom use.

Note: Built-in format validators use simplified regex patterns optimized for common cases. For example, the email validator covers standard addresses per RFC 5321 but does not support quoted local parts or comments. For domain-specific requirements, use pattern with your own regex.

Transforms

Use transform to modify values before validation. Combine with apply_transformed to include the transformed value in the output:

const schema = {
  email: {
    type: 'string',
    required: true,
    special: 'email',
    transform: (v) => typeof v === 'string' ? v.toLowerCase().trim() : v,
    apply_transformed: true,  // Output will contain the lowercased, trimmed email
  },
  score: {
    type: 'number',
    transform: (v) => typeof v === 'string' ? parseFloat(v) : v,
    apply_transformed: true,
  },
} as const

Without apply_transformed, the transform is only used for validation — the original value is preserved in the output.

Error Handling

Error Structure

Every validation error includes a machine-readable code, a human-readable message, and contextual parameters:

interface ODValidatorErrorEntry {
  code: string                        // e.g. 'TYPE_MISMATCH'
  message: string                     // e.g. 'Incorrect type: string required, number provided'
  params?: Record<string, unknown>    // e.g. { expected: 'string', actual: 'number' }
}

// Errors are keyed by field path
type ODValidatorErrors = Record<string, ODValidatorErrorEntry[]>
// e.g. { 'users.0.name': [{ code, message, params }] }

Error Codes

| Code | Meaning | |------|---------| | REQUIRED | Required field is missing | | NOT_ALLOWED | Field is not defined in schema (strict mode) | | TYPE_MISMATCH | Value type doesn't match type constraint | | MIN_VIOLATION | Value/length/count is below min | | MAX_VIOLATION | Value/length/count is above max | | PATTERN_MISMATCH | Value doesn't match pattern regex | | INVALID_FORMAT | Value doesn't match special format | | VALUE_NOT_IN_LIST | Value is not in the in whitelist | | ARRAY_ELEMENT_NOT_IN_LIST | Array contains elements not in in list | | CHILDREN_TYPE_ERROR | children applied to non-object/non-array |

Error codes are available as the ErrorCode constant for programmatic comparison.

Exception Types

import { ODValidatorException, ODValidatorRulesException } from 'orange-dragonfly-validator'

// ODValidatorException — input validation failed (expected at runtime)
try {
  parse(schema, badInput)
} catch (e) {
  if (e instanceof ODValidatorException) {
    e.details  // ODValidatorErrors — full structured errors
    e.info     // Record<string, string[]> — simplified (field → messages)
    e.message  // 'Validation failed'
  }
}

// ODValidatorRulesException — schema itself is invalid (programming error)
// Extends ODValidatorException. Thrown by parse, safeParse, and validateSchema.

safeParse catches ODValidatorException and returns { success: false, errors } instead — but still throws ODValidatorRulesException since invalid schemas are programming errors.

Custom Error Messages

Provide a messageFormatter to customize or localize error messages:

import { parse, type ODValidatorMessageFormatter } from 'orange-dragonfly-validator'

const messageFormatter: ODValidatorMessageFormatter = (code, params) => {
  switch (code) {
    case 'REQUIRED': return 'This field is required'
    case 'TYPE_MISMATCH': return `Expected ${params.expected}, got ${params.actual}`
    case 'MIN_VIOLATION': return `Must be at least ${params.min}`
    case 'MAX_VIOLATION': return `Must be at most ${params.max}`
    default: return 'Invalid value'
  }
}

const data = parse(schema, input, { messageFormatter })

JSON Schema Interop

Convert between ODValidator schemas and JSON Schema (draft-07 / 2020-12):

import { fromJsonSchema, toJsonSchema } from 'orange-dragonfly-validator'

// JSON Schema → ODValidator
const { schema, warnings } = fromJsonSchema({
  type: 'object',
  properties: {
    name: { type: 'string', minLength: 1 },
    age: { type: 'integer', minimum: 0 },
  },
  required: ['name'],
})
// warnings lists any unsupported features that were skipped

// ODValidator → JSON Schema
const jsonSchema = toJsonSchema(odvSchema)

Supported JSON Schema Features

type, properties, required, items, enum, const, default, minimum, maximum, exclusiveMinimum, exclusiveMaximum, minLength, maxLength, minItems, maxItems, pattern, format, additionalProperties, propertyNames.

Unsupported JSON Schema Features

$ref, oneOf, anyOf, allOf, not, if/then/else, dependencies, prefixItems, contains, patternProperties. These generate warnings during conversion and are silently skipped.

Schema Validation

Validate that a plain JSON object is a well-formed ODValidator schema (useful when schemas come from external sources):

import { validateSchema } from 'orange-dragonfly-validator'

try {
  const schema = validateSchema(jsonFromExternalSource)
  // schema is now typed as ODValidatorRulesSchema
} catch (e) {
  // ODValidatorRulesException — schema is malformed
}

API Reference

Functions

| Function | Signature | Description | |----------|-----------|-------------| | parse | parse<S>(schema, input, options?, errorsPrefix?): ODValidatorInfer<S> | Validate and return typed data. Throws ODValidatorException on failure | | safeParse | safeParse<S>(schema, input, options?, errorsPrefix?): SafeParseResult<S> | Like parse but returns { success, data } or { success, errors } instead of throwing | | validateSchema | validateSchema(json): ODValidatorRulesSchema | Validates a JSON object is a well-formed schema. Throws ODValidatorRulesException if not | | fromJsonSchema | fromJsonSchema(jsonSchema): { schema, warnings } | Convert JSON Schema to ODValidator schema | | toJsonSchema | toJsonSchema(schema): JsonSchema | Convert ODValidator schema to JSON Schema |

Classes

| Class | Description | |-------|-------------| | ODValidator | Core validator with validate(input) method, data/errors properties, and configurable strictMode/exceptionMode | | ODValidatorRules | Schema container with static validate() and normalize() methods | | ODValidatorRule | Single rule processor with apply() method | | ODValidatorException | Validation failure error with details (structured) and info (simplified) | | ODValidatorRulesException | Schema definition error (extends ODValidatorException) |

Types

| Type | Description | |------|-------------| | ODValidatorInfer<S> | Infers TypeScript type from a schema | | SafeParseResult<S> | Discriminated union: { success: true; data: ODValidatorInfer<S> } \| { success: false; errors: ODValidatorErrors } | | ODValidatorRulesSchema | Schema object type | | ODValidatorRuleSchema | Single rule definition type | | ODValidatorPerTypeRuleSchema | Per-type override rule type | | ODValidatorOptions | Constructor options for ODValidator | | ODValidatorErrors | Record<string, ODValidatorErrorEntry[]> | | ODValidatorErrorEntry | { code: string; message: string; params?: Record<string, unknown> } | | ODValidatorErrorCode | Union of all error code string literals | | ODValidatorMessageFormatter | (code: ODValidatorErrorCode, params: Record<string, unknown>) => string | | ODValidatorValueType | 'string' \| 'number' \| 'integer' \| 'array' \| 'object' \| 'boolean' \| 'function' \| 'null' | | JsonSchema | JSON Schema type definition | | FromJsonSchemaResult | { schema: ODValidatorRulesSchema; warnings: string[] } |

Architecture

Input + Schema
      │
      ▼
  parse() / safeParse()
      │
      ▼
  ODValidatorRules ─── normalizes schema (deep clone, type normalization)
      │
      ▼
  ODValidator.process()
      │
      ├── Apply wildcard ('*') rules to all values
      ├── Validate key names ('#') if defined
      ├── Apply defaults for missing fields
      ├── For each field:
      │     └── ODValidatorRule.applyRule()
      │           ├── transform (if defined)
      │           ├── type check
      │           ├── per_type overrides (if matched)
      │           ├── min / max constraints
      │           ├── in (whitelist) check
      │           ├── pattern / special format check
      │           └── children (recursive)
      ├── Enforce strict mode (reject undeclared keys)
      │
      ▼
  Typed data or structured errors
  • Fields are validated independently — errors are accumulated (not short-circuited)
  • Input is shallow-cloned; the original object is never mutated
  • Schema normalization adds 'integer' when 'number' is specified, and normalizes single types to arrays

Limitations

  • No cross-field dependencies — each field is validated independently. Use transform or multi-pass validation for dependent fields
  • No union schemasoneOf/anyOf/allOf from JSON Schema are not supported
  • No conditional logicif/then/else is not supported
  • No async validation — all validation is synchronous
  • No schema composition — no pick/omit/partial/merge utilities; compose schemas manually
  • No pattern complexity validationpattern values (RegExp or string) are used as-is. Schemas should be defined by developers, not constructed from end-user input. If you accept patterns from untrusted sources, validate them externally to prevent ReDoS attacks

License

ISC