UNPKG

n4s

Version:

typed schema validation version of enforce

134 lines (116 loc) 3.38 kB
/* eslint-disable max-statements */ /* eslint-disable max-lines-per-function */ import { dynamicValue, type DynamicValue, type Maybe, type Stringable, } from 'vest-utils'; import { StandardSchemaV1 } from 'vest-utils/standardSchemaSpec'; import { RuleInstance } from '../../utils/RuleInstance'; import { executeChain, type Predicate } from './chainExecutor'; import { createChainProxyHandlers } from './proxyHandlers'; export type RuleFunctions<T extends RuleInstance<any, any>> = Record< keyof Omit<T, 'infer' | 'test' | 'validate' | 'parse' | '~standard'>, (...args: any[]) => boolean | ReturnType<Predicate> >; type LazyMessage = DynamicValue< string, [value: unknown, originalMessage?: Stringable] >; /** * Creates a chain builder for rule validation. * Provides methods to add predicates, run validation, and apply custom messages. * Implements StandardSchema v1 support. */ export function createChainBuilder<T extends RuleInstance<any, any>>( rules: RuleFunctions<T> | Record<string, (...args: any[]) => any>, ) { const chain: Predicate[] = []; const target: Partial<T> = {}; let lazyMessage: Maybe<LazyMessage> = undefined; const add = (p: Predicate): T => { chain.push(p); return proxy; }; const prepend = (p: Predicate): T => { chain.unshift(p); return proxy; }; const resolveMessage = ( result: ReturnType<typeof executeChain>, value: unknown, ): string => { const defaultMessage = result.message || 'Validation failed'; if (!lazyMessage) { return defaultMessage; } return dynamicValue(lazyMessage, value, result.message) ?? defaultMessage; }; const validate: T['validate'] = ((...args: any[]) => { const result = executeChain(chain, args[0]); if (result.pass) { return { value: result.type }; } return { issues: [ { message: resolveMessage(result, args[0]), path: result.path || [], }, ], }; }) as T['validate']; const test: T['test'] = ((...args: any[]) => { const result = validate(...args); return !result.issues; }) as T['test']; // Internal compatibility method - converts StandardSchema Result to RuleRunReturn const parse: T['parse'] = ((...args: any[]) => { const result = validate(...args); if (!result.issues) { return result.value; } const [firstIssue] = result.issues; throw new TypeError(firstIssue?.message || 'Validation failed'); }) as T['parse']; const run: T['run'] = ((...args: any[]) => { const result = executeChain(chain, args[0]); if (!result.pass && lazyMessage) { return { ...result, message: dynamicValue(lazyMessage, args[0], result.message) ?? result.message, }; } return result; }) as T['run']; const message = (msg: Stringable): T => { if (msg) { lazyMessage = msg; } return proxy; }; const proxy: T = new Proxy( target as T, createChainProxyHandlers(rules, { '~standard': { types: { input: undefined!, output: undefined!, }, validate, vendor: 'n4s', version: 1 as const, } as StandardSchemaV1.Props<any, any>, add, message, parse, prepend, run, test, validate, }), ); return { add, proxy } as const; }