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

@orkestrel/validator

v0.0.1

Published

Runtime validation toolkit for TypeScript

Readme

@orkestrel/validator

Runtime validation toolkit for TypeScript — zero dependencies, fully typed.

@orkestrel/validator gives you two independent tools that work together:

  • String validation — a declarative rule engine for validating user input. Define which rules apply, call validateInput, and get back a structured result with every failing rule and its message.
  • Type guards — a composable system for narrowing unknown values to precise TypeScript types at runtime. Over 60 built-in is* guards plus a full set of builder functions for constructing guards over objects, arrays, tuples, enums, and more.

Both axes are ESM-first, tree-shakeable, and produce no side effects.


Installation

npm install @orkestrel/validator

All exports are available from the package root:

import { validateInput, isString, objectOf } from '@orkestrel/validator'

String Validation

Use the rule engine when you need to validate free-form user input — form fields, query parameters, CLI arguments — and want structured error feedback.

Defining rules

Describe constraints as a plain object:

import type { ValidationRules } from '@orkestrel/validator'

const usernameRules: ValidationRules = {
	required: true,
	minimum: 3,
	maximum: 20,
	alphanumeric: true,
}

const emailRules: ValidationRules = {
	required: true,
	email: true,
}

const commentRules: ValidationRules = {
	required: true,
	minimum: 1,
	maximum: 500,
}

Validating input

import { validateInput } from '@orkestrel/validator'

const result = validateInput('Ada123', usernameRules)

if (result.valid) {
	// result.errors is []
} else {
	for (const error of result.errors) {
		console.log(error.rule, error.message)
	}
}

validateInput evaluates every active rule and collects all failures — it does not stop at the first one. The returned ValidationResult always has a valid boolean and an errors array.

Quick pass/fail check

When you only need a boolean and don't need the error list:

import { testInput } from '@orkestrel/validator'

if (testInput('Ada123', usernameRules)) {
	// input is valid
}

Built-in rules

| Rule | Value | Fails when | | -------------- | ------------------- | -------------------------------------------------------- | | required | true | The trimmed input is empty | | minimum | number ≥ 0 | input.length < minimum | | maximum | number ≥ 0 | input.length > maximum | | pattern | regex string | Input does not match the compiled pattern | | email | true | Input does not look like [email protected] | | url | true | Input does not begin with http:// or https:// | | numeric | true | Input is not an integer or decimal (optionally negative) | | integer | true | Input is not a whole integer (optionally negative) | | alphanumeric | true | Input contains anything other than letters and digits | | custom | ValidatorFunction | The function returns false or an error string |

Rules set to false or omitted are skipped. Rules are always evaluated in this order: required → minimum → maximum → pattern → email → url → numeric → integer → alphanumeric → custom

Default error messages

Each built-in rule produces a default message when it fails:

| Rule | Default message | | -------------- | ---------------------------------------- | | required | 'This field is required' | | minimum | 'Must be at least N characters' | | maximum | 'Must be at most N characters' | | pattern | 'Must match pattern: <pattern>' | | email | 'Must be a valid email address' | | url | 'Must be a valid URL' | | numeric | 'Must be a numeric value' | | integer | 'Must be an integer' | | alphanumeric | 'Must contain only letters and digits' |

Custom validators

Every rule slot accepts a ValidatorFunction instead of its canonical value type. The function replaces the built-in check entirely for that slot.

type ValidatorFunction = (input: string) => boolean | string

Return true when the input passes. Return false or a message string when it fails.

const rules: ValidationRules = {
	// Replace the built-in minimum check with custom logic
	minimum: (input) => input.startsWith('z') || 'Must start with z',

	// The custom slot has no built-in behaviour — always use a function
	custom: (input) => input !== 'forbidden' || 'That value is not allowed',
}

Using a function on a rule slot still uses that slot's position in the evaluation order — a minimum function runs in the minimum position.

Collecting multiple errors

const result = validateInput('', {
	required: true,
	minimum: 3,
	email: true,
})

// {
//   valid: false,
//   errors: [
//     { rule: 'required', message: 'This field is required' },
//     { rule: 'minimum',  message: 'Must be at least 3 characters' },
//     { rule: 'email',    message: 'Must be a valid email address' },
//   ]
// }

Validating rule objects at runtime

If rules arrive from an untrusted source (config file, API), assert them before use:

import { assertValidationRules } from '@orkestrel/validator'

