UNPKG

n4s

Version:

typed schema validation version of enforce

133 lines (124 loc) 3.87 kB
import { assign } from 'vest-utils'; import { enforceEager } from './eager'; import { ctx } from './enforceContext'; import type { EnforceContext } from './enforceContext'; import { extendEnforce } from './extendLogic'; import { enforceLazy } from './lazy'; import type { RuleInstance } from './utils/RuleInstance'; /** * Context API for accessing validation context. * Allows accessing metadata and parent validation context during rule execution. */ export { ctx } from './enforceContext'; /** * Compose multiple validation rules into a single reusable rule. * Returns a composed rule that can be used in both eager and lazy validation. * * @example * ```typescript * // Compose separate rules that apply to the same value * const isValidUsername = compose( * enforce.isString() * .longerThan(3) * .shorterThan(20) * .matches(/^[a-zA-Z0-9_]+$/) * ); * * isValidUsername.test('john_doe'); // true * isValidUsername.test('ab'); // false (too short) * isValidUsername.test('john doe'); // false (contains space) * * // Use in schema validation * enforce({ username: 'john_doe' }).shape({ * username: isValidUsername * }); * ``` */ export { compose } from './compose'; type ExtendFn = (rules: Record<string, (...args: any[]) => any>) => void; type ContextFn = () => EnforceContext; type Enforce = typeof enforceEager & typeof enforceLazy & { extend: ExtendFn; context: ContextFn }; export namespace enforce { export type infer<R extends RuleInstance<any, any>> = R['infer']; } /** * Main validation function supporting both eager (imperative) and lazy (builder) APIs. * * **Eager API (Imperative):** * Immediately validates a value with chainable assertions that execute on call. * * **Lazy API (Builder Pattern):** * Builds a reusable validation rule without a value, returns a RuleInstance. * * @example * ```typescript * // Eager API - validates immediately * enforce('hello').isString().longerThan(3); * * // Lazy API - builds a reusable rule * const stringRule = enforce.isString(); * stringRule.test('hello'); // true * stringRule.run('hello'); // RuleRunReturn { pass: true, type: 'hello' } * * // Custom messages * enforce('').message('Field is required').isNotEmpty(); * * // Schema validation * enforce({ name: 'John', age: 30 }).shape({ * name: enforce.isString(), * age: enforce.isNumber() * }); * ``` */ export const enforce = assign(enforceEager, enforceLazy) as Enforce; /** * Access the current validation context. * Returns metadata and parent context information during rule execution. * * @returns The current EnforceContext or null if not in validation context * * @example * ```typescript * const context = enforce.context(); * console.log(context?.value); // Current value being validated * console.log(context?.meta); // Metadata attached to context * ``` */ enforce.context = function context(): EnforceContext { return ctx.use(); }; /** * Extend enforce with custom validation rules. * Custom rules become available on both eager and lazy APIs. * * @param rules - Object mapping rule names to validation functions * * @example * ```typescript * enforce.extend({ * isPositive: (value: number) => value > 0, * isBetween: (value: number, min: number, max: number) => * value >= min && value <= max * }); * * // Now use your custom rules * enforce(5).isPositive(); // eager API * const rule = enforce.isPositive(); // lazy API * * // With TypeScript, declare types: * declare global { * namespace n4s { * interface EnforceMatchers { * isPositive: (value: number) => boolean; * isBetween: (value: number, min: number, max: number) => boolean; * } * } * } * ``` */ enforce.extend = function extend( rules: Record<string, (...args: any[]) => any>, ) { extendEnforce(enforce, rules); };