Skip to content

Rules & Registry

The registry is the central container for all your validation rules. It provides a clean way to organize, access, and compose rules.

Creating a Registry

ts
import { createRegistry } from '@pliant/core';

const registry = createRegistry();

The registry is a simple object that holds rule definitions keyed by name.

Adding Rules

Use addRules to register one or more rules:

ts
import { addRules, requiredRule, emailRule, lengthRule } from '@pliant/core';

addRules(registry, {
  required: requiredRule(),
  email: emailRule(),
  username: lengthRule({ min: 3, max: 20 })
});

Rule Factories

Pliant provides factory functions for all built-in rules. These functions accept default options:

ts
// No options needed
requiredRule()

// With options
lengthRule({ min: 3, max: 20 })
rangeRule({ min: 0, max: 100, inclusive: true })
regexRule({ pattern: /^[a-z]+$/ })

Rule Definition Structure

Every rule is an object with this shape:

ts
interface RuleDef<TOptions, TValue> {
  // Default options for this rule
  options?: TOptions;
  
  // Default message (string or function)
  message?: string | ((detail: any) => string);
  
  // Parent rule to inherit from
  inherit?: string;
  
  // Sync validation function
  validate?: (value: TValue, context: Context, options: TOptions) => ErrorDetail | null;
  
  // Async validation function
  validateAsync?: (value: TValue, context: Context, options: TOptions) => Promise<ErrorDetail | null>;
}

Using Rules

Basic Usage

ts
import { evaluateRules } from '@pliant/core';

const errors = evaluateRules(
  registry,
  'test@example.com',  // value
  { field: 'email' },  // context
  ['required', 'email'] // rule names
);
// null (valid)

With Options Override

Pass a rule reference object to override options:

ts
const errors = evaluateRules(
  registry,
  'ab',
  { field: 'username' },
  [
    'required',
    { name: 'username', options: { min: 5 } }  // Override min
  ]
);
// { username: { code: 'length', min: 5, max: 20, actual: 2 } }

Context Object

The context provides information about the current validation:

ts
interface Context {
  // Current field name
  field?: string;
  
  // All form data (for cross-field validation)
  data?: Record<string, any>;
  
  // Custom context data
  [key: string]: any;
}

Cross-Field Validation

Use context for rules that reference other fields:

ts
const errors = evaluateRules(
  registry,
  'different-password',
  { 
    field: 'confirmPassword',
    data: { password: 'my-password', confirmPassword: 'different-password' }
  },
  [{ name: 'equals', options: { field: 'password' } }]
);
// { equals: { code: 'equals' } }

Multiple Registries

You can create multiple registries for different contexts:

ts
const formRegistry = createRegistry();
const apiRegistry = createRegistry();

// Different rules for different contexts
addRules(formRegistry, {
  email: emailRule(),
  password: lengthRule({ min: 8 })
});

addRules(apiRegistry, {
  email: emailRule(),
  apiKey: regexRule({ pattern: /^sk_[a-z0-9]{32}$/ })
});

Released under the MIT License.