email-utils
API reference

@email-utils/validator-syntax

The API of @email-utils/validator-syntax 1.0.0-rc.4.

Interfaces

AddressComment

A comment lifted out of an address.

Properties

position
position: 
  | "before-local"
  | "inside-local"
  | "after-local"
  | "before-domain"
  | "inside-domain"
  | "after-domain";

Where the comment sat. RFC 5322's obsolete syntax also allows comments between the words of the local part and the labels of the domain (test.(comment)test@example.com): those are inside-local and inside-domain.

text
text: string;

The comment's text, without the parentheses.


ParsedAddress

An address split into its parts, as parseAddress returns it.

Properties

comments
comments: AddressComment[];

Comments in input order; empty when there are none or they aren't allowed.

domain
domain: string;

The domain, without comments or folding whitespace.

local
local: string;

The local part, without comments or folding whitespace.

tld?
optional tld?: string;

The last label; absent for a domain literal, or a dotless domain the preset allows.


SyntaxOptions

Options for parseAddress and the other entry points.

Properties

allowComments?
optional allowComments?: boolean;

Accept RFC 5322 comments, like ada(work)@example.com. In practical they may sit only at the ends of the local part and the domain; in rfc5322, around every word and label too.

Remarks

Throws TypeError when true with rfc5321 or html5, whose grammars have no comments.

Default Value

true in rfc5322, false elsewhere

allowIdn?
optional allowIdn?: boolean;

Accept internationalized domain names written as U-labels, like ada@bücher.example. Each label must convert to an A-label (xn--bcher-kva) under UTS #46, and the 63 and 253 caps apply to the A-label form. ParsedAddress.domain and tld keep the labels as written. A-labels are hostname labels, so every preset accepts them without this.

Remarks

Throws TypeError when true with html5, whose grammar is ASCII.

Default Value

false

allowIpLiteral?
optional allowIpLiteral?: boolean;

Accept a domain literal: an IPv4 or IPv6: address literal (ada@[192.0.2.1]), or in rfc5322, any text its grammar allows in brackets. false turns them off in the RFC presets.

Remarks

Throws TypeError when true with html5, whose grammar has none.

Default Value

true in rfc5321 and rfc5322, false elsewhere

allowNoTld?
optional allowNoTld?: boolean;

Accept a domain with no dot, like localhost.

Default Value

true in html5, false elsewhere

allowUnicode?
optional allowUnicode?: boolean;

Accept non-ASCII characters in the local part, as RFC 6531 (SMTPUTF8) does, like 用户@example.com: in atoms and quoted strings, and in comments where they're allowed. The 64 cap then counts UTF-8 octets.

Remarks

Throws TypeError when true with html5, whose grammar is ASCII.

Default Value

false

checkTld?
optional checkTld?: boolean;

Check the TLD against the IANA set.

Default Value

true in practical, false elsewhere

maxLength?
optional maxLength?: number;

The longest input accepted, counted in UTF-16 code units, as email.length counts. Longer input fails as syntax.address.too_long before it's scanned, so it's rejected in constant time however long it is. A positive integer, or Infinity for no limit.

Remarks

The address caps (254 characters, and 64 and 253 for the local part and domain where the preset has them) still apply within it. This bounds the input as written, comments and folding whitespace included, which those caps don't count. Throws TypeError when it isn't a positive integer or Infinity.

Default Value

512 in every preset

preset?
optional preset?: Preset;

The base rule set:

  • practical: what real mailboxes look like. Dot-atom local parts, hostname domains, a TLD from the IANA set.
  • rfc5321: what SMTP accepts on the wire, quoted local parts and IP address literals included.
  • rfc5322: RFC 5322's full addr-spec, comments, folding whitespace, and obsolete syntax included.
  • html5: exactly what a browser's input[type=email] accepts.
Default Value

'practical'


SyntaxValidator

parseAddress and isValidSyntax with options bound.

Properties

maxLength
readonly maxLength: number;

The maxLength this validator applies: the one in its options, or the default, 512. Infinity when there's no limit.

Remarks

For a dependent that trims or otherwise pre-processes input before parsing it: checking the raw input's length against this first rejects oversized input before any of that work, and padding can't carry input past the limit. The validator is frozen, so this can't be reassigned.

