UNPKG

n4s

Version:

typed schema validation version of enforce

86 lines (78 loc) 2.2 kB
import { StringObject, assign, invariant, mapFirst } from 'vest-utils'; import { ctx } from './enforceContext'; import { RuleInstance } from './utils/RuleInstance'; import { RuleRunReturn } from './utils/RuleRunReturn'; type ComposeResult<T = any> = RuleInstance<T, [T]> & { (value: T): void; }; /** * Composes multiple validation rules into a single reusable rule. * The composed rule executes rules in order and fails on the first failing rule. * Returns a RuleInstance that can be used with both eager and lazy APIs. * * @template T - The type of value to validate * @param composites - Validation rules to compose * @returns A composed rule that can be run with values or called directly * * @example * ```typescript * // Create a reusable adult age validation * const isAdult = compose( * enforce.isNumber(), * enforce.greaterThanOrEquals(18), * enforce.lessThan(150) * ); * * // Use with lazy API * isAdult.test(25); // true * isAdult.test(16); // false * * // Use with eager API * enforce(30).run(isAdult); // passes * * // Call directly (throws on failure) * isAdult(25); // ok * isAdult(16); // throws * * // Compose with other rules * const userSchema = enforce.shape({ * age: isAdult, * name: enforce.isString() * }); * ``` */ export function compose<T = any>( ...composites: RuleInstance<any, [any]>[] ): ComposeResult<T> { const composedFn = assign( (value: T) => { const res = run(value); invariant(res.pass, StringObject(res.message)); }, { run, test: (value: T) => run(value).pass, infer: {} as T, }, ); return composedFn as ComposeResult<T>; function run(value: T): RuleRunReturn<T> { return ctx.run({ value }, () => { let result: RuleRunReturn<T> = RuleRunReturn.Passing(value); mapFirst( composites, ( composite: RuleInstance<any, [any]>, breakout: (conditional: boolean, res: RuleRunReturn<any>) => void, ) => { const res = composite.run(value); if (!res.pass) { result = res; breakout(true, res); } }, ); return result; }); } }