UNPKG

valia

Version:

A runtime data validator in TypeScript with advanced type inference, built-in validation functions, and seamless integration for server and client environments.

94 lines (75 loc) 2.71 kB
import { lazy } from "../utils"; import { isDomain } from "./isDomain"; import { ipV4Pattern, IPv6Pattern } from "./isIp"; interface IsEmailParams { /** **Default:** `false` */ allowQuotedString?: boolean; /** **Default:** `false` */ allowAddressLiteral?: boolean; /** **Default:** `false` */ allowGeneralAddressLiteral?: boolean; } const dotStringPattern = "(?:[-!=?A-B\\x23-\\x27\\x2A-\\x2B\\x2F-\\x39\\x5E-\\x7E]+(?:\\.[-!=?A-B\\x23-\\x27\\x2A-\\x2B\\x2F-\\x39\\x5E-\\x7E]+)*)"; const quotedStringPattern = "(?:\"(?:[\\x20-\\x21\\x23-\\x5B\\x5D-\\x7E]|\\\\[\\x20-\\x7E])*\")"; const localPartSimpleRegex = new RegExp(`^${dotStringPattern}$`); const localPartQuotedRegex = lazy(() => new RegExp(`^(?:${dotStringPattern}|${quotedStringPattern})$`)); const domainPartAddrLiteralRegex = lazy(() => new RegExp(`^\\[(?:IPv6:${IPv6Pattern}|${ipV4Pattern})\\]$`)); const domainPartGeneralAddrLiteralRegex = lazy(() => new RegExp(`[a-zA-Z0-9-]*[a-zA-Z0-9]+:[\\x21-\\x5A\\x5E-\\x7E]+)`)); function splitEmail(str: string) { const arrayLength = str.length; // FIND SYMBOL INDEX // /!\ Starts from the end because the local part allows "@" in quoted strings. let i = arrayLength - 1; while (i >= 0 && str[i] !== "@") { i--; } // CHECK SYMBOL CHAR if (str[i] !== "@") return (null); const symbolIndex = i; // CHECK LOCAL LENGTH if (!symbolIndex) return (null); // CHECK DOMAIN LENGTH /** @see https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1.2 */ const domainLength = arrayLength - (symbolIndex + 1); if (!domainLength || domainLength > 255) return (null); return { local: str.slice(0, symbolIndex), domain: str.slice(symbolIndex + 1, arrayLength) }; } function isValidLocalPart(str: string, params?: IsEmailParams): boolean { if (localPartSimpleRegex.test(str)) { return (true); } else if (params?.allowQuotedString && localPartQuotedRegex().test(str)) { return (true); } return (false); } function isValidDomainPart(str: string, params?: IsEmailParams): boolean { if (isDomain(str)) return (true); if (params?.allowAddressLiteral && domainPartAddrLiteralRegex().test(str)) return (true); if (params?.allowGeneralAddressLiteral && domainPartGeneralAddrLiteralRegex().test(str)) return (true); return (false); } /** * **Standard :** RFC 5321 * * @see https://datatracker.ietf.org/doc/html/rfc5321#section-4.1.2 * * **Follows :** * `Mailbox` * * @version 1.1.0-beta */ export function isEmail(str: string, params?: IsEmailParams): boolean { const parts = splitEmail(str); if (!parts) return (false); if ( isValidLocalPart(parts.local, params) && isValidDomainPart(parts.domain, params) ) return (true); return (false); }