assertValidationRules(untrustedRules) // throws ValidatorError when invalid
// now untrustedRules is narrowed to ValidationRules

assertValidationRules checks:

  • All keys are supported rule names
  • All values are the correct kind for their rule (boolean for flag rules, number ≥ 0 for minimum/maximum, string for pattern)
  • minimum ≤ maximum when both are provided as numbers
  • pattern compiles as a valid regular expression

ValidatorError

Invalid rule configuration throws a ValidatorError — a subclass of Error with two extra fields:

import { ValidatorError, isValidatorError } from '@orkestrel/validator'

try {
	assertValidationRules({ minimum: 10, maximum: 3 })
} catch (error) {
	if (isValidatorError(error)) {
		console.log(error.code) // 'INVALID_RULE_VALUE'
		console.log(error.message) // 'Minimum cannot be greater than maximum'
		console.log(error.context.rule) // 'minimum'
		console.log(error.context.value) // 10
	}
}

The three error codes:

| Code | When thrown | | -------------------- | -------------------------------------------------------------------- | | INVALID_RULES | The rule object has unsupported keys or wrong value kinds | | INVALID_RULE_VALUE | A numeric rule is out of range (minimum > maximum, negative value) | | INVALID_PATTERN | The pattern string is not a valid regular expression |

Use isValidatorError in catch blocks to distinguish package errors from unexpected throws.


Type Guards

Use type guards when you need to narrow unknown values — incoming API responses, JSON.parse output, event payloads, dynamic config — to specific TypeScript types.

All guards follow the same contract:

// Returns true and narrows the type — never throws
isString(value) // value is string
isNumber(value) // value is number

Primitive guards

import {
	isNull,
	isUndefined,
	isDefined,
	isString,
	isNumber,
	isBoolean,
	isBigInt,
	isSymbol,
	isFunction,
	isDate,
	isRegExp,
	isError,
	isPromise,
	isPromiseLike,
	isIterable,
	isAsyncIterator,
	isArrayBuffer,
	isSharedArrayBuffer,
} from '@orkestrel/validator'

| Guard | Narrows to | Notes | | ------------------------ | ------------------------ | ----------------------------------------- | | isNull(v) | null | Strict === null | | isUndefined(v) | undefined | Strict === undefined | | isDefined(v) | T | Excludes both null and undefined | | isString(v) | string | typeof === 'string' | | isNumber(v) | number | Includes NaN and Infinity | | isBoolean(v) | boolean | | | isBigInt(v) | bigint | | | isSymbol(v) | symbol | | | isFunction(v) | AnyFunction | typeof === 'function' | | isDate(v) | Date | instanceof Date | | isRegExp(v) | RegExp | instanceof RegExp | | isError(v) | Error | instanceof Error | | isPromise(v) | Promise<T> | Native instanceof Promise only | | isPromiseLike(v) | Promise<T> or thenable | Checks then, catch, finally methods | | isIterable(v) | Iterable<T> | Strings are iterable | | isAsyncIterator(v) | AsyncIterable<T> | Checks Symbol.asyncIterator | | isArrayBuffer(v) | ArrayBuffer | | | isSharedArrayBuffer(v) | SharedArrayBuffer | Feature-checks availability first |

const raw: unknown = JSON.parse(text)

if (isString(raw)) {
	// raw: string
}

// isDefined removes both null and undefined
function process<T>(value: T | null | undefined) {
	if (isDefined(value)) {
		// value: T
	}
}

Object and collection guards

import { isObject, isRecord, isMap, isSet, isWeakMap, isWeakSet } from '@orkestrel/validator'

| Guard | Narrows to | Notes | | -------------- | -------------------------- | ------------------------------------- | | isObject(v) | object | Any non-null object, including arrays | | isRecord(v) | Record<string, unknown> | Non-null, non-array object | | isMap(v) | ReadonlyMap<K, V> | instanceof Map | | isSet(v) | ReadonlySet<T> | instanceof Set | | isWeakMap(v) | WeakMap<object, unknown> | | | isWeakSet(v) | WeakSet<object> | |

isRecord is the right choice for plain objects — it excludes arrays:

isObject([]) // true  — arrays are objects
isRecord([]) // false — arrays excluded
isRecord({}) // true
isRecord(null) // false

Array and typed array guards

import {
	isArray,
	isDataView,
	isArrayBufferView,
	isUint8Array,
	isInt32Array,
	isFloat64Array,
	// ... all 11 typed-array guards
} from '@orkestrel/validator'

