/
emailAddress.ts
93 lines (89 loc) · 3.09 KB
/
emailAddress.ts
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
import { array } from './array';
import { buildLowerAlphaNumericArbitrary } from './_internals/builders/CharacterRangeArbitraryBuilder';
import { domain } from './domain';
import { stringOf } from './stringOf';
import { tuple } from './tuple';
import type { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
import type { SizeForArbitrary } from './_internals/helpers/MaxLengthFromMinLength';
import type { AdapterOutput } from './_internals/AdapterArbitrary';
import { adapter } from './_internals/AdapterArbitrary';
import { safeJoin, safeSlice, safeSplit } from '../utils/globals';
/** @internal */
function dotAdapter(a: string[]): AdapterOutput<string[]> {
// According to RFC 2821:
// The maximum total length of a user name or other local-part is 64 characters.
let currentLength = a[0].length; // always at least one element
for (let index = 1; index !== a.length; ++index) {
currentLength += 1 + a[index].length;
if (currentLength > 64) {
return { adapted: true, value: safeSlice(a, 0, index) };
}
}
return { adapted: false, value: a };
}
/** @internal */
function dotMapper(a: string[]): string {
return safeJoin(a, '.');
}
/** @internal */
function dotUnmapper(value: unknown): string[] {
if (typeof value !== 'string') {
throw new Error('Unsupported');
}
return safeSplit(value, '.');
}
/** @internal */
function atMapper(data: [string, string]): string {
return `${data[0]}@${data[1]}`;
}
/** @internal */
function atUnmapper(value: unknown): [string, string] {
if (typeof value !== 'string') {
throw new Error('Unsupported');
}
return safeSplit(value, '@', 2) as [string, string];
}
/**
* Constraints to be applied on {@link emailAddress}
* @remarks Since 2.22.0
* @public
*/
export interface EmailAddressConstraints {
/**
* Define how large the generated values should be (at max)
* @remarks Since 2.22.0
*/
size?: Exclude<SizeForArbitrary, 'max'>;
}
/**
* For email address
*
* According to {@link https://www.ietf.org/rfc/rfc2821.txt | RFC 2821},
* {@link https://www.ietf.org/rfc/rfc3696.txt | RFC 3696} and
* {@link https://www.ietf.org/rfc/rfc5322.txt | RFC 5322}
*
* @param constraints - Constraints to apply when building instances (since 2.22.0)
*
* @remarks Since 1.14.0
* @public
*/
export function emailAddress(constraints: EmailAddressConstraints = {}): Arbitrary<string> {
const others = ['!', '#', '$', '%', '&', "'", '*', '+', '-', '/', '=', '?', '^', '_', '`', '{', '|', '}', '~'];
const atextArb = buildLowerAlphaNumericArbitrary(others);
const localPartArb = adapter(
// Maximal length for the output of dotMapper is 64,
// In other words:
// - `stringOf(atextArb, ...)` cannot produce values having more than 64 characters
// - `array(...)` cannot produce more than 32 values
array(
stringOf(atextArb, {
minLength: 1,
maxLength: 64,
size: constraints.size,
}),
{ minLength: 1, maxLength: 32, size: constraints.size },
),
dotAdapter,
).map(dotMapper, dotUnmapper);
return tuple(localPartArb, domain({ size: constraints.size })).map(atMapper, atUnmapper);
}