Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | /**
* Validation Error Types and Utilities
*
* Standardized error format for all validation operations.
*/
import { ZodError, ZodIssue } from 'zod';
// ============================================================================
// TYPES
// ============================================================================
/**
* Standardized validation error format
*/
export interface ValidationError {
/** Field path (e.g., "email" or "address.city") */
field: string;
/** Human-readable error message */
message: string;
/** Zod error code for programmatic handling */
code: string;
}
/**
* Result of a validation operation
*/
export type ValidationResult<T> =
| { success: true; data: T; errors?: never }
| { success: false; data?: never; errors: ValidationError[] };
/**
* Field-to-message mapping for form display
*/
export type FieldErrors = Record<string, string>;
// ============================================================================
// ERROR FORMATTING
// ============================================================================
/**
* Convert a ZodError to an array of ValidationErrors
*/
export function formatZodError(error: ZodError): ValidationError[] {
return error.issues.map((issue: ZodIssue) => ({
field: issue.path.join('.') || '_root',
message: issue.message,
code: issue.code,
}));
}
/**
* Convert a ZodError to a field-message map for form display
*/
export function formatZodErrorToFieldMap(error: ZodError): FieldErrors {
const fieldErrors: FieldErrors = {};
error.issues.forEach((issue: ZodIssue) => {
const field = issue.path.join('.') || '_root';
// Only keep first error per field
if (!fieldErrors[field]) {
fieldErrors[field] = issue.message;
}
});
return fieldErrors;
}
/**
* Convert ValidationError array to FieldErrors map
*/
export function toFieldErrors(errors: ValidationError[]): FieldErrors {
return errors.reduce((acc, error) => {
if (!acc[error.field]) {
acc[error.field] = error.message;
}
return acc;
}, {} as FieldErrors);
}
/**
* Get the first error message from a FieldErrors map
*/
export function getFirstError(errors: FieldErrors): string | null {
const firstField = Object.keys(errors)[0];
return firstField ? errors[firstField] : null;
}
/**
* Check if an unknown error is a ZodError
*/
export function isZodError(error: unknown): error is ZodError {
return error instanceof ZodError;
}
// ============================================================================
// ERROR MESSAGES
// ============================================================================
/**
* Standard validation error messages
*/
export const ValidationMessages = {
REQUIRED: 'This field is required',
INVALID_EMAIL: 'Please enter a valid email address',
INVALID_PHONE: 'Please enter a valid phone number',
INVALID_URL: 'Please enter a valid URL',
INVALID_DATE: 'Please enter a valid date',
PASSWORD_TOO_SHORT: 'Password must be at least 8 characters',
PASSWORD_REQUIREMENTS: 'Password must contain uppercase, lowercase, and number',
MIN_LENGTH: (min: number) => `Must be at least ${min} characters`,
MAX_LENGTH: (max: number) => `Must be less than ${max} characters`,
MIN_VALUE: (min: number) => `Must be at least ${min}`,
MAX_VALUE: (max: number) => `Must be at most ${max}`,
INVALID_FORMAT: 'Invalid format',
} as const;
|