isArray wraps Array.isArray. For element-level checking use arrayOf from the guard builders section.

isArrayBufferView accepts any typed array or DataView via ArrayBuffer.isView.

All 11 typed-array guards: isInt8Array, isUint8Array, isUint8ClampedArray, isInt16Array, isUint16Array, isInt32Array, isUint32Array, isFloat32Array, isFloat64Array, isBigInt64Array, isBigUint64Array.

Emptiness guards

import {
	isEmptyString,
	isEmptyArray,
	isEmptyObject,
	isEmptyMap,
	isEmptySet,
	isNonEmptyString,
	isNonEmptyArray,
	isNonEmptyObject,
	isNonEmptyMap,
	isNonEmptySet,
} from '@orkestrel/validator'

Empty:

| Guard | Narrows to | | ------------------ | --------------------------------- | | isEmptyString(v) | '' | | isEmptyArray(v) | readonly [] | | isEmptyObject(v) | Record<string \| symbol, never> | | isEmptyMap(v) | ReadonlyMap<never, never> | | isEmptySet(v) | ReadonlySet<never> |

Non-empty:

| Guard | Narrows to | | --------------------- | ----------------------------------- | | isNonEmptyString(v) | string (length > 0) | | isNonEmptyArray(v) | readonly [T, ...T[]] | | isNonEmptyObject(v) | Record<string \| symbol, unknown> | | isNonEmptyMap(v) | ReadonlyMap<K, V> | | isNonEmptySet(v) | ReadonlySet<T> |

isEmptyObject counts both string keys and enumerable symbol keys — an object with only a symbol property is not considered empty:

isEmptyObject({}) // true
isEmptyObject({ [Symbol('x')]: true }) // false

Function guards

import {
	isAsyncFunction,
	isGeneratorFunction,
	isAsyncGeneratorFunction,
	isZeroArg,
	isZeroArgAsync,
	isZeroArgGenerator,
	isZeroArgAsyncGenerator,
} from '@orkestrel/validator'

| Guard | Passes when | | ------------------------------ | ---------------------------------- | | isZeroArg(fn) | fn.length === 0 | | isAsyncFunction(fn) | Native async function | | isGeneratorFunction(fn) | function* | | isAsyncGeneratorFunction(fn) | async function* | | isZeroArgAsync(fn) | Async and zero arguments | | isZeroArgGenerator(fn) | Generator and zero arguments | | isZeroArgAsyncGenerator(fn) | Async generator and zero arguments |

isAsyncFunction(async () => true) // true
isAsyncFunction(() => Promise.resolve(true)) // false — sync, returns a Promise
isGeneratorFunction(function* () {
	yield 1
}) // true

Note: Detection uses constructor.name. Minifiers that rename function constructors will break these guards.


Guard Builders

Guard builders compose primitive guards into guards for complex shapes. They are the primary API for narrowing structured data like API responses.

Building object schemas — objectOf

import { objectOf, isString, isNumber } from '@orkestrel/validator'

const isUser = objectOf({ id: isString, age: isNumber })

const raw: unknown = await fetchUser()
if (isUser(raw)) {
	// raw: Readonly<{ id: string; age: number }>
	console.log(raw.id, raw.age)
}

objectOf is exact by default: it rejects any value with extra string keys not in the shape. Symbol keys on the value are silently ignored.

Optional keys

Pass an array of key names to make those keys optional:

const isUser = objectOf(
	{ id: isString, role: isString, note: isString },
	['note'], // note may be absent
)

isUser({ id: 'u1', role: 'admin' }) // true
isUser({ id: 'u1', role: 'admin', note: 'hi' }) // true
isUser({ id: 'u1', role: 'admin', note: 1 }) // false — note fails its guard

Pass true to make every key optional:

const isPartialUser = objectOf({ id: isString, age: isNumber }, true)
isPartialUser({}) // true
isPartialUser({ id: 'u1' }) // true

recordOf

Identical signature to objectOf. Use recordOf when the value is a dictionary-style record rather than a structured entity — purely a naming convention for readability.

Arrays — arrayOf

import { arrayOf, isString } from '@orkestrel/validator'

const isStrings = arrayOf(isString)
isStrings(['a', 'b']) // true
isStrings(['a', 1]) // false

Tuples — tupleOf

Validates a fixed-length array with per-index guards:

