/** * Validation Utilities - Core Module * * Framework-agnostic validation helper functions. * Can be used in any project (NestJS, Express, Frontend, etc.) * * @module @shared/utils/validation * @version 1.0.0 */ /** * Check if value is email */ export const isEmail = (email: string): boolean => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; /** * Check if value is UUID (v4) */ export const isUUID = (uuid: string): boolean => { const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; return uuidRegex.test(uuid); }; /** * Check if value is valid URL */ export const isURL = (url: string): boolean => { try { new URL(url); return true; } catch { return false; } }; /** * Check if password is strong * Requirements: 8+ chars, 1 uppercase, 1 lowercase, 1 number, 1 special char */ export const isStrongPassword = (password: string): boolean => { const strongRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; return strongRegex.test(password); }; /** * Get password strength (0-4) * 0: Very weak, 1: Weak, 2: Fair, 3: Strong, 4: Very strong */ export const getPasswordStrength = (password: string): number => { let strength = 0; if (password.length >= 8) strength++; if (password.length >= 12) strength++; if (/[a-z]/.test(password) && /[A-Z]/.test(password)) strength++; if (/\d/.test(password)) strength++; if (/[@$!%*?&#^()_+=[\]{};':"\\|,.<>/?-]/.test(password)) strength++; return Math.min(strength, 4); }; /** * Check if value is phone number (international format) */ export const isPhoneNumber = (phone: string): boolean => { const phoneRegex = /^\+?[1-9]\d{1,14}$/; return phoneRegex.test(phone.replace(/[\s\-()]/g, '')); }; /** * Check if value is numeric */ export const isNumeric = (value: string): boolean => { return !isNaN(Number(value)) && !isNaN(parseFloat(value)); }; /** * Check if value is integer */ export const isInteger = (value: string | number): boolean => { const num = typeof value === 'string' ? parseFloat(value) : value; return Number.isInteger(num); }; /** * Check if value is alphanumeric */ export const isAlphanumeric = (value: string): boolean => { const alphanumericRegex = /^[a-zA-Z0-9]+$/; return alphanumericRegex.test(value); }; /** * Check if value is alphabetic only */ export const isAlphabetic = (value: string): boolean => { const alphabeticRegex = /^[a-zA-Z]+$/; return alphabeticRegex.test(value); }; /** * Check if value is within range (inclusive) */ export const isInRange = (value: number, min: number, max: number): boolean => { return value >= min && value <= max; }; /** * Check if array has minimum length */ export const hasMinLength = (array: T[], minLength: number): boolean => { return array.length >= minLength; }; /** * Check if array has maximum length */ export const hasMaxLength = (array: T[], maxLength: number): boolean => { return array.length <= maxLength; }; /** * Check if string has minimum length */ export const hasMinStringLength = (str: string, minLength: number): boolean => { return str.length >= minLength; }; /** * Check if string has maximum length */ export const hasMaxStringLength = (str: string, maxLength: number): boolean => { return str.length <= maxLength; }; /** * Check if value is valid JSON string */ export const isValidJSON = (value: string): boolean => { try { JSON.parse(value); return true; } catch { return false; } }; /** * Check if value is positive number */ export const isPositive = (value: number): boolean => { return value > 0; }; /** * Check if value is non-negative number */ export const isNonNegative = (value: number): boolean => { return value >= 0; }; /** * Check if value is negative number */ export const isNegative = (value: number): boolean => { return value < 0; }; /** * Check if string matches pattern */ export const matchesPattern = (value: string, pattern: RegExp): boolean => { return pattern.test(value); }; /** * Validate required fields in object */ export const hasRequiredFields = ( obj: T, requiredFields: (keyof T)[], ): boolean => { return requiredFields.every( (field) => obj[field] !== undefined && obj[field] !== null, ); }; /** * Check if value is valid credit card number (Luhn algorithm) */ export const isCreditCard = (cardNumber: string): boolean => { const sanitized = cardNumber.replace(/\D/g, ''); if (sanitized.length < 13 || sanitized.length > 19) return false; let sum = 0; let isEven = false; for (let i = sanitized.length - 1; i >= 0; i--) { let digit = parseInt(sanitized[i], 10); if (isEven) { digit *= 2; if (digit > 9) digit -= 9; } sum += digit; isEven = !isEven; } return sum % 10 === 0; }; /** * Check if value is valid IP address (v4) */ export const isIPv4 = (ip: string): boolean => { const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; return ipv4Regex.test(ip); }; /** * Check if value is valid hex color */ export const isHexColor = (color: string): boolean => { const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; return hexColorRegex.test(color); }; /** * Check if value is valid slug */ export const isSlug = (slug: string): boolean => { const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; return slugRegex.test(slug); }; /** * Check if value is valid username * Requirements: 3-30 chars, alphanumeric + underscore, starts with letter */ export const isValidUsername = (username: string): boolean => { const usernameRegex = /^[a-zA-Z][a-zA-Z0-9_]{2,29}$/; return usernameRegex.test(username); }; /** * Check if object is empty */ export const isEmptyObject = (obj: object): boolean => { return Object.keys(obj).length === 0; }; /** * Check if array is empty */ export const isEmptyArray = (arr: T[]): boolean => { return arr.length === 0; }; /** * Check if value is null or undefined */ export const isNullOrUndefined = (value: unknown): value is null | undefined => { return value === null || value === undefined; }; /** * Check if value is defined (not null and not undefined) */ export const isDefined = (value: T | null | undefined): value is T => { return value !== null && value !== undefined; }; /** * Validate Mexican RFC (tax ID) */ export const isMexicanRFC = (rfc: string): boolean => { const rfcRegex = /^([A-ZÑ&]{3,4})(\d{2})(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([A-Z\d]{2})([A\d])$/; return rfcRegex.test(rfc.toUpperCase()); }; /** * Validate Mexican CURP */ export const isMexicanCURP = (curp: string): boolean => { const curpRegex = /^[A-Z]{4}\d{6}[HM][A-Z]{5}[A-Z\d]\d$/; return curpRegex.test(curp.toUpperCase()); }; /** * Validation result type */ export interface ValidationResult { isValid: boolean; errors: string[]; } /** * Create a validator chain */ export const createValidator = (value: T): { validate: (condition: boolean, errorMessage: string) => ReturnType>; result: () => ValidationResult; } => { const errors: string[] = []; const validator = { validate: (condition: boolean, errorMessage: string) => { if (!condition) { errors.push(errorMessage); } return validator; }, result: (): ValidationResult => ({ isValid: errors.length === 0, errors, }), }; return validator; };