// Modern validation utilities with ES2024 features /** * Modern validation result type */ export type ValidationResult = | { success: true; data: T; } | { success: false; error: string; errors?: Record; }; /** * Create a successful validation result */ export function success(data: T): ValidationResult { return { success: true, data }; } /** * Create a failed validation result */ export function failure( error: string, errors?: Record, ): ValidationResult { return { success: false, error, errors }; } /** * Enhanced email validation with better error reporting */ export function validateEmail(email: string): ValidationResult { if (!email || typeof email !== "string") { return failure("Email is required"); } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { return failure("Invalid email format"); } const trimmed = email.trim().toLowerCase(); if (trimmed.length > 254) { return failure("Email is too long"); } return success(trimmed); } /** * Legacy boolean validation (kept for backward compatibility) */ export function isValidEmail(email: string): boolean { const result = validateEmail(email); return result.success; } /** * Enhanced phone validation with better error reporting */ export function validatePhoneNumber(phone: string): ValidationResult { if (!phone || typeof phone !== "string") { return failure("Phone number is required"); } const phoneRegex = /^\+?[\d\s\-()]{10,}$/; if (!phoneRegex.test(phone)) { return failure("Invalid phone number format"); } const cleaned = phone.replace(/[\s\-()]/g, ""); if (cleaned.length < 10) { return failure("Phone number is too short"); } return success(cleaned); } /** * Legacy boolean validation (kept for backward compatibility) */ export function isValidPhoneNumber(phone: string): boolean { const result = validatePhoneNumber(phone); return result.success; } /** * Validates password strength */ export function isStrongPassword(password: string): boolean { // At least 8 characters, one uppercase, one lowercase, one number const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/; return passwordRegex.test(password); } /** * Validates UUID format */ export function isValidUUID(uuid: string): boolean { const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; return uuidRegex.test(uuid); } /** * Sanitizes string by removing HTML tags and scripts */ export function sanitizeString(input: string): string { return input .replace(/)<[^<]*)*<\/script>/gi, "") .replace(/<[^>]*>/g, "") .trim(); } /** * Formats currency amount */ export function formatCurrency(amount: number, currency = "USD"): string { return new Intl.NumberFormat("en-US", { style: "currency", currency, }).format(amount); } /** * Formats date to locale string */ export function formatDate(date: Date | string, locale = "en-US"): string { const dateObj = typeof date === "string" ? new Date(date) : date; return dateObj.toLocaleDateString(locale); } /** * Truncates string to specified length with ellipsis */ export function truncateString(str: string, maxLength: number): string { if (str.length <= maxLength) return str; return str.slice(0, maxLength - 3) + "..."; }