import { tupleOf, isString, isNumber } from '@orkestrel/validator'

const isEntry = tupleOf(isString, isNumber)
isEntry(['id', 42]) // true
isEntry(['id', 'x']) // false — second element fails
isEntry(['id']) // false — length mismatch

Literals and enums — literalOf, enumOf

import { literalOf, enumOf } from '@orkestrel/validator'

// Narrow to a fixed set of literal values
const isRole = literalOf('user', 'admin', 'guest')
isRole('admin') // true
isRole('owner') // false

// Narrow to a TypeScript enum
enum Status {
	Idle,
	Busy,
}
const isStatus = enumOf(Status)
isStatus(Status.Idle) // true
isStatus(0) // true  — numeric enum value
isStatus('Idle') // false — reverse-mapped names are not values

// String enum
const isFlag = enumOf({ active: 'ACTIVE', paused: 'PAUSED' })
isFlag('ACTIVE') // true
isFlag('active') // false — keys are not values

Sets and Maps — setOf, mapOf

import { setOf, mapOf, isString, isNumber } from '@orkestrel/validator'

const isTagSet = setOf(isString)
const isScores = mapOf(isString, isNumber)

isTagSet(new Set(['a', 'b'])) // true
isTagSet(new Set(['a', 1])) // false
isScores(new Map([['alice', 98]])) // true
isScores(new Map([['alice', '98']])) // false

Iterables — iterableOf

Accepts any value with Symbol.iterator — arrays, sets, generators — and validates every yielded element:

import { iterableOf, isNumber } from '@orkestrel/validator'

const isNumbers = iterableOf(isNumber)
isNumbers([1, 2, 3]) // true
isNumbers(new Set([1, 2])) // true
isNumbers([1, '2']) // false

Keys and instance checks — keyOf, instanceOf

import { keyOf, instanceOf } from '@orkestrel/validator'

// Accept only keys present on a specific object
const COLORS = { red: '#f00', blue: '#00f', green: '#0f0' } as const
const isColorKey = keyOf(COLORS)
isColorKey('red') // true
isColorKey('yellow') // false

// Accept instances of a class
class ApiError extends Error {
	constructor(
		readonly status: number,
		message: string,
	) {
		super(message)
	}
}
const isApiError = instanceOf(ApiError)
isApiError(new ApiError(404, 'Not found')) // true
isApiError(new Error('generic')) // false

Shape transforms — pickOf, omitOf

Build a new guard shape by selecting or removing keys from an existing one. The result is a GuardsShape — pass it to objectOf or recordOf.

import { pickOf, omitOf, objectOf, isString, isNumber } from '@orkestrel/validator'

const userShape = { id: isString, age: isNumber, name: isString, role: isString }

// Keep only the keys you need
const isPublicUser = objectOf(pickOf(userShape, ['id', 'name']))
isPublicUser({ id: 'u1', name: 'Ada' }) // true
isPublicUser({ id: 'u1', name: 'Ada', age: 30 }) // false — exact shape, extra key rejected

// Remove keys you don't want
const isUserWithoutAge = objectOf(omitOf(userShape, ['age']))
isUserWithoutAge({ id: 'u1', name: 'Ada', role: 'admin' }) // true

Logical Combinators

Refinement — whereOf

The most common combinator. Refines a base guard with an additional predicate. The predicate receives a typed value (the base guard already narrowed it), so TypeScript provides full autocomplete.

import { whereOf, isString, testInput } from '@orkestrel/validator'

// Simple predicate
const isNonEmptyString = whereOf(isString, (value) => value.length > 0)

// Embed a rule check inside a guard
const usernameRules = { required: true, minimum: 3, maximum: 20, alphanumeric: true }
const isUsername = whereOf(isString, (value) => testInput(value, usernameRules))

AND — andOf

import { andOf, isString } from '@orkestrel/validator'

const isNonEmptyString = andOf(isString, (value: string) => value.length > 0)

OR — orOf

import { orOf, isString, isNumber } from '@orkestrel/validator'

const isStringOrNumber = orOf(isString, isNumber)

NOT — notOf

import { notOf, isString } from '@orkestrel/validator'

const isNotString = notOf(isString)

Union — unionOf

Variadic OR across any number of guards:

import { unionOf, literalOf } from '@orkestrel/validator'

const isDirection = unionOf(
	literalOf('north'),
	literalOf('south'),
	literalOf('east'),
	literalOf('west'),
)