Example
import { createSyntaxValidator } from '@email-utils/validator-syntax';

createSyntaxValidator().maxLength; // => 512
createSyntaxValidator({ maxLength: 1024 }).maxLength; // => 1024

const validator = createSyntaxValidator();
const padded = `${' '.repeat(600)}ada@example.com`;
padded.length > validator.maxLength; // => true
validator.isValid(padded.trim()); // => true

Methods

isValid()
isValid(email): boolean;
Parameters
ParameterType
emailstring
Returns

boolean

parse()
parse(email): Result<ParsedAddress>;
Parameters
ParameterType
emailstring
Returns

Result<ParsedAddress>

Type Aliases

Preset

type Preset = "practical" | "rfc5321" | "rfc5322" | "html5";

A base rule set; see SyntaxOptions.preset.


ReasonCode

type ReasonCode = 
  | "syntax.address.empty"
  | "syntax.address.no_at"
  | "syntax.address.too_long"
  | "syntax.local.empty"
  | "syntax.local.too_long"
  | "syntax.local.invalid_char"
  | "syntax.local.consecutive_dots"
  | "syntax.local.unquoted_space"
  | "syntax.domain.empty"
  | "syntax.domain.no_dot"
  | "syntax.domain.label_invalid"
  | "syntax.domain.literal_invalid"
  | "syntax.domain.too_long"
  | "syntax.domain.invalid_char"
  | "syntax.comment.not_allowed"
  | "syntax.comment.unterminated"
  | "syntax.tld.unknown";

The syntax.* codes from the reason-code catalogue (https://email-utils.github.io/meta/docs/reference/reason-codes#validator-syntax).


Result

type Result<T> = 
  | {
  ok: true;
  value: T;
}
  | {
  index?: number;
  message?: string;
  ok: false;
  reason: ReasonCode;
};

A validation result: the value on success, or the first check that failed. Branch on reason, never on message, which isn't semver-stable.

Type Parameters

Type Parameter
T

Functions

createSyntaxValidator()

function createSyntaxValidator(options?): SyntaxValidator;

Binds options once, checking them up front, and returns parseAddress and isValidSyntax with them applied, and the maxLength they resolve to. The validator is frozen.

Parameters

ParameterType
options?SyntaxOptions

Returns

SyntaxValidator

Example

import { createSyntaxValidator } from '@email-utils/validator-syntax';

const strict = createSyntaxValidator({ preset: 'rfc5321' });
strict.parse('a b@example.com');
// => { ok: false, reason: 'syntax.local.unquoted_space', index: 1 }
strict.isValid('"a b"@example.com'); // => true
createSyntaxValidator({ preset: 'html5', allowComments: true });
// => throws TypeError

Throws

TypeError when options are malformed.


isValidSyntax()

function isValidSyntax(email, options?): boolean;

Whether email is well-formed: exactly parseAddress(email, options).ok.

Parameters

ParameterType
emailstring
options?SyntaxOptions

Returns

boolean

Example

import { isValidSyntax } from '@email-utils/validator-syntax';

isValidSyntax('ada@example.com'); // => true
isValidSyntax('"ada"@example.com'); // => false
isValidSyntax('"ada"@example.com', { preset: 'rfc5321' }); // => true

Throws

TypeError when email isn't a string, or options are malformed.


parseAddress()

function parseAddress(email, options?): Result<ParsedAddress>;

Parses email into its local part, domain, TLD, and comments, or returns the first check it fails.

Parameters

ParameterType
emailstring
options?SyntaxOptions

Returns

Result<ParsedAddress>

Remarks

Input longer than maxLength (512 UTF-16 code units by default) fails as syntax.address.too_long before it's scanned. Otherwise the local part is checked left to right, then its length; then the domain, left to right, then its length; then the address length, a dotless domain, and the TLD. index, where a failure has one, points at the offending character.

Example

import { parseAddress } from '@email-utils/validator-syntax';

parseAddress('ada.lovelace@example.co.uk');
// => { ok: true, value: { local: 'ada.lovelace', tld: 'uk' } }
parseAddress('ada..lovelace@example.com');
// => { ok: false, reason: 'syntax.local.consecutive_dots', index: 4 }

Throws

TypeError when email isn't a string, or options are malformed.

On this page