UNPKG

@resk/core

Version:

An innovative TypeScript framework that empowers developers to build applications with a fully decorator-based architecture for efficient resource management. By combining the power of decorators with a resource-oriented design, DecorRes enhances code cla

1,068 lines 108 kB
import { IClassConstructor, IMakeOptional } from "../types"; import { I18n } from "../i18n"; import { IValidatorDefaultMultiRule, IValidatorMultiRuleFunction, IValidatorMultiRuleNames, IValidatorNestedRuleFunctionOptions, IValidatorRegisteredRules, IValidatorResult, IValidatorRule, IValidatorRuleFunction, IValidatorRuleName, IValidatorRuleParams, IValidatorRules, IValidatorSanitizedRules, IValidatorValidateFailure, IValidatorValidateMultiRuleOptions, IValidatorValidateOptions, IValidatorValidateResult, IValidatorValidateSuccess, IValidatorValidateTargetOptions, IValidatorValidateTargetResult } from "./types"; /** * # Validator Class * * A comprehensive validation system that provides flexible and powerful validation capabilities * for TypeScript/JavaScript applications. This class supports both synchronous and asynchronous * validation, decorator-based validation for classes, and a rich ecosystem of validation rules. * * ## Key Features * - **Type-Safe Validation**: Full TypeScript support with generic types * - **Decorator Support**: Class property validation using decorators * - **Async Validation**: Support for asynchronous validation rules * - **Internationalization**: Built-in i18n support for error messages * - **Extensible**: Easy to register custom validation rules * - **Rule Composition**: Combine multiple validation rules * * ## Basic Usage * ```typescript * // Register a custom validation rule * Validator.registerRule('CustomRule', ({ value }) => { * return value > 10 || 'Value must be greater than 10'; * }); * * // Validate a single value * const result = await Validator.validate({ * value: 15, * rules: ['Required', 'CustomRule'] * }); * * // Use with decorators * class User { * @IsRequired * @IsEmail * email: string; * * @IsRequired * @MinLength([3]) * name: string; * } * * const userData = { email: 'user@example.com', name: 'John' }; * const validated = await Validator.validateTarget(User, userData); * ``` * * ## Advanced Usage * ```typescript * // Complex validation with context * const validationOptions = { * value: userData, * rules: [ * 'required', * { minLength: [5] }, * async ({ value, context }) => { * const exists = await checkIfUserExists(value); * return !exists || 'User already exists'; * } * ], * context: { userId: 123 } * }; * * try { * const result = await Validator.validate(validationOptions); * console.log('Validation passed!', result); * } catch (error) { * console.error('Validation failed:', error.message); * } * ``` * * @author Resk Framework Team * @since 1.0.0 * @version 2.1.0 * @see {@link https://docs.resk.dev/validation | Validation Documentation} * @public */ export declare class Validator { /** * ## Metadata Storage Key * * Private symbol used to store validation rules in metadata. This ensures * that the validation rules don't conflict with other metadata keys. * * @private * @readonly * @since 1.0.0 */ private static readonly RULES_METADATA_KEY; /** * ## Mark Rule With Symbol * * Marks a rule function with a specific marker symbol. Used internally to mark * decorators (OneOf, AllOf, ArrayOf, ValidateNested) for reliable identification * even in minified code. Symbols survive minification while function names do not. * * @param ruleFunc - The rule function to mark * @param marker - The marker symbol to apply * @internal */ static markRuleWithSymbol(ruleFunc: any, marker: symbol): void; /** * ## Register Validation Rule * * Registers a new custom validation rule that can be used throughout the application. * This method provides type-safe registration of validation functions with proper * error handling and validation of input parameters. * * ### Type Parameters * - `ParamType` - Array type defining the parameters the rule function accepts * - `Context` - Type of the validation context object passed to the rule * * ### Rule Function Signature * ```typescript * type RuleFunction<ParamType, Context> = (options: { * value: any; * ruleParams: ParamType; * context?: Context; * fieldName?: string; * translatedPropertyName?: string; * }) => boolean | string | Promise<boolean | string>; * ``` * * ### Rule Return Values * - `true` - Validation passed * - `false` - Validation failed (uses default error message) * - `string` - Validation failed with custom error message * - `Promise<boolean|string>` - Async validation * * @example * ```typescript * // Simple synchronous rule * Validator.registerRule('MinValue', ({ value, ruleParams }) => { * const [minValue] = ruleParams; * return value >= minValue || `Value must be at least ${minValue}`; * }); * * // Async rule with database check * Validator.registerRule('UniqueEmail', async ({ value, context }) => { * const exists = await database.user.findByEmail(value); * return !exists || 'Email address is already taken'; * }); * * // Rule with multiple parameters * Validator.registerRule('Between', ({ value, ruleParams }) => { * const [min, max] = ruleParams; * return (value >= min && value <= max) || * `Value must be between ${min} and ${max}`; * }); * * // Rule with context * Validator.registerRule('DifferentFrom', ({ value, ruleParams, context }) => { * const [fieldName] = ruleParams; * const otherValue = context?.data?.[fieldName]; * return value !== otherValue || * `Must be different from ${fieldName}`; * }); * ``` * * @template ParamType - Array type for rule parameters * @template Context - Type for validation context * * @param ruleName - Unique identifier for the validation rule (must be non-empty string) * @param ruleHandler - Function that performs the validation logic * * @throws {Error} When ruleName is not a non-empty string * @throws {Error} When ruleHandler is not a function * * @since 1.0.0 * @see {@link findRegisteredRule} - Find a registered rule * @see {@link getRules} - Get all registered rules * @public */ static registerRule<ParamType extends Array<any> = Array<any>, Context = unknown>(ruleName: IValidatorRuleName, ruleHandler: IValidatorRuleFunction<ParamType, Context>): void; /** * ## Get All Registered Rules * * Retrieves an immutable copy of all currently registered validation rules. * This method returns a shallow copy to prevent external modification of * the internal rules registry while allowing inspection of available rules. * * ### Use Cases * - Debugging: Check what rules are available * - Rule Discovery: List all registered rules for documentation * - Testing: Verify rule registration in unit tests * - Introspection: Build dynamic validation UIs * * @example * ```typescript * // Get all registered rules * const allRules = Validator.getRules(); * console.log('Available rules:', Object.keys(allRules)); * * // Check if a specific rule exists * const hasEmailRule = 'Email' in Validator.getRules(); * * // Get rule function directly (not recommended, use findRegisteredRule instead) * const emailRule = Validator.getRules()['Email']; * ``` * * @returns An immutable copy of all registered validation rules * * @since 1.0.0 * @see {@link registerRule} - Register a new rule * @see {@link findRegisteredRule} - Find a specific rule * @public */ static getRules<Context = unknown>(): IValidatorRegisteredRules<Context>; /** * ## Get Registered Rule * * Retrieves a specific validation rule function by its registered name. This method * provides direct access to the underlying validation functions that have been * registered with the Validator system. * * ### Rule Retrieval * This method looks up rules in the internal rules registry that was populated * through the `registerRule` method. It returns the actual validation function * that can be used for custom validation logic or inspection. * * ### Return Value * - Returns the validation rule function if found * - Returns `undefined` if no rule with the given name exists * - The returned function has the signature `IValidatorRuleFunction` * * @example * ```typescript * // Get a registered rule function * const emailRule = Validator.getRule('Email'); * if (emailRule) { * // Use the rule directly * const result = await emailRule({ * value: 'test@example.com', * ruleParams: [] * }); * console.log('Email validation result:', result); * } * * // Check if a rule exists before using it * const customRule = Validator.getRule('CustomRule'); * if (customRule) { * // Rule exists, safe to use * } else { * console.log('CustomRule is not registered'); * } * * // Get rule for programmatic validation * const minLengthRule = Validator.getRule('MinLength'); * if (minLengthRule) { * const isValid = await minLengthRule({ * value: 'hello', ruleParams: [3] // Minimum length of 3 * }); * } * ``` * * @param ruleName - The name of the validation rule to retrieve * * @returns The validation rule function if found, undefined otherwise * * @since 1.0.0 * @see {@link registerRule} - Register a new validation rule * @see {@link getRules} - Get all registered rules * @see {@link hasRule} - Check if a rule exists (type guard) * @public */ static getRule<Context = unknown>(ruleName: IValidatorRuleName): IValidatorRuleFunction<[], Context> | IValidatorRuleFunction<[number], Context> | IValidatorRuleFunction<[lengthOrMinLength: number, maxLength?: number | undefined], Context> | IValidatorRuleFunction<[minLength: number], Context> | IValidatorRuleFunction<[maxLength: number], Context> | IValidatorRuleFunction<[countryCode?: "AF" | "AL" | "DZ" | "AS" | "AD" | "AO" | "AI" | "AG" | "AR" | "AM" | "AW" | "AU" | "AT" | "AZ" | "BS" | "BH" | "BD" | "BB" | "BY" | "BE" | "BZ" | "BJ" | "BM" | "BT" | "BO" | "BA" | "BW" | "BR" | "IO" | "VG" | "BN" | "BG" | "BF" | "BI" | "KH" | "CM" | "CA" | "CV" | "BQ" | "KY" | "CF" | "TD" | "CL" | "CN" | "CX" | "CC" | "CO" | "KM" | "CD" | "CG" | "CK" | "CR" | "CI" | "HR" | "CU" | "CW" | "CY" | "CZ" | "DK" | "DJ" | "DM" | "DO" | "EC" | "EG" | "SV" | "GQ" | "ER" | "EE" | "ET" | "FK" | "FO" | "FJ" | "FI" | "FR" | "GF" | "PF" | "GA" | "GM" | "GE" | "DE" | "GH" | "GI" | "GR" | "GL" | "GD" | "GP" | "GU" | "GT" | "GG" | "GN" | "GW" | "GY" | "HT" | "HN" | "HK" | "HU" | "IS" | "IN" | "ID" | "IR" | "IQ" | "IE" | "IM" | "IL" | "IT" | "JM" | "JP" | "JE" | "JO" | "KZ" | "KE" | "KI" | "KW" | "KG" | "LA" | "LV" | "LB" | "LS" | "LR" | "LY" | "LI" | "LT" | "LU" | "MO" | "MK" | "MG" | "MW" | "MY" | "MV" | "ML" | "MT" | "MH" | "MQ" | "MR" | "MU" | "YT" | "MX" | "FM" | "MD" | "MC" | "MN" | "ME" | "MS" | "MA" | "MZ" | "MM" | "NA" | "NR" | "NP" | "NL" | "NC" | "NZ" | "NI" | "NE" | "NG" | "NU" | "NF" | "KP" | "MP" | "NO" | "OM" | "PK" | "PW" | "PS" | "PA" | "PG" | "PY" | "PE" | "PH" | "PL" | "PT" | "PR" | "QA" | "RE" | "RO" | "RU" | "RW" | "BL" | "SH" | "KN" | "LC" | "MF" | "PM" | "VC" | "WS" | "SM" | "ST" | "SA" | "SN" | "RS" | "SC" | "SL" | "SG" | "SX" | "SK" | "SI" | "SB" | "SO" | "ZA" | "KR" | "SS" | "ES" | "LK" | "SD" | "SR" | "SJ" | "SZ" | "SE" | "CH" | "SY" | "TW" | "TJ" | "TZ" | "TH" | "TL" | "TG" | "TK" | "TO" | "TT" | "TN" | "TR" | "TM" | "TC" | "TV" | "VI" | "UG" | "UA" | "AE" | "GB" | "US" | "UY" | "UZ" | "VU" | "VA" | "VE" | "VN" | "WF" | "EH" | "YE" | "ZM" | "ZW" | "AX" | undefined], Context> | IValidatorRuleFunction<[minLength: number], Context> | IValidatorRuleFunction<[maxLength: number], Context> | IValidatorRuleFunction<[length: number], Context> | IValidatorRuleFunction<any[], Context> | IValidatorRuleFunction<[date: string | Date], Context> | IValidatorRuleFunction<[date: string | Date], Context> | IValidatorRuleFunction<[minDate: string | Date, maxDate: string | Date], Context> | IValidatorRuleFunction<[date: string | Date], Context> | IValidatorRuleFunction<import("../types").IPrimitive[], Context> | IValidatorRuleFunction<[size: number], Context> | IValidatorRuleFunction<string[], Context> | IValidatorRuleFunction<[minSize: number], Context> | IValidatorRuleFunction<[min: number, max: number], Context> | IValidatorRuleFunction<[minDecimalPlaces: number, maxDecimalPlaces?: number | undefined], Context>; /** * ## Check Rule Existence (Type Guard) * * Type guard method that checks whether a validation rule with the given name * is registered in the Validator system. This method provides both existence * checking and TypeScript type narrowing for rule names. * * ### Type Guard Behavior * - **Input Validation**: First checks if the input is a non-null string * - **Rule Lookup**: Uses `getRule` to check if the rule exists in the registry * - **Type Narrowing**: Narrows `ruleName` to `IValidatorRuleName` if it returns true * * ### Use Cases * - **Safe Rule Access**: Verify rule existence before using `getRule` * - **Dynamic Validation**: Check rules at runtime before applying them * - **Type Safety**: Enable TypeScript to narrow types based on rule existence * - **Error Prevention**: Avoid undefined access when working with rule names * * @example * ```typescript * // Basic existence check * if (Validator.hasRule('Email')) { * console.log('Email rule is available'); * } * * // Type narrowing with type guard * function processRule(ruleName: string) { * if (Validator.hasRule(ruleName)) { * // TypeScript now knows ruleName is IValidatorRuleName * const rule = Validator.getRule(ruleName); // Type safe return rule; * } else { * console.log(`${ruleName} is not a valid rule`); return null; * } * } * * // Safe rule processing * const ruleNames = ['Email', 'Required', 'InvalidRule']; * const validRules = ruleNames.filter(Validator.hasRule); * console.log('Valid rules:', validRules); // ['Email', 'Required'] * * // Dynamic rule application * function applyRuleIfExists(value: any, ruleName: string) { * if (Validator.hasRule(ruleName)) { * const rule = Validator.getRule(ruleName); * return rule?.({ value, ruleParams: [] }); * } * return 'Rule not found'; * } * ``` * * @param ruleName - The name to check for rule existence (any type, validated internally) * * @returns `true` if the rule exists and ruleName is a valid IValidatorRuleName, `false` otherwise * * @since 1.0.0 * @see {@link getRule} - Get the actual rule function * @see {@link getRules} - Get all registered rules * @see {@link registerRule} - Register a new validation rule * @public */ static hasRule(ruleName: any): ruleName is IValidatorRuleName; private static getI18n; /** * ## Get Error Message Separators * * Retrieves the configured separators used for formatting validation error messages. * These separators are internationalized and can be customized through the i18n system. * This method provides a centralized way to get consistent error message formatting. * * ### Separator Types * - `multiple` - Used when joining multiple error messages * - `single` - Used for single error message formatting * * ### Internationalization * The separators are loaded from the i18n translation system under the key * `validator.separators`. This allows different languages to use appropriate * punctuation and formatting conventions. * * @param customI18n - Optional custom I18n instance to use for translations * @example * ```typescript * // Get current separators * const separators = Validator.getErrorMessageSeparators(); * console.log(separators); // { multiple: ", ", single: ", " } * * // Use separators for custom error formatting * const errors = ['Field is required', 'Must be an email', 'Too short']; * const errorMessage = errors.join(separators.multiple); * console.log(errorMessage); // "Field is required, Must be an email, Too short" * * // Custom error message builder * function buildErrorMessage(fieldName: string, errors: string[]) { * const seps = Validator.getErrorMessageSeparators(); * return `${fieldName}: ${errors.join(seps.multiple)}`; * } * ``` * * @returns Object containing separator strings for error message formatting * @returns returns.multiple - Separator for joining multiple error messages * @returns returns.single - Separator for single error message formatting * * @since 1.0.0 * @see {@link validate} - Uses these separators for error formatting * @see {@link validateTarget} - Also uses these separators * @public */ static getErrorMessageSeparators(customI18n?: I18n): { multiple: string; single: string; }; /** * ## Find Registered Rule * * Locates and returns a specific validation rule by its name. This method provides * type-safe access to registered validation rules with proper error handling for * invalid rule names. Returns undefined if the rule doesn't exist. * * ### Type Safety * This method is fully type-safe and will return the correctly typed rule function * based on the generic parameters provided. The rule function signature will match * the expected parameter and context types. * * @example * ```typescript * // Find a simple rule * const emailRule = Validator.findRegisteredRule('Email'); * if (emailRule) { * const result = await emailRule({ * value: 'test@example.com', * ruleParams: [] * }); * } * * // Find a rule with specific parameter types * const minLengthRule = Validator.findRegisteredRule<[number]>('MinLength'); * if (minLengthRule) { * const result = await minLengthRule({ * value: 'hello', * ruleParams: [5] * }); * } * * // Find a rule with context * interface UserContext { * userId: number; * permissions: string[]; * } * * const permissionRule = Validator.findRegisteredRule<string[], UserContext>('HasPermission'); * if (permissionRule) { * const result = await permissionRule({ * value: 'admin', * ruleParams: ['admin', 'moderator'], * context: { userId: 123, permissions: ['user', 'admin'] } * }); * } * * // Safe rule checking * const unknownRule = Validator.findRegisteredRule('NonExistentRule'); * console.log(unknownRule); // undefined * ``` * * @template ParamType - Array type specifying the rule parameter types * @template Context - Type of the validation context object * * @param ruleName - The name of the rule to find * * @returns The validation rule function if found, undefined otherwise * * @since 1.0.0 * @see {@link registerRule} - Register a new rule * @see {@link getRules} - Get all rules * @public */ static findRegisteredRule<ParamType extends Array<any> = Array<any>, Context = unknown>(ruleName: IValidatorRuleName): IValidatorRuleFunction<ParamType, Context> | undefined; /** * ## Parse and Validate Rules * * Converts various input rule formats into a standardized, executable format while * identifying and reporting any invalid rules. This method handles the complex task * of normalizing different rule input formats into a consistent internal representation. * * ### Supported Input Formats * * #### 1. Function Rules * ```typescript * const functionRule = ({ value }) => value > 0 || 'Must be positive'; * ``` * * #### 2. String Rules * ```typescript * 'Required' // Simple rule * 'MinLength[5]' // Rule with single parameter * 'Between[10,20]' // Rule with multiple parameters * ``` * * #### 3. Object Rules * ```typescript * { Required: [] } // Rule without parameters * { MinLength: [5] } // Rule with parameters * { Between: [10, 20] } // Rule with multiple parameters * ``` * * ### Processing Logic * 1. **Function Detection**: Direct function rules are passed through unchanged * 2. **String Parsing**: Extracts rule names and parameters from bracketed syntax * 3. **Object Processing**: Converts object notation to standardized format * 4. **Validation**: Verifies that all referenced rules are registered * 5. **Error Tracking**: Collects invalid rules for reporting * * @example * ```typescript * // Mixed rule formats * const mixedRules = [ * 'Required', * 'MinLength[3]', * { MaxLength: [50] }, * ({ value }) => value.includes('@') || 'Must contain @', * 'InvalidRule' // This will be reported as invalid * ]; * * const { sanitizedRules, invalidRules } = Validator.parseAndValidateRules(mixedRules); * * console.log('Valid rules:', sanitizedRules.length); // 4 * console.log('Invalid rules:', invalidRules); // ['InvalidRule'] * * // Empty or undefined input * const { sanitizedRules: empty } = Validator.parseAndValidateRules(); * console.log(empty.length); // 0 * * // Complex rule with parameters * const complexRules = [ * 'Between[1,100]', * { CustomRule: ['param1', 'param2'] } * ]; * * const result = Validator.parseAndValidateRules(complexRules); * // Each sanitized rule will have: ruleName, params, ruleFunction, rawRuleName * ``` * * @param inputRules - Array of validation rules in various formats, or undefined * * @returns Object containing processed results * @returns returns.sanitizedRules - Array of standardized, executable rule objects * @returns returns.invalidRules - Array of rules that couldn't be processed (unregistered) * * @since 1.22.0 * @see {@link parseStringRule} - Internal string rule parser * @see {@link parseObjectRule} - Internal object rule parser * @see {@link validate} - Uses this method for rule processing * @public */ static parseAndValidateRules<Context = unknown>(inputRules?: IValidatorValidateOptions<Array<any>, Context>["rules"]): { sanitizedRules: IValidatorSanitizedRules<Context>; invalidRules: IValidatorRules<Context>[]; }; /** * ## Parse String-Based Validation Rules * * Internal helper method that parses string-format validation rules into standardized * rule objects. Handles both simple rule names and rules with parameters using * bracket notation syntax. * * ### Supported String Formats * - `"ruleName"` - Simple rule without parameters * - `"ruleName[param]"` - Rule with single parameter * - `"ruleName[param1,param2,param3]"` - Rule with multiple parameters * * ### Parameter Parsing * - Parameters are extracted from content within square brackets * - Multiple parameters are separated by commas * - Leading/trailing whitespace is automatically trimmed * - All parameters are treated as strings (conversion happens in rule functions) * * @example * ```typescript * // These calls demonstrate the parsing logic (internal method) * // Simple rule * parseStringRule("Required", registeredRules) * // Returns: { ruleName: "Required", params: [], ruleFunction: fn, rawRuleName: "Required" } * * // Rule with single parameter * parseStringRule("MinLength[5]", registeredRules) * // Returns: { ruleName: "MinLength", params: ["5"], ruleFunction: fn, rawRuleName: "MinLength[5]" } * * // Rule with multiple parameters * parseStringRule("Between[10, 20]", registeredRules) * // Returns: { ruleName: "Between", params: ["10", "20"], ruleFunction: fn, rawRuleName: "Between[10, 20]" } * ``` * * @internal * @param ruleString - The string representation of the rule to parse * @param registeredRules - Map of all currently registered validation rules * * @returns Parsed rule object with standardized structure, or null if rule not found * @returns returns.ruleName - The extracted rule name * @returns returns.params - Array of string parameters * @returns returns.ruleFunction - The actual validation function * @returns returns.rawRuleName - The original unparsed rule string * * @since 1.22.0 * @see {@link parseAndValidateRules} - Public method that uses this parser * @private */ private static parseStringRule; private static parseObjectRule; /** * ## Validate a Single Value * * Performs validation on a single value using a set of specified validation rules. * This is the main validation method for validating individual values outside of * class-based validation contexts. * * ### Key Features * - **Synchronous Rule Support**: Handles both synchronous and asynchronous validation rules * - **Multiple Rules**: Supports validation with multiple rules applied sequentially * - **Error Handling**: Never throws errors; returns a result object with success/failure status * - **Type Safe**: Full TypeScript support with generic typing for context * - **Nullable Handling**: Supports Empty, Nullable, and Optional rules for conditional validation * - **Performance**: Tracks validation duration and timestamps * * ### Return Type: IValidatorValidateResult * The method returns a discriminated union that can be narrowed: * ```typescript * type IValidatorValidateResult<Context> = * | IValidatorValidateSuccess<Context> // success: true * | IValidatorValidateFailure<Context> // success: false * ``` * * #### Success Result (success: true) * - `success`: true * - `value`: The original value that was validated * - `validatedAt`: ISO timestamp when validation completed * - `duration`: Milliseconds elapsed during validation * - `data`: Optional context data passed to rules * - `context`: Optional validation context of type Context * * #### Failure Result (success: false) * - `success`: false * - `value`: The original value that failed validation * - `error`: IValidatorValidationError containing: * - `message`: Error message (translated if i18n available) * - `ruleName`: Name of the rule that failed * - `ruleParams`: Parameters passed to the rule * - `fieldName`: Optionally provided field identifier * - `failedAt`: ISO timestamp when validation failed * - `duration`: Milliseconds elapsed before failure * * ### Nullable Rules * Special handling for conditional validation rules: * - **Empty**: Skips validation if value is empty string "" * - **Nullable**: Skips validation if value is null or undefined * - **Optional**: Skips validation if value is undefined only * * Priority order: Empty > Nullable > Optional * * ### Examples * * #### Basic Single Rule Validation * ```typescript * const result = await Validator.validate({ * value: "user@example.com", * rules: ["Required", "Email"], * }); * * if (result.success) { * console.log("Email is valid:", result.value); * } else { * console.error("Validation failed:", result.error.message); * } * ``` * * #### Validation with Parameters * ```typescript * const result = await Validator.validate({ * value: "hello", * rules: [ * "Required", * "MinLength[5]", // Validates length >= 5 * "MaxLength[20]", // Validates length <= 20 * ], * }); * ``` * * #### Custom Error Messages with i18n * ```typescript * const result = await Validator.validate({ * value: "", * rules: ["Required"], * fieldName: "email", // For context in error messages * }); * * if (!result.success) { * // Error message can include field name if i18n is configured * console.error(result.error.message); * } * ``` * * #### Async Rule with Context * ```typescript * interface MyContext { * userId: number; * permissions: string[]; * } * * const result = await Validator.validate<MyContext>({ * value: "admin_action", * rules: ["Required", "UniqueAction"], * context: { * userId: 123, * permissions: ["admin"], * }, * }); * ``` * * #### Nullable Rule Examples * ```typescript * // Null is valid with Nullable rule * const result1 = await Validator.validate({ * value: null, * rules: ["Nullable", "Required"], * }); * // result1.success === true (skips Required check) * * // Empty string is valid with Empty rule * const result2 = await Validator.validate({ * value: "", * rules: ["Empty", "Email"], * }); * // result2.success === true (skips Email check) * * // Undefined is valid with Optional rule * const result3 = await Validator.validate({ * value: undefined, * rules: ["Optional", "MinLength[5]"], * }); * // result3.success === true (skips MinLength check) * ``` * * #### Type Guards for Result Narrowing * ```typescript * const result = await Validator.validate({ * value: "test", * rules: ["Required"], * }); * * // Using type guards * if (Validator.isSuccess(result)) { * // TypeScript knows result.success === true * console.log("Valid value:", result.value); * } else if (Validator.isFailure(result)) { * // TypeScript knows result.success === false * console.error("Error:", result.error.message); * } * ``` * * @template Context - Optional type for the validation context object * * @param options - Validation options (IMakeOptional< IValidatorValidateOptions<Array<any>, Context>, "i18n" >) * @param options.value - The value to validate (required) * @param options.rules - Array of validation rules to apply * @param options.context - Optional context object passed to rule functions * @param options.data - Optional data object for rule context * @param options.fieldName - Optional field identifier for error messages * @param options.propertyName - Optional property identifier for error messages * @param options.translatedPropertyName - Optional translated property name * @param options.message - Optional custom error message prefix * * @returns Promise resolving to IValidatorValidateResult<Context> * - Success: object with success=true, value, validatedAt, duration * - Failure: object with success=false, error, failedAt, duration * * @throws {Never} This method never throws. All errors are returned in the result object. * * @since 1.0.0 * @see {@link validateTarget} - For class-based validation using decorators * @see {@link registerRule} - To register custom validation rules * @see {@link IValidatorValidateResult} - Result type documentation * @see {@link IValidatorValidationError} - Error details type * * @public * @async */ static validate<Context = unknown>({ rules, ...extra }: IMakeOptional<IValidatorValidateOptions<Array<any>, Context>, "i18n">): Promise<IValidatorValidateResult<Context>>; /** * ## Should Skip Validation * * Determines whether validation should be skipped based on the presence of nullable rules * and the current value. This method checks if the value meets the conditions for * skipping validation when nullable rules like Empty, Nullable, or Optional are present * in the rules array. * * ### Nullable Rules and Conditions * - **Empty**: Skips validation if value is an empty string "" * - **Nullable**: Skips validation if value is null or undefined * - **Optional**: Skips validation if value is undefined * * ### Logic * 1. Only checks when the value is considered "empty" (using isEmpty utility) * 2. Iterates through the rules array to find matching nullable rule names * 3. Supports both string rules ("Empty") and object rules ({ Empty: [] }) * 4. Returns true if any matching nullable rule condition is met * 5. Function rules are ignored in this check * * @param options - The options object containing value and rules * @param options.value - The value to check for nullable conditions * @param options.rules - The array of validation rules to inspect for nullable rules * * @returns `true` if validation should be skipped due to nullable conditions, `false` otherwise * * @since 1.0.0 * @see {@link validate} - Uses this method to conditionally skip validation * @see {@link validateTarget} - Also uses this method for class-based validation * @public */ static shouldSkipValidation({ value, rules }: { rules: Array<IValidatorRuleName> | IValidatorSanitizedRules<any>; value: any; }): boolean; /** * ## Validate OneOf Rule * * Wrapper that applies OR logic across multiple sub-rules. Delegates to * {@link validateMultiRule} with `"OneOf"`. Succeeds on the first passing * sub-rule (early exit). If all sub-rules fail, returns a single error string * aggregating each sub-rule’s message joined by `; `. * * @template Context - Optional type for validation context * @template RulesFunctions - Array of sub-rules to evaluate * @param options - Multi-rule validation options * @returns `IValidatorResult` (`Promise<boolean|string>`) * @example * const res = await Validator.validateOneOfRule({ * value: "user@example.com", * ruleParams: ["Email", "PhoneNumber"], * }); * // res === true when any sub-rule succeeds * @since 1.35.0 * @see {@link validateMultiRule} */ static validateOneOfRule<Context = unknown, RulesFunctions extends IValidatorDefaultMultiRule<Context> = IValidatorDefaultMultiRule<Context>>(options: IValidatorValidateMultiRuleOptions<Context, RulesFunctions>): IValidatorResult; /** * ## Validate AllOf Rule * * Wrapper that applies AND logic across multiple sub-rules. Delegates to * {@link validateMultiRule} with `"AllOf"`. Succeeds only if all sub-rules * pass. When any sub-rule fails, returns a single aggregated error string * joining messages with `; `. * * @template Context - Optional type for validation context * @template RulesFunctions - Array of sub-rules to evaluate * @param options - Multi-rule validation options * @returns `IValidatorResult` (`Promise<boolean|string>`) * @example * const res = await Validator.validateAllOfRule({ * value: "hello", * ruleParams: ["String", { MinLength: [5] }], * }); * // res === true only if all sub-rules succeed * @since 1.35.0 * @see {@link validateMultiRule} */ static validateAllOfRule<Context = unknown, RulesFunctions extends IValidatorDefaultMultiRule<Context> = IValidatorDefaultMultiRule<Context>>(options: IValidatorValidateMultiRuleOptions<Context, RulesFunctions>): IValidatorResult; /** * ## Validate ArrayOf Rule * * Validates that a value is an array and that each item in the array * satisfies all of the provided sub-rules (AND logic per item). * * - Ensures `value` is an array; otherwise returns the localized `array` error. * - Applies {@link validateMultiRule} with `"AllOf"` to each item using the provided `ruleParams`. * - Aggregates failing item messages; returns `true` when all items pass. * - When any items fail, returns a localized summary using `failedForNItems` * followed by concatenated item error messages. * * @template Context - Optional type for validation context * @template RulesFunctions - Array of sub-rules applied to each item * @param options - Multi-rule validation options * @returns `IValidatorResult` (`Promise<boolean|string>`) - `true` if all items pass; otherwise an aggregated error string * @example * const res = await Validator.validateArrayOfRule({ * value: ["user@example.com", "admin@example.com"], * ruleParams: ["Email"], * }); * // res === true when every item is a valid email * @since 1.36.0 */ static validateArrayOfRule<Context = unknown, RulesFunctions extends IValidatorDefaultMultiRule<Context> = IValidatorDefaultMultiRule<Context>>(options: IValidatorValidateMultiRuleOptions<Context, RulesFunctions>): Promise<boolean | string>; static getI18nTranslateOptions({ fieldName, propertyName, fieldLabel, translatedPropertyName, context, data, ...rest }: Partial<IValidatorValidateOptions<Array<any>, any>>): Partial<IValidatorValidateOptions<any[], any>>; /** * ## Validate Nested Rule (Core Nested Validation Executor) * * Internal rule function that validates a nested object against a class constructor with * validation decorators. This method is the workhorse for nested class validation, delegating * to {@link validateTarget} for the actual multi-field validation logic. * * ### Purpose * This method implements the core logic for the `ValidateNested` rule, enabling validation of * complex hierarchical object structures where a property value must itself be validated against * a decorated class schema. It acts as the bridge between single-value rule validation and * multi-field class-based validation. * * ### Validation Flow * 1. **Parameter Extraction**: Extracts the target class constructor from `ruleParams[0]` * 2. **Validation**: Calls `validateTarget()` to validate the nested object against the class * 3. **Error Aggregation**: Collects nested validation errors with property path information * 4. **Result Formatting**: Returns either `true` (success) or error message string (failure) * * ### Error Handling Strategy * - **Missing Class Constructor**: Returns invalidRule error if no target class provided * - **Invalid Data Type**: Returns validateNested error if data is not an object * - **Nested Validation Failures**: Aggregates all nested field errors with property names in format: * `"[propertyName]: error message; [propertyName]: error message"` * - **Successful Validation**: Returns `true` without modification * * ### Type Parameters * - `Target` - Class constructor extending IClassConstructor with validation decorators * - `Context` - Optional validation context type passed through nested validations * * ### Return Values * - `true` - Nested object validation succeeded * - `string` - Validation failed; returns i18n-formatted error message with nested error details * * ### Usage Context * This method is primarily used as: * - The internal handler for the `validateNested` factory function * - A sub-rule within multi-rule validators (OneOf, AllOf) * - Direct validator for nested object properties in class-based validation * * ### Example * ```typescript * class Address { * @IsRequired * @MinLength([5]) * street: string; * * @IsRequired * @IsPostalCode * postalCode: string; * } * * class User { * @IsRequired * name: string; * * @ValidateNested([Address]) * address: Address; * } * * // When validating a User instance with an Address property, * // validateNestedRule is called to validate the address against the Address class * const result = await Validator.validateTarget(User, { * data : { * name: "John", * address: { street: "123 Main St", postalCode: "12345" } * }}); * ``` * * ### Key Features * - **DRY Principle**: Reuses existing `validateTarget` logic to avoid code duplication * - **Error Context**: Preserves field hierarchy information in error messages * - **i18n Integration**: Uses translation system for localized error messages * - **Context Propagation**: Passes validation context through to nested validators * - **Timing Tracking**: Maintains duration tracking across nested validations * * @template Target - Class constructor type (must extend IClassConstructor) * @template Context - Optional validation context type * * @param options - Validation rule function options (IValidatorNestedRuleFunctionOptions<Target, Context>) * @param options.ruleParams - Array containing the nested class constructor at index [0] * @param options.value - The nested object value to validate (extracted to data property) * @param options.data - The nested object data to validate against the target class * @param options.context - Optional validation context passed to all validation rules * @param options.fieldName - Optional field identifier for error messages * @param options.propertyName - Optional property identifier for error messages * @param options.translatedPropertyName - Optional i18n property name for error messages * @param options.startTime - Optional timestamp for duration tracking * @param options.i18n - Optional i18n instance for error message translation * * @returns Promise<boolean | string> * - Resolves to `true` if nested object validation succeeds * - Resolves to error message string if validation fails * - Never rejects; all errors are returned as resolution values * - Error messages include nested field paths: `"[fieldName]: error; [fieldName]: error"` * * @throws {Never} This method never throws errors; all failures are returned as strings * * @remarks * - This is an internal method primarily used by the `validateNested` factory * - Accepts IValidatorNestedRuleFunctionOptions which omits validateTarget's i18n parameter * - Delegates directly to validateTarget(target, options) maintaining all context * - Nested validation errors include property names for clear error tracing * - The method integrates seamlessly with the multi-rule validation system * - Supports recursive nesting of arbitrarily deep object structures * - Performance: Delegates to validateTarget which validates fields in parallel * - Error aggregation uses nested field paths for hierarchical clarity * * @since 1.36.0 * @see {@link validateNested} - Factory function that creates rule functions using this method * @see {@link validateTarget} - The underlying class-based validation method (accepts options with data) * @see {@link ValidateNested} - Decorator that uses this method via the factory * @see {@link IValidatorNestedRuleFunctionOptions} - Options interface for this method * @see {@link buildMultiRuleDecorator} - Decorator builder for complex multi-rule scenarios * @internal * @async */ static validateNestedRule<Target extends IClassConstructor = IClassConstructor, Context = unknown>({ ruleParams, ...options }: IValidatorNestedRuleFunctionOptions<Target, Context>): Promise<boolean | string>; /** * ## Validate Multi-Rule (OneOf / AllOf) * * Evaluates multiple sub-rules against a single value using either OR logic (`OneOf`) or * AND logic (`AllOf`). Each sub-rule is validated in sequence via {@link Validator.validate}, * with early exit on success for `OneOf` and full aggregation of errors for `AllOf`. * * ### Behavior * - `OneOf`: Returns `true` as soon as any sub-rule succeeds (early exit). If all sub-rules fail, * returns a concatenated error message string summarizing each failure. * - `AllOf`: Requires every sub-rule to succeed. If any sub-rule fails, returns a concatenated * error message string summarizing all failures; otherwise returns `true`. * - Empty `ruleParams`: If no sub-rules are provided, returns `true`. * * ### Execution Notes * - Sub-rules are evaluated sequentially (not in parallel) to allow early exit optimization for `OneOf`. * - Error messages from failed sub-rules are collected and joined using `; ` as a separator. * - Internationalization: Uses `i18n` (if provided) to prefix the aggregated error message * with the localized rule label (`validator.OneOf` or `validator.AllOf`). * - Timing: Initializes `startTime` when absent to enable duration tracking downstream. * * @template Context - Optional type for the validation context object * @template RulesFunctions - Array type of sub-rules; each sub-rule can be a named rule, * parameterized rule object, or a rule function * * @param ruleName - Multi-rule mode to apply: `"OneOf"` or `"AllOf"` * @param options - Validation options extending {@link IValidatorValidateMultiRuleOptions} * @param options.value - The value to validate against the sub-rules * @param options.ruleParams - Array of sub-rules to evaluate (functions or named/object rules) * @param options.context - Optional context passed through to each sub-rule * @param options.data - Optional auxiliary data passed through to each sub-rule * @param options.startTime - Optional start timestamp used for duration tracking * @param options.fieldName - Optional field identifier used in error construction * @param options.propertyName - Optional property identifier used in error construction * @param options.translatedPropertyName - Optional localized property name for error messages * @param options.i18n - Optional i18n instance used to localize the error label * * @returns IValidatorResult * - `true` when validation succeeds (any sub-rule for `OneOf`, all sub-rules for `AllOf`) * - `string` containing aggregated error messages when validation fails * * @example * // OneOf: either email or phone must be valid * const resultOneOf = await Validator.validateOneOfRule({ * value: "user@example.com", * ruleParams: ["Email", "PhoneNumber"], * }); * // resultOneOf === true * * @example * // AllOf: must be a string and minimum length 5 * const resultAllOf = await Validator.validateAllOfRule({ * value: "hello", * ruleParams: ["String", { MinLength: [5] }], * }); * // resultAllOf === true * * @since 1.35.0 * @see {@link validateOneOfRule} - Convenience wrapper applying `OneOf` logic * @see {@link validateAllOfRule} - Convenience wrapper applying `AllOf` logic * @see {@link oneOf} - Factory to build a reusable `OneOf` rule function * @see {@link allOf} - Factory to build a reusable `AllOf` rule function * @see {@link validate} - Underlying validator used for each sub-rule * @public * @async */ static validateMultiRule<Context = unknown, RulesFunctions extends IValidatorDefaultMultiRule<Context> = IValidatorDefaultMultiRule<Context>>(ruleName: IValidatorMultiRuleNames, { value, ruleParams, startTime, ...extra }: IValidatorValidateMultiRuleOptions<Context, RulesFunctions>): Promise<string | true>; /** * ## Create OneOf Validation Rule * * Factory method that creates a OneOf validation rule function. This method provides * a programmatic way to create validation rules that implement OR logic, where * validation succeeds if at least one of the specified sub-rules passes. * * ### OneOf Validation Concept * OneOf validation allows flexible validation scenarios where multiple validation * paths are acceptable. Instead of requiring all rules to pass (AND logic), * OneOf requires only one rule to pass (OR logic), making it ideal for: * - Alternative input formats (email OR phone number) * - Flexible validation requirements * - Multiple acceptable validation criteria * * ### Method Behavior * This factory method returns a validation rule function that can be used directly * in validation calls or registered as a named rule. The returned function delegates * to `validateOneOfRule` for the actual validation logic. * * ### Parallel Execution * When the returned rule function is executed, all sub-rules are validated in parallel * usi