Intersection — intersectionOf and composedOf

Both require every guard to pass. Use intersectionOf for true type intersections; use composedOf when layering constraints on the same base type:

import { intersectionOf, composedOf, isString } from '@orkestrel/validator'

const isShortString = intersectionOf(
	isString,
	(value: unknown): value is string => isString(value) && value.length < 10,
)

// composedOf reads more clearly when all guards share the same type
const isAlphaCode = composedOf(
	(value: unknown): value is string => isString(value) && /^[A-Za-z]+$/.test(value),
	(value: unknown): value is string => isString(value) && value.length === 2,
)
isAlphaCode('en') // true
isAlphaCode('en1') // false

Complement — complementOf

Subtracts a narrower type from a broader one — passes when the value satisfies the base guard but not the excluded guard:

import { complementOf, unionOf, objectOf, literalOf, isNumber } from '@orkestrel/validator'

const isCircle = objectOf({ kind: literalOf('circle'), radius: isNumber })
const isRectangle = objectOf({ kind: literalOf('rectangle'), width: isNumber, height: isNumber })
const isShape = unionOf(isCircle, isRectangle)
const isNotCircle = complementOf(isShape, isCircle)

isNotCircle({ kind: 'rectangle', width: 2, height: 3 }) // true
isNotCircle({ kind: 'circle', radius: 3 }) // false

Nullable — nullableOf

Extends any guard to also accept null:

import { nullableOf, isString } from '@orkestrel/validator'

const isMaybeString = nullableOf(isString)
isMaybeString(null) // true
isMaybeString('value') // true
isMaybeString(undefined) // false

Transform — transformOf

Validates the original input by projecting it and checking the projected value. The original (not the projection) is what gets narrowed:

import { transformOf, isString, isNumber } from '@orkestrel/validator'

// Accept strings with a positive character count
const isNonEmptyString = transformOf(
	isString,
	(value) => value.length,
	(n): n is number => typeof n === 'number' && n > 0,
)
isNonEmptyString('abc') // true
isNonEmptyString('') // false

Lazy — lazyOf

Defers guard creation until first call. Required when building guards for recursive types, because the guard variable isn't defined yet at the point of the objectOf call:

import type { Guard } from '@orkestrel/validator'
import { lazyOf, objectOf, arrayOf, isNumber } from '@orkestrel/validator'

interface Tree {
	readonly value: number
	readonly children?: readonly Tree[]
}

const isTree: Guard<Tree> = lazyOf(() =>
	objectOf(
		{
			value: isNumber,
			children: arrayOf(isTree), // safe — isTree is resolved at call time
		},
		['children'],
	),
)

isTree({ value: 1, children: [{ value: 2 }] }) // true
isTree({ value: 'x' }) // false

Combining Guards and Validation

The two axes compose naturally. Use whereOf with testInput to embed field-level validation inside a structural guard:

import {
	objectOf,
	arrayOf,
	literalOf,
	isString,
	whereOf,
	testInput,
	validateInput,
} from '@orkestrel/validator'

const usernameRules = { required: true, minimum: 3, maximum: 20, alphanumeric: true }
const emailRules = { required: true, email: true }

const isSignupForm = objectOf(
	{
		username: whereOf(isString, (v) => testInput(v, usernameRules)),
		email: whereOf(isString, (v) => testInput(v, emailRules)),
		role: literalOf('user', 'admin'),
		tags: arrayOf(whereOf(isString, (v) => v.length > 0)),
	},
	['tags'], // tags is optional
)

const payload: unknown = JSON.parse(request.body)

if (isSignupForm(payload)) {
	// payload is fully typed:
	// {
	//   username: string
	//   email:    string
	//   role:     'user' | 'admin'
	//   tags?:    readonly string[]
	// }
	submit(payload)
} else {
	// Get per-field error details with validateInput
	const usernameResult = validateInput(isString(payload) ? payload : '', usernameRules)
	return { errors: usernameResult.errors }
}

TypeScript Types

These types are exported for use in your own function signatures.

Guard types

import type { Guard, GuardType, GuardsShape } from '@orkestrel/validator'

// Guard<T> — the type predicate signature
const isRole: Guard<'user' | 'admin'> = literalOf('user', 'admin')

// GuardType — extract T from a Guard<T>
type Role = GuardType<typeof isRole> // 'user' | 'admin'

