import { z } from 'zod'; /** * RFC 5322 compliant email regex. * * This is more permissive than Zod's built-in `.email()` validator which rejects * valid international characters (e.g. "Søren@gmail.com"). * * Compiled once at module level to avoid re-compilation on every validation call. */ const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~\u{0080}-\u{FFFF}-]+@[a-zA-Z0-9\u{0080}-\u{FFFF}](?:[a-zA-Z0-9\u{0080}-\u{FFFF}-]{0,61}[a-zA-Z0-9\u{0080}-\u{FFFF}])?(?:\.[a-zA-Z0-9\u{0080}-\u{FFFF}](?:[a-zA-Z0-9\u{0080}-\u{FFFF}-]{0,61}[a-zA-Z0-9\u{0080}-\u{FFFF}])?)*$/u; const DEFAULT_EMAIL_MESSAGE = 'Invalid email address'; /** * Creates a Zod email schema using an RFC 5322 compliant regex. * * Supports international characters in the local part and domain * (e.g. "Søren@gmail.com", "user@dömain.com"). * * Returns a standard `ZodString` so all string methods are chainable: * `.min()`, `.max()`, `.trim()`, `.toLowerCase()`, `.optional()`, `.nullish()`, etc. * * @example * ```ts * zEmail() * zEmail().min(1).max(254) * zEmail().trim().toLowerCase() * zEmail('Email is invalid') * zEmail({ message: 'Email is invalid' }) * ``` */ export const zEmail = (options?: string | { message?: string }) => { const message = typeof options === 'string' ? options : (options?.message ?? DEFAULT_EMAIL_MESSAGE); return z.string().regex(EMAIL_REGEX, { message }); };