// GuardsShape — a map of string keys to guards, input to objectOf / recordOf
const userShape: GuardsShape = { id: isString, age: isNumber }

Output types from shapes

import type { FromGuards, OptionalFromGuards, AllOptionalFromGuards } from '@orkestrel/validator'

const userShape = { id: isString, age: isNumber, note: isString }

// All keys required
type User = FromGuards<typeof userShape>
// Readonly<{ id: string; age: number; note: string }>

// Selected keys optional
type FlexUser = OptionalFromGuards<typeof userShape, ['note']>
// Readonly<{ id: string; age: number; note?: string }>

// All keys optional
type PartialUser = AllOptionalFromGuards<typeof userShape>
// Readonly<Partial<{ id: string; age: number; note: string }>>

Validation types

import type {
	ValidationRules,
	ValidationResult,
	ValidationError,
	ValidationRuleName,
	ValidatorFunction,
	ValidatorErrorCode,
	ValidatorErrorContext,
} from '@orkestrel/validator'

Function type aliases

import type {
	AnyFunction, // (...args: unknown[]) => unknown
	AnyAsyncFunction, // (...args: unknown[]) => Promise<unknown>
	ZeroArgFunction, // () => unknown
	ZeroArgAsyncFunction, // () => Promise<unknown>
	AnyConstructor, // new (...args: never[]) => T
} from '@orkestrel/validator'

Low-level APIs

These are exported for advanced use cases but most consumers won't need them directly.

evaluateRule

Evaluate a single rule against a string without constructing a full rule set:

import { evaluateRule } from '@orkestrel/validator'

evaluateRule('required', true, '') // 'This field is required'
evaluateRule('required', true, 'x') // undefined (passed)
evaluateRule('minimum', 3, 'ab') // 'Must be at least 3 characters'

cloneValidationRules

Shallow-clone a rule object (validates it first):

import { cloneValidationRules } from '@orkestrel/validator'

const copy = cloneValidationRules({ required: true, minimum: 3 })

createEnumValues

Extract the runtime values from a TypeScript enum. Numeric enum reverse-mapping is handled automatically:

import { createEnumValues } from '@orkestrel/validator'

createEnumValues({ idle: 'IDLE', busy: 'BUSY' }) // ['IDLE', 'BUSY']

enum Status {
	Idle,
	Busy,
}
createEnumValues(Status) // [0, 1] — not ['Idle', 'Busy']

createShapeGuard

The factory behind objectOf and recordOf. Prefer those functions in application code:

import { createShapeGuard } from '@orkestrel/validator'

const isUser = createShapeGuard({ id: isString, age: isNumber })
const isFlexUser = createShapeGuard({ id: isString, note: isString }, ['note'])
const isPartialUser = createShapeGuard({ id: isString, age: isNumber }, true)

Domain-level type guards

Guards for the validation domain types themselves — useful when processing rule objects or results from unknown sources:

import {
	isValidationRuleName, // 'required' | 'minimum' | ...
	isValidationRules, // ValidationRules
	isValidationError, // ValidationError
	isValidationResult, // ValidationResult
	isValidatorFunction, // ValidatorFunction
} from '@orkestrel/validator'

isValidationRuleName('required') // true
isValidationRuleName('unknown') // false
isValidationRules({ required: true, minimum: 3 }) // true
isValidationRules({ required: 'yes' }) // false
isValidationError({ rule: 'required', message: 'This field...' }) // true
isValidationResult({ valid: true, errors: [] }) // true

recordValue

Read a property from an object by any key type (including symbols):

import { recordValue } from '@orkestrel/validator'

const sym = Symbol('id')
const obj = { name: 'Ada', [sym]: 42 }

recordValue(obj, 'name') // 'Ada'
recordValue(obj, sym) // 42
recordValue(obj, 'missing') // undefined

Built-in regex constants

import {
	EMAIL_PATTERN, // /^[^\s@]+@[^\s@]+\.[^\s@]+$/
	URL_PATTERN, // /^https?:\/\/.+/
	NUMERIC_PATTERN, // /^-?\d+(\.\d+)?$/
	INTEGER_PATTERN, // /^-?\d+$/
	ALPHANUMERIC_PATTERN, // /^[a-zA-Z0-9]+$/
	VALIDATION_RULE_NAMES, // ordered list of all rule names
} from '@orkestrel/validator'

Import these when building custom validators that should match the built-in rule semantics exactly.