@email-utils/validator-syntax 0.0.1-9 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,2138 @@
1
+ const table = /* @__PURE__ */ new Uint8Array(128);
2
+ function mark(chars, flag) {
3
+ for (let i = 0; i < chars.length; i++) table[chars.charCodeAt(i)] |= flag;
4
+ }
5
+ const alnum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
6
+ mark(alnum + "!#$%&'*+-/=?^_`{|}~", 1);
7
+ mark(alnum + "#$&'*+-/=?^_`{|}~", 2);
8
+ mark(alnum + "!#$%&'*+-/=?^_`{|}~.", 4);
9
+ mark(alnum + "-", 8);
10
+ /** Whether the character with this code is in the class `flag`. */
11
+ function inClass(code, flag) {
12
+ return code < 128 && (table[code] & flag) !== 0;
13
+ }
14
+ /** Space, tab, CR, and LF: the characters folding whitespace is made of. */
15
+ function isWhitespace(code) {
16
+ return code === 32 || code === 9 || code === 13 || code === 10;
17
+ }
18
+ /**
19
+ * The code units in the non-ASCII code point at `i`, before `end`: 2 for a
20
+ * surrogate pair, 1 for anything else outside ASCII, and 0 for ASCII or a
21
+ * lone surrogate, which no UTF-8 text can hold.
22
+ */
23
+ function nonAsciiAt(text, i, end) {
24
+ const code = text.charCodeAt(i);
25
+ if (code < 128 || code >= 56320 && code <= 57343) return 0;
26
+ if (code < 55296 || code > 56319) return 1;
27
+ const low = i + 1 < end ? text.charCodeAt(i + 1) : 0;
28
+ return low >= 56320 && low <= 57343 ? 2 : 0;
29
+ }
30
+ /** The length of `text` in UTF-8 octets, the unit RFC 6531 caps a local part in. */
31
+ function utf8Length(text) {
32
+ let octets = text.length;
33
+ for (let i = 0; i < text.length; i++) {
34
+ const code = text.charCodeAt(i);
35
+ if (code >= 2048 && (code < 55296 || code > 57343)) octets += 2;
36
+ else if (code >= 128) octets += 1;
37
+ }
38
+ return octets;
39
+ }
40
+ //#endregion
41
+ //#region src/options.ts
42
+ const presets = {
43
+ practical: {
44
+ local: 2,
45
+ domain: 8,
46
+ quotes: false,
47
+ literals: false,
48
+ comments: false,
49
+ obs: false,
50
+ localCap: 64,
51
+ domainCap: 253,
52
+ checkTld: true,
53
+ allowNoTld: false,
54
+ unicode: false,
55
+ idn: false
56
+ },
57
+ rfc5321: {
58
+ local: 1,
59
+ domain: 8,
60
+ quotes: true,
61
+ literals: true,
62
+ comments: false,
63
+ obs: false,
64
+ localCap: 64,
65
+ domainCap: 253,
66
+ checkTld: false,
67
+ allowNoTld: false,
68
+ unicode: false,
69
+ idn: false
70
+ },
71
+ rfc5322: {
72
+ local: 1,
73
+ domain: 1,
74
+ quotes: true,
75
+ literals: true,
76
+ comments: true,
77
+ obs: true,
78
+ localCap: Infinity,
79
+ domainCap: 253,
80
+ checkTld: false,
81
+ allowNoTld: false,
82
+ unicode: false,
83
+ idn: false
84
+ },
85
+ html5: {
86
+ local: 4,
87
+ domain: 8,
88
+ quotes: false,
89
+ literals: false,
90
+ comments: false,
91
+ obs: false,
92
+ localCap: Infinity,
93
+ domainCap: Infinity,
94
+ checkTld: false,
95
+ allowNoTld: true,
96
+ unicode: false,
97
+ idn: false
98
+ }
99
+ };
100
+ const overrides = [
101
+ "checkTld",
102
+ "allowNoTld",
103
+ "allowComments",
104
+ "allowUnicode",
105
+ "allowIdn",
106
+ "allowIpLiteral"
107
+ ];
108
+ const unsupported = {
109
+ practical: [],
110
+ rfc5321: ["allowComments"],
111
+ rfc5322: [],
112
+ html5: [
113
+ "allowComments",
114
+ "allowUnicode",
115
+ "allowIdn",
116
+ "allowIpLiteral"
117
+ ]
118
+ };
119
+ /**
120
+ * Resolves `options` into the rules they select.
121
+ *
122
+ * @throws TypeError when `options` isn't an object, names an unknown option
123
+ * or preset, gives a non-boolean override, or turns on something the
124
+ * preset's grammar has no room for.
125
+ */
126
+ function resolve(options) {
127
+ if (options === void 0) return presets.practical;
128
+ if (typeof options !== "object" || options === null) throw new TypeError("Expected the options to be an object");
129
+ for (const key of Object.keys(options)) if (key !== "preset" && !overrides.includes(key)) throw new TypeError(`Unknown option: ${key}`);
130
+ const preset = options.preset ?? "practical";
131
+ if (!Object.hasOwn(presets, preset)) throw new TypeError(`Unknown preset: ${preset}`);
132
+ const base = presets[preset];
133
+ for (const key of overrides) {
134
+ const value = options[key];
135
+ if (value !== void 0 && typeof value !== "boolean") throw new TypeError(`Expected ${key} to be a boolean`);
136
+ }
137
+ for (const key of unsupported[preset]) if (options[key] === true) throw new TypeError(`${key} can’t be true with the ${preset} preset`);
138
+ if (overrides.every((key) => options[key] === void 0)) return base;
139
+ return {
140
+ ...base,
141
+ checkTld: options.checkTld ?? base.checkTld,
142
+ allowNoTld: options.allowNoTld ?? base.allowNoTld,
143
+ comments: options.allowComments ?? base.comments,
144
+ unicode: options.allowUnicode ?? base.unicode,
145
+ idn: options.allowIdn ?? base.idn,
146
+ literals: options.allowIpLiteral ?? base.literals
147
+ };
148
+ }
149
+ //#endregion
150
+ //#region src/idn.ts
151
+ const ldh = /^[\da-z-]+$/;
152
+ /**
153
+ * The A-label for a U-label, or `undefined` when UTS #46 rejects it or maps
154
+ * it to something other than one IDN label (plain ASCII, or two labels, as
155
+ * `。` becomes a dot).
156
+ *
157
+ * @remarks
158
+ * Converts by way of the WHATWG URL host parser, which every runtime the
159
+ * package supports has and which applies UTS #46 with its bidi and joiner
160
+ * checks. It leaves hyphens and lengths alone, so the caller checks those.
161
+ */
162
+ function toALabel(label) {
163
+ let host;
164
+ try {
165
+ host = new URL(`http://x.${label}`).hostname;
166
+ } catch {
167
+ return;
168
+ }
169
+ const aLabel = host.slice(2);
170
+ return aLabel.startsWith("xn--") && ldh.test(aLabel) ? aLabel : void 0;
171
+ }
172
+ //#endregion
173
+ //#region src/ip.ts
174
+ const octet = /^\d{1,3}$/;
175
+ const hexGroup = /^[\dA-Fa-f]{1,4}$/;
176
+ /** An IPv4 address: four dot-separated decimal octets, each 0–255. */
177
+ function isIPv4(text) {
178
+ const parts = text.split(".");
179
+ return parts.length === 4 && parts.every((part) => octet.test(part) && Number(part) <= 255);
180
+ }
181
+ /**
182
+ * An IPv6 address as RFC 5321 writes it: eight hex groups, or a `::` with no
183
+ * more than six groups beside it, and either may end in an IPv4 address in
184
+ * place of the last two groups.
185
+ */
186
+ function isIPv6(text) {
187
+ const colon = text.lastIndexOf(":");
188
+ const tail = text.slice(colon + 1);
189
+ if (tail.includes(".")) {
190
+ if (!isIPv4(tail)) return false;
191
+ text = `${text.slice(0, colon + 1)}0:0`;
192
+ }
193
+ const halves = text.split("::");
194
+ if (halves.length > 2) return false;
195
+ const groups = halves.flatMap((half) => half === "" ? [] : half.split(":"));
196
+ return groups.every((group) => hexGroup.test(group)) && (halves.length === 1 ? groups.length === 8 : groups.length <= 6);
197
+ }
198
+ /** The text of an RFC 5321 address literal: IPv4, or `IPv6:` and IPv6. */
199
+ function isAddressLiteral(text) {
200
+ return /^IPv6:/i.test(text) ? isIPv6(text.slice(5)) : isIPv4(text);
201
+ }
202
+ //#endregion
203
+ //#region node_modules/tlds/index.json
204
+ var tlds_default = [
205
+ "aaa",
206
+ "aarp",
207
+ "abb",
208
+ "abbott",
209
+ "abbvie",
210
+ "abc",
211
+ "able",
212
+ "abogado",
213
+ "abudhabi",
214
+ "ac",
215
+ "academy",
216
+ "accenture",
217
+ "accountant",
218
+ "accountants",
219
+ "aco",
220
+ "actor",
221
+ "ad",
222
+ "ads",
223
+ "adult",
224
+ "ae",
225
+ "aeg",
226
+ "aero",
227
+ "aetna",
228
+ "af",
229
+ "afl",
230
+ "africa",
231
+ "ag",
232
+ "agakhan",
233
+ "agency",
234
+ "ai",
235
+ "aig",
236
+ "airbus",
237
+ "airforce",
238
+ "airtel",
239
+ "akdn",
240
+ "al",
241
+ "alibaba",
242
+ "alipay",
243
+ "allfinanz",
244
+ "allstate",
245
+ "ally",
246
+ "alsace",
247
+ "alstom",
248
+ "am",
249
+ "amazon",
250
+ "americanexpress",
251
+ "americanfamily",
252
+ "amex",
253
+ "amfam",
254
+ "amica",
255
+ "amsterdam",
256
+ "analytics",
257
+ "android",
258
+ "anquan",
259
+ "anz",
260
+ "ao",
261
+ "aol",
262
+ "apartments",
263
+ "app",
264
+ "apple",
265
+ "aq",
266
+ "aquarelle",
267
+ "ar",
268
+ "arab",
269
+ "aramco",
270
+ "archi",
271
+ "army",
272
+ "arpa",
273
+ "art",
274
+ "arte",
275
+ "as",
276
+ "asda",
277
+ "asia",
278
+ "associates",
279
+ "at",
280
+ "athleta",
281
+ "attorney",
282
+ "au",
283
+ "auction",
284
+ "audi",
285
+ "audible",
286
+ "audio",
287
+ "auspost",
288
+ "author",
289
+ "auto",
290
+ "autos",
291
+ "aw",
292
+ "aws",
293
+ "ax",
294
+ "axa",
295
+ "az",
296
+ "azure",
297
+ "ba",
298
+ "baby",
299
+ "baidu",
300
+ "banamex",
301
+ "band",
302
+ "bank",
303
+ "bar",
304
+ "barcelona",
305
+ "barclaycard",
306
+ "barclays",
307
+ "barefoot",
308
+ "bargains",
309
+ "baseball",
310
+ "basketball",
311
+ "bauhaus",
312
+ "bayern",
313
+ "bb",
314
+ "bbc",
315
+ "bbt",
316
+ "bbva",
317
+ "bcg",
318
+ "bcn",
319
+ "bd",
320
+ "be",
321
+ "beats",
322
+ "beauty",
323
+ "beer",
324
+ "berlin",
325
+ "best",
326
+ "bestbuy",
327
+ "bet",
328
+ "bf",
329
+ "bg",
330
+ "bh",
331
+ "bharti",
332
+ "bi",
333
+ "bible",
334
+ "bid",
335
+ "bike",
336
+ "bing",
337
+ "bingo",
338
+ "bio",
339
+ "biz",
340
+ "bj",
341
+ "black",
342
+ "blackfriday",
343
+ "blockbuster",
344
+ "blog",
345
+ "bloomberg",
346
+ "blue",
347
+ "bm",
348
+ "bms",
349
+ "bmw",
350
+ "bn",
351
+ "bnpparibas",
352
+ "bo",
353
+ "boats",
354
+ "boehringer",
355
+ "bofa",
356
+ "bom",
357
+ "bond",
358
+ "boo",
359
+ "book",
360
+ "booking",
361
+ "bosch",
362
+ "bostik",
363
+ "boston",
364
+ "bot",
365
+ "boutique",
366
+ "box",
367
+ "br",
368
+ "bradesco",
369
+ "bridgestone",
370
+ "broadway",
371
+ "broker",
372
+ "brother",
373
+ "brussels",
374
+ "bs",
375
+ "bt",
376
+ "build",
377
+ "builders",
378
+ "business",
379
+ "buy",
380
+ "buzz",
381
+ "bv",
382
+ "bw",
383
+ "by",
384
+ "bz",
385
+ "bzh",
386
+ "ca",
387
+ "cab",
388
+ "cafe",
389
+ "cal",
390
+ "call",
391
+ "calvinklein",
392
+ "cam",
393
+ "camera",
394
+ "camp",
395
+ "canon",
396
+ "capetown",
397
+ "capital",
398
+ "capitalone",
399
+ "car",
400
+ "caravan",
401
+ "cards",
402
+ "care",
403
+ "career",
404
+ "careers",
405
+ "cars",
406
+ "casa",
407
+ "case",
408
+ "cash",
409
+ "casino",
410
+ "cat",
411
+ "catering",
412
+ "catholic",
413
+ "cba",
414
+ "cbn",
415
+ "cbre",
416
+ "cc",
417
+ "cd",
418
+ "center",
419
+ "ceo",
420
+ "cern",
421
+ "cf",
422
+ "cfa",
423
+ "cfd",
424
+ "cg",
425
+ "ch",
426
+ "chanel",
427
+ "channel",
428
+ "charity",
429
+ "chase",
430
+ "chat",
431
+ "cheap",
432
+ "chintai",
433
+ "christmas",
434
+ "chrome",
435
+ "church",
436
+ "ci",
437
+ "cipriani",
438
+ "circle",
439
+ "cisco",
440
+ "citadel",
441
+ "citi",
442
+ "citic",
443
+ "city",
444
+ "ck",
445
+ "cl",
446
+ "claims",
447
+ "cleaning",
448
+ "click",
449
+ "clinic",
450
+ "clinique",
451
+ "clothing",
452
+ "cloud",
453
+ "club",
454
+ "clubmed",
455
+ "cm",
456
+ "cn",
457
+ "co",
458
+ "coach",
459
+ "codes",
460
+ "coffee",
461
+ "college",
462
+ "cologne",
463
+ "com",
464
+ "commbank",
465
+ "community",
466
+ "company",
467
+ "compare",
468
+ "computer",
469
+ "comsec",
470
+ "condos",
471
+ "construction",
472
+ "consulting",
473
+ "contact",
474
+ "contractors",
475
+ "cooking",
476
+ "cool",
477
+ "coop",
478
+ "corsica",
479
+ "country",
480
+ "coupon",
481
+ "coupons",
482
+ "courses",
483
+ "cpa",
484
+ "cr",
485
+ "credit",
486
+ "creditcard",
487
+ "creditunion",
488
+ "cricket",
489
+ "crown",
490
+ "crs",
491
+ "cruise",
492
+ "cruises",
493
+ "cu",
494
+ "cuisinella",
495
+ "cv",
496
+ "cw",
497
+ "cx",
498
+ "cy",
499
+ "cymru",
500
+ "cyou",
501
+ "cz",
502
+ "dad",
503
+ "dance",
504
+ "data",
505
+ "date",
506
+ "dating",
507
+ "datsun",
508
+ "day",
509
+ "dclk",
510
+ "dds",
511
+ "de",
512
+ "deal",
513
+ "dealer",
514
+ "deals",
515
+ "degree",
516
+ "delivery",
517
+ "dell",
518
+ "deloitte",
519
+ "delta",
520
+ "democrat",
521
+ "dental",
522
+ "dentist",
523
+ "desi",
524
+ "design",
525
+ "dev",
526
+ "dhl",
527
+ "diamonds",
528
+ "diet",
529
+ "digital",
530
+ "direct",
531
+ "directory",
532
+ "discount",
533
+ "discover",
534
+ "dish",
535
+ "diy",
536
+ "dj",
537
+ "dk",
538
+ "dm",
539
+ "dnp",
540
+ "do",
541
+ "docs",
542
+ "doctor",
543
+ "dog",
544
+ "domains",
545
+ "dot",
546
+ "download",
547
+ "drive",
548
+ "dtv",
549
+ "dubai",
550
+ "dupont",
551
+ "durban",
552
+ "dvag",
553
+ "dvr",
554
+ "dz",
555
+ "earth",
556
+ "eat",
557
+ "ec",
558
+ "eco",
559
+ "edeka",
560
+ "edu",
561
+ "education",
562
+ "ee",
563
+ "eg",
564
+ "email",
565
+ "emerck",
566
+ "energy",
567
+ "engineer",
568
+ "engineering",
569
+ "enterprises",
570
+ "epson",
571
+ "equipment",
572
+ "er",
573
+ "ericsson",
574
+ "erni",
575
+ "es",
576
+ "esq",
577
+ "estate",
578
+ "et",
579
+ "eu",
580
+ "eurovision",
581
+ "eus",
582
+ "events",
583
+ "exchange",
584
+ "expert",
585
+ "exposed",
586
+ "express",
587
+ "extraspace",
588
+ "fage",
589
+ "fail",
590
+ "fairwinds",
591
+ "faith",
592
+ "family",
593
+ "fan",
594
+ "fans",
595
+ "farm",
596
+ "farmers",
597
+ "fashion",
598
+ "fast",
599
+ "fedex",
600
+ "feedback",
601
+ "ferrari",
602
+ "ferrero",
603
+ "fi",
604
+ "fidelity",
605
+ "fido",
606
+ "film",
607
+ "final",
608
+ "finance",
609
+ "financial",
610
+ "fire",
611
+ "firestone",
612
+ "firmdale",
613
+ "fish",
614
+ "fishing",
615
+ "fit",
616
+ "fitness",
617
+ "fj",
618
+ "fk",
619
+ "flickr",
620
+ "flights",
621
+ "flir",
622
+ "florist",
623
+ "flowers",
624
+ "fly",
625
+ "fm",
626
+ "fo",
627
+ "foo",
628
+ "food",
629
+ "football",
630
+ "ford",
631
+ "forex",
632
+ "forsale",
633
+ "forum",
634
+ "foundation",
635
+ "fox",
636
+ "fr",
637
+ "free",
638
+ "fresenius",
639
+ "frl",
640
+ "frogans",
641
+ "frontier",
642
+ "ftr",
643
+ "fujitsu",
644
+ "fun",
645
+ "fund",
646
+ "furniture",
647
+ "futbol",
648
+ "fyi",
649
+ "ga",
650
+ "gal",
651
+ "gallery",
652
+ "gallo",
653
+ "gallup",
654
+ "game",
655
+ "games",
656
+ "gap",
657
+ "garden",
658
+ "gay",
659
+ "gb",
660
+ "gbiz",
661
+ "gd",
662
+ "gdn",
663
+ "ge",
664
+ "gea",
665
+ "gent",
666
+ "genting",
667
+ "george",
668
+ "gf",
669
+ "gg",
670
+ "ggee",
671
+ "gh",
672
+ "gi",
673
+ "gift",
674
+ "gifts",
675
+ "gives",
676
+ "giving",
677
+ "gl",
678
+ "glass",
679
+ "gle",
680
+ "global",
681
+ "globo",
682
+ "gm",
683
+ "gmail",
684
+ "gmbh",
685
+ "gmo",
686
+ "gmx",
687
+ "gn",
688
+ "godaddy",
689
+ "gold",
690
+ "goldpoint",
691
+ "golf",
692
+ "goo",
693
+ "goodyear",
694
+ "goog",
695
+ "google",
696
+ "gop",
697
+ "got",
698
+ "gov",
699
+ "gp",
700
+ "gq",
701
+ "gr",
702
+ "grainger",
703
+ "graphics",
704
+ "gratis",
705
+ "green",
706
+ "gripe",
707
+ "grocery",
708
+ "group",
709
+ "gs",
710
+ "gt",
711
+ "gu",
712
+ "gucci",
713
+ "guge",
714
+ "guide",
715
+ "guitars",
716
+ "guru",
717
+ "gw",
718
+ "gy",
719
+ "hair",
720
+ "hamburg",
721
+ "hangout",
722
+ "haus",
723
+ "hbo",
724
+ "hdfc",
725
+ "hdfcbank",
726
+ "health",
727
+ "healthcare",
728
+ "help",
729
+ "helsinki",
730
+ "here",
731
+ "hermes",
732
+ "hiphop",
733
+ "hisamitsu",
734
+ "hitachi",
735
+ "hiv",
736
+ "hk",
737
+ "hkt",
738
+ "hm",
739
+ "hn",
740
+ "hockey",
741
+ "holdings",
742
+ "holiday",
743
+ "homedepot",
744
+ "homegoods",
745
+ "homes",
746
+ "homesense",
747
+ "honda",
748
+ "horse",
749
+ "hospital",
750
+ "host",
751
+ "hosting",
752
+ "hot",
753
+ "hotels",
754
+ "hotmail",
755
+ "house",
756
+ "how",
757
+ "hr",
758
+ "hsbc",
759
+ "ht",
760
+ "hu",
761
+ "hughes",
762
+ "hyatt",
763
+ "hyundai",
764
+ "ibm",
765
+ "icbc",
766
+ "ice",
767
+ "icu",
768
+ "id",
769
+ "ie",
770
+ "ieee",
771
+ "ifm",
772
+ "ikano",
773
+ "il",
774
+ "im",
775
+ "imamat",
776
+ "imdb",
777
+ "immo",
778
+ "immobilien",
779
+ "in",
780
+ "inc",
781
+ "industries",
782
+ "infiniti",
783
+ "info",
784
+ "ing",
785
+ "ink",
786
+ "institute",
787
+ "insurance",
788
+ "insure",
789
+ "int",
790
+ "international",
791
+ "intuit",
792
+ "investments",
793
+ "io",
794
+ "ipiranga",
795
+ "iq",
796
+ "ir",
797
+ "irish",
798
+ "is",
799
+ "ismaili",
800
+ "ist",
801
+ "istanbul",
802
+ "it",
803
+ "itau",
804
+ "itv",
805
+ "jaguar",
806
+ "java",
807
+ "jcb",
808
+ "je",
809
+ "jeep",
810
+ "jetzt",
811
+ "jewelry",
812
+ "jio",
813
+ "jll",
814
+ "jm",
815
+ "jmp",
816
+ "jnj",
817
+ "jo",
818
+ "jobs",
819
+ "joburg",
820
+ "jot",
821
+ "joy",
822
+ "jp",
823
+ "jpmorgan",
824
+ "jprs",
825
+ "juegos",
826
+ "juniper",
827
+ "kaufen",
828
+ "kddi",
829
+ "ke",
830
+ "kerryhotels",
831
+ "kerryproperties",
832
+ "kfh",
833
+ "kg",
834
+ "kh",
835
+ "ki",
836
+ "kia",
837
+ "kids",
838
+ "kim",
839
+ "kindle",
840
+ "kitchen",
841
+ "kiwi",
842
+ "km",
843
+ "kn",
844
+ "koeln",
845
+ "komatsu",
846
+ "kosher",
847
+ "kp",
848
+ "kpmg",
849
+ "kpn",
850
+ "kr",
851
+ "krd",
852
+ "kred",
853
+ "kuokgroup",
854
+ "kw",
855
+ "ky",
856
+ "kyoto",
857
+ "kz",
858
+ "la",
859
+ "lacaixa",
860
+ "lamborghini",
861
+ "lamer",
862
+ "land",
863
+ "landrover",
864
+ "lanxess",
865
+ "lasalle",
866
+ "lat",
867
+ "latino",
868
+ "latrobe",
869
+ "law",
870
+ "lawyer",
871
+ "lb",
872
+ "lc",
873
+ "lds",
874
+ "lease",
875
+ "leclerc",
876
+ "lefrak",
877
+ "legal",
878
+ "lego",
879
+ "lexus",
880
+ "lgbt",
881
+ "li",
882
+ "lidl",
883
+ "life",
884
+ "lifeinsurance",
885
+ "lifestyle",
886
+ "lighting",
887
+ "like",
888
+ "lilly",
889
+ "limited",
890
+ "limo",
891
+ "lincoln",
892
+ "link",
893
+ "live",
894
+ "living",
895
+ "lk",
896
+ "llc",
897
+ "llp",
898
+ "loan",
899
+ "loans",
900
+ "locker",
901
+ "locus",
902
+ "lol",
903
+ "london",
904
+ "lotte",
905
+ "lotto",
906
+ "love",
907
+ "lpl",
908
+ "lplfinancial",
909
+ "lr",
910
+ "ls",
911
+ "lt",
912
+ "ltd",
913
+ "ltda",
914
+ "lu",
915
+ "lundbeck",
916
+ "luxe",
917
+ "luxury",
918
+ "lv",
919
+ "ly",
920
+ "ma",
921
+ "madrid",
922
+ "maif",
923
+ "maison",
924
+ "makeup",
925
+ "man",
926
+ "management",
927
+ "mango",
928
+ "map",
929
+ "market",
930
+ "marketing",
931
+ "markets",
932
+ "marriott",
933
+ "marshalls",
934
+ "mattel",
935
+ "mba",
936
+ "mc",
937
+ "mckinsey",
938
+ "md",
939
+ "me",
940
+ "med",
941
+ "media",
942
+ "meet",
943
+ "melbourne",
944
+ "meme",
945
+ "memorial",
946
+ "men",
947
+ "menu",
948
+ "merckmsd",
949
+ "mg",
950
+ "mh",
951
+ "miami",
952
+ "microsoft",
953
+ "mil",
954
+ "mini",
955
+ "mint",
956
+ "mit",
957
+ "mitsubishi",
958
+ "mk",
959
+ "ml",
960
+ "mlb",
961
+ "mls",
962
+ "mm",
963
+ "mma",
964
+ "mn",
965
+ "mo",
966
+ "mobi",
967
+ "mobile",
968
+ "moda",
969
+ "moe",
970
+ "moi",
971
+ "mom",
972
+ "monash",
973
+ "money",
974
+ "monster",
975
+ "mormon",
976
+ "mortgage",
977
+ "moscow",
978
+ "moto",
979
+ "motorcycles",
980
+ "mov",
981
+ "movie",
982
+ "mp",
983
+ "mq",
984
+ "mr",
985
+ "ms",
986
+ "msd",
987
+ "mt",
988
+ "mtn",
989
+ "mtr",
990
+ "mu",
991
+ "museum",
992
+ "music",
993
+ "mv",
994
+ "mw",
995
+ "mx",
996
+ "my",
997
+ "mz",
998
+ "na",
999
+ "nab",
1000
+ "nagoya",
1001
+ "name",
1002
+ "navy",
1003
+ "nba",
1004
+ "nc",
1005
+ "ne",
1006
+ "nec",
1007
+ "net",
1008
+ "netbank",
1009
+ "netflix",
1010
+ "network",
1011
+ "neustar",
1012
+ "new",
1013
+ "news",
1014
+ "next",
1015
+ "nextdirect",
1016
+ "nexus",
1017
+ "nf",
1018
+ "nfl",
1019
+ "ng",
1020
+ "ngo",
1021
+ "nhk",
1022
+ "ni",
1023
+ "nico",
1024
+ "nike",
1025
+ "nikon",
1026
+ "ninja",
1027
+ "nissan",
1028
+ "nissay",
1029
+ "nl",
1030
+ "no",
1031
+ "nokia",
1032
+ "norton",
1033
+ "now",
1034
+ "nowruz",
1035
+ "nowtv",
1036
+ "np",
1037
+ "nr",
1038
+ "nra",
1039
+ "nrw",
1040
+ "ntt",
1041
+ "nu",
1042
+ "nyc",
1043
+ "nz",
1044
+ "obi",
1045
+ "observer",
1046
+ "office",
1047
+ "okinawa",
1048
+ "olayan",
1049
+ "olayangroup",
1050
+ "ollo",
1051
+ "om",
1052
+ "omega",
1053
+ "one",
1054
+ "ong",
1055
+ "onl",
1056
+ "online",
1057
+ "ooo",
1058
+ "open",
1059
+ "oracle",
1060
+ "orange",
1061
+ "org",
1062
+ "organic",
1063
+ "origins",
1064
+ "osaka",
1065
+ "otsuka",
1066
+ "ott",
1067
+ "ovh",
1068
+ "pa",
1069
+ "page",
1070
+ "panasonic",
1071
+ "paris",
1072
+ "pars",
1073
+ "partners",
1074
+ "parts",
1075
+ "party",
1076
+ "pay",
1077
+ "pccw",
1078
+ "pe",
1079
+ "pet",
1080
+ "pf",
1081
+ "pfizer",
1082
+ "pg",
1083
+ "ph",
1084
+ "pharmacy",
1085
+ "phd",
1086
+ "philips",
1087
+ "phone",
1088
+ "photo",
1089
+ "photography",
1090
+ "photos",
1091
+ "physio",
1092
+ "pics",
1093
+ "pictet",
1094
+ "pictures",
1095
+ "pid",
1096
+ "pin",
1097
+ "ping",
1098
+ "pink",
1099
+ "pioneer",
1100
+ "pizza",
1101
+ "pk",
1102
+ "pl",
1103
+ "place",
1104
+ "play",
1105
+ "playstation",
1106
+ "plumbing",
1107
+ "plus",
1108
+ "pm",
1109
+ "pn",
1110
+ "pnc",
1111
+ "pohl",
1112
+ "poker",
1113
+ "politie",
1114
+ "porn",
1115
+ "post",
1116
+ "pr",
1117
+ "praxi",
1118
+ "press",
1119
+ "prime",
1120
+ "pro",
1121
+ "prod",
1122
+ "productions",
1123
+ "prof",
1124
+ "progressive",
1125
+ "promo",
1126
+ "properties",
1127
+ "property",
1128
+ "protection",
1129
+ "pru",
1130
+ "prudential",
1131
+ "ps",
1132
+ "pt",
1133
+ "pub",
1134
+ "pw",
1135
+ "pwc",
1136
+ "py",
1137
+ "qa",
1138
+ "qpon",
1139
+ "quebec",
1140
+ "quest",
1141
+ "racing",
1142
+ "radio",
1143
+ "re",
1144
+ "read",
1145
+ "realestate",
1146
+ "realtor",
1147
+ "realty",
1148
+ "recipes",
1149
+ "red",
1150
+ "redumbrella",
1151
+ "rehab",
1152
+ "reise",
1153
+ "reisen",
1154
+ "reit",
1155
+ "reliance",
1156
+ "ren",
1157
+ "rent",
1158
+ "rentals",
1159
+ "repair",
1160
+ "report",
1161
+ "republican",
1162
+ "rest",
1163
+ "restaurant",
1164
+ "review",
1165
+ "reviews",
1166
+ "rexroth",
1167
+ "rich",
1168
+ "richardli",
1169
+ "ricoh",
1170
+ "ril",
1171
+ "rio",
1172
+ "rip",
1173
+ "ro",
1174
+ "rocks",
1175
+ "rodeo",
1176
+ "rogers",
1177
+ "room",
1178
+ "rs",
1179
+ "rsvp",
1180
+ "ru",
1181
+ "rugby",
1182
+ "ruhr",
1183
+ "run",
1184
+ "rw",
1185
+ "rwe",
1186
+ "ryukyu",
1187
+ "sa",
1188
+ "saarland",
1189
+ "safe",
1190
+ "safety",
1191
+ "sakura",
1192
+ "sale",
1193
+ "salon",
1194
+ "samsclub",
1195
+ "samsung",
1196
+ "sandvik",
1197
+ "sandvikcoromant",
1198
+ "sanofi",
1199
+ "sap",
1200
+ "sarl",
1201
+ "sas",
1202
+ "save",
1203
+ "saxo",
1204
+ "sb",
1205
+ "sbi",
1206
+ "sbs",
1207
+ "sc",
1208
+ "scb",
1209
+ "schaeffler",
1210
+ "schmidt",
1211
+ "scholarships",
1212
+ "school",
1213
+ "schule",
1214
+ "schwarz",
1215
+ "science",
1216
+ "scot",
1217
+ "sd",
1218
+ "se",
1219
+ "search",
1220
+ "seat",
1221
+ "secure",
1222
+ "security",
1223
+ "seek",
1224
+ "select",
1225
+ "sener",
1226
+ "services",
1227
+ "seven",
1228
+ "sew",
1229
+ "sex",
1230
+ "sexy",
1231
+ "sfr",
1232
+ "sg",
1233
+ "sh",
1234
+ "shangrila",
1235
+ "sharp",
1236
+ "shell",
1237
+ "shia",
1238
+ "shiksha",
1239
+ "shoes",
1240
+ "shop",
1241
+ "shopping",
1242
+ "shouji",
1243
+ "show",
1244
+ "si",
1245
+ "silk",
1246
+ "sina",
1247
+ "singles",
1248
+ "site",
1249
+ "sj",
1250
+ "sk",
1251
+ "ski",
1252
+ "skin",
1253
+ "sky",
1254
+ "skype",
1255
+ "sl",
1256
+ "sling",
1257
+ "sm",
1258
+ "smart",
1259
+ "smile",
1260
+ "sn",
1261
+ "sncf",
1262
+ "so",
1263
+ "soccer",
1264
+ "social",
1265
+ "softbank",
1266
+ "software",
1267
+ "sohu",
1268
+ "solar",
1269
+ "solutions",
1270
+ "song",
1271
+ "sony",
1272
+ "soy",
1273
+ "spa",
1274
+ "space",
1275
+ "sport",
1276
+ "spot",
1277
+ "sr",
1278
+ "srl",
1279
+ "ss",
1280
+ "st",
1281
+ "stada",
1282
+ "staples",
1283
+ "star",
1284
+ "statebank",
1285
+ "statefarm",
1286
+ "stc",
1287
+ "stcgroup",
1288
+ "stockholm",
1289
+ "storage",
1290
+ "store",
1291
+ "stream",
1292
+ "studio",
1293
+ "study",
1294
+ "style",
1295
+ "su",
1296
+ "sucks",
1297
+ "supplies",
1298
+ "supply",
1299
+ "support",
1300
+ "surf",
1301
+ "surgery",
1302
+ "suzuki",
1303
+ "sv",
1304
+ "swatch",
1305
+ "swiss",
1306
+ "sx",
1307
+ "sy",
1308
+ "sydney",
1309
+ "systems",
1310
+ "sz",
1311
+ "tab",
1312
+ "taipei",
1313
+ "talk",
1314
+ "taobao",
1315
+ "target",
1316
+ "tatamotors",
1317
+ "tatar",
1318
+ "tattoo",
1319
+ "tax",
1320
+ "taxi",
1321
+ "tc",
1322
+ "tci",
1323
+ "td",
1324
+ "tdk",
1325
+ "team",
1326
+ "tech",
1327
+ "technology",
1328
+ "tel",
1329
+ "temasek",
1330
+ "tennis",
1331
+ "teva",
1332
+ "tf",
1333
+ "tg",
1334
+ "th",
1335
+ "thd",
1336
+ "theater",
1337
+ "theatre",
1338
+ "tiaa",
1339
+ "tickets",
1340
+ "tienda",
1341
+ "tips",
1342
+ "tires",
1343
+ "tirol",
1344
+ "tj",
1345
+ "tjmaxx",
1346
+ "tjx",
1347
+ "tk",
1348
+ "tkmaxx",
1349
+ "tl",
1350
+ "tm",
1351
+ "tmall",
1352
+ "tn",
1353
+ "to",
1354
+ "today",
1355
+ "tokyo",
1356
+ "tools",
1357
+ "top",
1358
+ "toray",
1359
+ "toshiba",
1360
+ "total",
1361
+ "tours",
1362
+ "town",
1363
+ "toyota",
1364
+ "toys",
1365
+ "tr",
1366
+ "trade",
1367
+ "trading",
1368
+ "training",
1369
+ "travel",
1370
+ "travelers",
1371
+ "travelersinsurance",
1372
+ "trust",
1373
+ "trv",
1374
+ "tt",
1375
+ "tube",
1376
+ "tui",
1377
+ "tunes",
1378
+ "tushu",
1379
+ "tv",
1380
+ "tvs",
1381
+ "tw",
1382
+ "tz",
1383
+ "ua",
1384
+ "ubank",
1385
+ "ubs",
1386
+ "ug",
1387
+ "uk",
1388
+ "unicom",
1389
+ "university",
1390
+ "uno",
1391
+ "uol",
1392
+ "ups",
1393
+ "us",
1394
+ "uy",
1395
+ "uz",
1396
+ "va",
1397
+ "vacations",
1398
+ "vana",
1399
+ "vanguard",
1400
+ "vc",
1401
+ "ve",
1402
+ "vegas",
1403
+ "ventures",
1404
+ "verisign",
1405
+ "vermögensberater",
1406
+ "vermögensberatung",
1407
+ "versicherung",
1408
+ "vet",
1409
+ "vg",
1410
+ "vi",
1411
+ "viajes",
1412
+ "video",
1413
+ "vig",
1414
+ "viking",
1415
+ "villas",
1416
+ "vin",
1417
+ "vip",
1418
+ "virgin",
1419
+ "visa",
1420
+ "vision",
1421
+ "viva",
1422
+ "vivo",
1423
+ "vlaanderen",
1424
+ "vn",
1425
+ "vodka",
1426
+ "volvo",
1427
+ "vote",
1428
+ "voting",
1429
+ "voto",
1430
+ "voyage",
1431
+ "vu",
1432
+ "wales",
1433
+ "walmart",
1434
+ "walter",
1435
+ "wang",
1436
+ "wanggou",
1437
+ "watch",
1438
+ "watches",
1439
+ "weather",
1440
+ "weatherchannel",
1441
+ "webcam",
1442
+ "weber",
1443
+ "website",
1444
+ "wed",
1445
+ "wedding",
1446
+ "weibo",
1447
+ "weir",
1448
+ "wf",
1449
+ "whoswho",
1450
+ "wien",
1451
+ "wiki",
1452
+ "williamhill",
1453
+ "win",
1454
+ "windows",
1455
+ "wine",
1456
+ "winners",
1457
+ "wme",
1458
+ "wolterskluwer",
1459
+ "woodside",
1460
+ "work",
1461
+ "works",
1462
+ "world",
1463
+ "wow",
1464
+ "ws",
1465
+ "wtc",
1466
+ "wtf",
1467
+ "xbox",
1468
+ "xerox",
1469
+ "xihuan",
1470
+ "xin",
1471
+ "xxx",
1472
+ "xyz",
1473
+ "yachts",
1474
+ "yahoo",
1475
+ "yamaxun",
1476
+ "yandex",
1477
+ "ye",
1478
+ "yodobashi",
1479
+ "yoga",
1480
+ "yokohama",
1481
+ "you",
1482
+ "youtube",
1483
+ "yt",
1484
+ "yun",
1485
+ "za",
1486
+ "zappos",
1487
+ "zara",
1488
+ "zero",
1489
+ "zip",
1490
+ "zm",
1491
+ "zone",
1492
+ "zuerich",
1493
+ "zw",
1494
+ "ελ",
1495
+ "ευ",
1496
+ "бг",
1497
+ "бел",
1498
+ "дети",
1499
+ "ею",
1500
+ "католик",
1501
+ "ком",
1502
+ "мкд",
1503
+ "мон",
1504
+ "москва",
1505
+ "онлайн",
1506
+ "орг",
1507
+ "рус",
1508
+ "рф",
1509
+ "сайт",
1510
+ "срб",
1511
+ "укр",
1512
+ "қаз",
1513
+ "հայ",
1514
+ "ישראל",
1515
+ "קום",
1516
+ "ابوظبي",
1517
+ "ارامكو",
1518
+ "الاردن",
1519
+ "البحرين",
1520
+ "الجزائر",
1521
+ "السعودية",
1522
+ "العليان",
1523
+ "المغرب",
1524
+ "امارات",
1525
+ "ایران",
1526
+ "بارت",
1527
+ "بازار",
1528
+ "بيتك",
1529
+ "بھارت",
1530
+ "تونس",
1531
+ "سودان",
1532
+ "سورية",
1533
+ "شبكة",
1534
+ "عراق",
1535
+ "عرب",
1536
+ "عمان",
1537
+ "فلسطين",
1538
+ "قطر",
1539
+ "كاثوليك",
1540
+ "كوم",
1541
+ "مصر",
1542
+ "مليسيا",
1543
+ "موريتانيا",
1544
+ "موقع",
1545
+ "همراه",
1546
+ "پاکستان",
1547
+ "ڀارت",
1548
+ "कॉम",
1549
+ "नेट",
1550
+ "भारत",
1551
+ "भारतम्",
1552
+ "भारोत",
1553
+ "संगठन",
1554
+ "বাংলা",
1555
+ "ভারত",
1556
+ "ভাৰত",
1557
+ "ਭਾਰਤ",
1558
+ "ભારત",
1559
+ "ଭାରତ",
1560
+ "இந்தியா",
1561
+ "இலங்கை",
1562
+ "சிங்கப்பூர்",
1563
+ "భారత్",
1564
+ "ಭಾರತ",
1565
+ "ഭാരതം",
1566
+ "ලංකා",
1567
+ "คอม",
1568
+ "ไทย",
1569
+ "ລາວ",
1570
+ "გე",
1571
+ "みんな",
1572
+ "アマゾン",
1573
+ "クラウド",
1574
+ "グーグル",
1575
+ "コム",
1576
+ "ストア",
1577
+ "セール",
1578
+ "ファッション",
1579
+ "ポイント",
1580
+ "世界",
1581
+ "中信",
1582
+ "中国",
1583
+ "中國",
1584
+ "中文网",
1585
+ "亚马逊",
1586
+ "企业",
1587
+ "佛山",
1588
+ "信息",
1589
+ "健康",
1590
+ "八卦",
1591
+ "公司",
1592
+ "公益",
1593
+ "台湾",
1594
+ "台灣",
1595
+ "商城",
1596
+ "商店",
1597
+ "商标",
1598
+ "嘉里",
1599
+ "嘉里大酒店",
1600
+ "在线",
1601
+ "大拿",
1602
+ "天主教",
1603
+ "娱乐",
1604
+ "家電",
1605
+ "广东",
1606
+ "微博",
1607
+ "慈善",
1608
+ "我爱你",
1609
+ "手机",
1610
+ "招聘",
1611
+ "政务",
1612
+ "政府",
1613
+ "新加坡",
1614
+ "新闻",
1615
+ "时尚",
1616
+ "書籍",
1617
+ "机构",
1618
+ "淡马锡",
1619
+ "游戏",
1620
+ "澳門",
1621
+ "点看",
1622
+ "移动",
1623
+ "组织机构",
1624
+ "网址",
1625
+ "网店",
1626
+ "网站",
1627
+ "网络",
1628
+ "联通",
1629
+ "谷歌",
1630
+ "购物",
1631
+ "通販",
1632
+ "集团",
1633
+ "電訊盈科",
1634
+ "飞利浦",
1635
+ "食品",
1636
+ "餐厅",
1637
+ "香格里拉",
1638
+ "香港",
1639
+ "닷넷",
1640
+ "닷컴",
1641
+ "삼성",
1642
+ "한국"
1643
+ ];
1644
+ //#endregion
1645
+ //#region src/tld.ts
1646
+ let known;
1647
+ /** Whether `tld` is in the IANA set, as a U-label or A-label, ignoring case. */
1648
+ function isKnownTld(tld) {
1649
+ known ??= buildSet();
1650
+ return known.has(tld.toLowerCase());
1651
+ }
1652
+ function buildSet() {
1653
+ const set = new Set(tlds_default);
1654
+ for (const tld of tlds_default) if (!/^[a-z\d]+$/.test(tld)) {
1655
+ const aLabel = toALabel(tld);
1656
+ if (aLabel !== void 0) set.add(aLabel);
1657
+ }
1658
+ return set;
1659
+ }
1660
+ //#endregion
1661
+ //#region src/parse.ts
1662
+ const messages = {
1663
+ "syntax.address.empty": "The address is empty",
1664
+ "syntax.address.no_at": "The address has no @",
1665
+ "syntax.address.too_long": "The address is longer than 254 characters",
1666
+ "syntax.local.empty": "Nothing comes before the @",
1667
+ "syntax.local.too_long": "The local part is longer than 64 characters",
1668
+ "syntax.local.invalid_char": "The local part has a character it can’t hold",
1669
+ "syntax.local.consecutive_dots": "The local part has two dots in a row",
1670
+ "syntax.local.unquoted_space": "The local part has a space outside quotes",
1671
+ "syntax.domain.empty": "Nothing comes after the @",
1672
+ "syntax.domain.no_dot": "The domain has no dot",
1673
+ "syntax.domain.label_invalid": "A domain label is empty, too long, or starts or ends with a hyphen",
1674
+ "syntax.domain.literal_invalid": "The domain literal isn’t accepted",
1675
+ "syntax.domain.too_long": "The domain is longer than 253 characters",
1676
+ "syntax.domain.invalid_char": "The domain has a character it can’t hold",
1677
+ "syntax.comment.not_allowed": "Comments aren’t allowed here",
1678
+ "syntax.comment.unterminated": "A comment is missing its closing parenthesis",
1679
+ "syntax.tld.unknown": "The TLD isn’t in the IANA set"
1680
+ };
1681
+ function fail(reason, index) {
1682
+ const message = messages[reason];
1683
+ return index === void 0 ? {
1684
+ ok: false,
1685
+ reason,
1686
+ message
1687
+ } : {
1688
+ ok: false,
1689
+ reason,
1690
+ message,
1691
+ index
1692
+ };
1693
+ }
1694
+ const NORMAL = 0;
1695
+ const QUOTED = 1;
1696
+ const COMMENT = 2;
1697
+ const LITERAL = 3;
1698
+ /**
1699
+ * Finds the `@` that splits `email`: the last one outside a quoted string,
1700
+ * comment, or domain literal, or -1 when there's none.
1701
+ *
1702
+ * @remarks
1703
+ * A quoted string opens only at the start of a word, and a literal only at
1704
+ * the start of the domain; both, and comments, take backslash escapes. One
1705
+ * left open runs to the end, taking any `@` with it.
1706
+ */
1707
+ function findAt(email, rules) {
1708
+ let state = NORMAL;
1709
+ let wordStart = true;
1710
+ let domainStart = false;
1711
+ let depth = 0;
1712
+ let at = -1;
1713
+ for (let i = 0; i < email.length; i++) {
1714
+ const code = email.charCodeAt(i);
1715
+ if (state === NORMAL) {
1716
+ if (code === 64) {
1717
+ at = i;
1718
+ wordStart = true;
1719
+ domainStart = true;
1720
+ } else if (code === 40 && rules.comments) {
1721
+ state = COMMENT;
1722
+ depth = 1;
1723
+ } else if (!isWhitespace(code)) {
1724
+ if (code === 34 && rules.quotes && wordStart) state = QUOTED;
1725
+ else if (code === 91 && rules.literals && domainStart) state = LITERAL;
1726
+ wordStart = code === 46;
1727
+ domainStart = false;
1728
+ }
1729
+ } else if (code === 92) i++;
1730
+ else if (state === COMMENT) {
1731
+ if (code === 40) depth++;
1732
+ else if (code === 41 && --depth === 0) state = NORMAL;
1733
+ } else if (code === (state === QUOTED ? 34 : 93)) state = NORMAL;
1734
+ }
1735
+ return at;
1736
+ }
1737
+ /**
1738
+ * Parses `email` under `rules`.
1739
+ *
1740
+ * @throws TypeError when `email` isn't a string.
1741
+ */
1742
+ function parse(email, rules) {
1743
+ if (typeof email !== "string") throw new TypeError(`Expected a string, got ${typeof email}`);
1744
+ if (email === "") return fail("syntax.address.empty");
1745
+ const at = findAt(email, rules);
1746
+ if (at < 0) return fail("syntax.address.no_at");
1747
+ const comments = [];
1748
+ const local = scanLocal(email, at, rules, comments);
1749
+ if (typeof local !== "string") return local;
1750
+ const localSize = rules.unicode ? utf8Length(local) : local.length;
1751
+ if (localSize > rules.localCap) return fail("syntax.local.too_long");
1752
+ const domain = scanDomain(email, at, rules, comments);
1753
+ if ("reason" in domain) return domain;
1754
+ if (domain.size > rules.domainCap) return fail("syntax.domain.too_long");
1755
+ if (localSize + 1 + domain.size > 254) return fail("syntax.address.too_long");
1756
+ const { tld } = domain;
1757
+ if (tld === void 0 && !domain.literal && !rules.allowNoTld) return fail("syntax.domain.no_dot");
1758
+ if (tld !== void 0 && rules.checkTld && !isKnownTld(tld)) return fail("syntax.tld.unknown");
1759
+ return {
1760
+ ok: true,
1761
+ value: tld === void 0 ? {
1762
+ local,
1763
+ domain: domain.domain,
1764
+ comments
1765
+ } : {
1766
+ local,
1767
+ domain: domain.domain,
1768
+ tld,
1769
+ comments
1770
+ }
1771
+ };
1772
+ }
1773
+ /**
1774
+ * Scans the local part, `email` up to `at`, and returns it without comments
1775
+ * or folding whitespace.
1776
+ */
1777
+ function scanLocal(email, at, rules, comments) {
1778
+ let local = "";
1779
+ let words = 0;
1780
+ let afterWord = false;
1781
+ let closed = false;
1782
+ let dot = -1;
1783
+ let space = -1;
1784
+ let edge = -1;
1785
+ let trailing = comments.length;
1786
+ let i = 0;
1787
+ while (i < at) {
1788
+ const code = email.charCodeAt(i);
1789
+ const quote = code === 34 && rules.quotes && (rules.obs || i === 0);
1790
+ const atom = inClass(code, rules.local);
1791
+ if (quote || atom || rules.unicode && nonAsciiAt(email, i, at) > 0) {
1792
+ if (edge >= 0) return fail("syntax.comment.not_allowed", edge);
1793
+ if (afterWord) return space >= 0 ? fail("syntax.local.unquoted_space", space) : fail("syntax.local.invalid_char", i);
1794
+ let next;
1795
+ if (quote) {
1796
+ next = skipQuoted(email, i, at, rules.obs, rules.unicode);
1797
+ if (next < 0) return fail("syntax.local.invalid_char", -1 - next);
1798
+ closed = !rules.obs;
1799
+ } else {
1800
+ next = atom ? i + 1 : i;
1801
+ while (next < at && inClass(email.charCodeAt(next), rules.local)) next++;
1802
+ if (rules.unicode) next = skipUnicode(email, next, at, rules.local);
1803
+ }
1804
+ local += unfold(email.slice(i, next));
1805
+ words++;
1806
+ afterWord = true;
1807
+ dot = -1;
1808
+ space = -1;
1809
+ trailing = comments.length;
1810
+ i = next;
1811
+ } else if (code === 46) {
1812
+ if (edge >= 0) return fail("syntax.comment.not_allowed", edge);
1813
+ if (!afterWord || closed) return dot >= 0 ? fail("syntax.local.consecutive_dots", i) : fail("syntax.local.invalid_char", i);
1814
+ local += ".";
1815
+ afterWord = false;
1816
+ dot = i;
1817
+ space = -1;
1818
+ i++;
1819
+ } else if (code === 40 && rules.comments) {
1820
+ if (!rules.obs && words > 0) {
1821
+ if (!afterWord) return fail("syntax.comment.not_allowed", i);
1822
+ edge = edge < 0 ? i : edge;
1823
+ }
1824
+ const next = skipComment(email, i, at, rules.unicode);
1825
+ if (next < 0) return fail("syntax.local.invalid_char", -1 - next);
1826
+ comments.push({
1827
+ text: email.slice(i + 1, next - 1),
1828
+ position: words === 0 ? "before-local" : "inside-local"
1829
+ });
1830
+ i = next;
1831
+ } else if (code === 40) return fail("syntax.comment.not_allowed", i);
1832
+ else if (rules.obs && isWhitespace(code)) {
1833
+ const next = skipSpace(email, i, at);
1834
+ if (next < 0) return fail("syntax.local.invalid_char", i);
1835
+ space = space < 0 ? i : space;
1836
+ i = next;
1837
+ } else return code === 32 ? fail("syntax.local.unquoted_space", i) : fail("syntax.local.invalid_char", i);
1838
+ }
1839
+ if (words === 0) return fail("syntax.local.empty");
1840
+ if (dot >= 0) return fail("syntax.local.invalid_char", dot);
1841
+ settle(comments, trailing, "after-local");
1842
+ return local;
1843
+ }
1844
+ /**
1845
+ * Scans the domain, `email` after `at`, and returns it without comments or
1846
+ * folding whitespace.
1847
+ */
1848
+ function scanDomain(email, at, rules, comments) {
1849
+ const end = email.length;
1850
+ let domain = "";
1851
+ let labels = 0;
1852
+ let afterLabel = false;
1853
+ let literal = false;
1854
+ let dot = -1;
1855
+ let space = -1;
1856
+ let edge = -1;
1857
+ let trailing = comments.length;
1858
+ let tld = "";
1859
+ let growth = 0;
1860
+ let i = at + 1;
1861
+ while (i < end) {
1862
+ const code = email.charCodeAt(i);
1863
+ const opensLiteral = code === 91 && rules.literals && labels === 0;
1864
+ const ldh = inClass(code, rules.domain);
1865
+ if (opensLiteral || ldh || rules.idn && nonAsciiAt(email, i, end) > 0) {
1866
+ if (edge >= 0) return fail("syntax.comment.not_allowed", edge);
1867
+ if (afterLabel) return fail("syntax.domain.invalid_char", space >= 0 ? space : i);
1868
+ let next;
1869
+ if (opensLiteral) {
1870
+ next = rules.obs ? skipLiteral(email, i, end) : skipAddressLiteral(email, i, end);
1871
+ if (next < 0) return fail("syntax.domain.literal_invalid", i);
1872
+ literal = true;
1873
+ } else {
1874
+ next = ldh ? i + 1 : i;
1875
+ while (next < end && inClass(email.charCodeAt(next), rules.domain)) next++;
1876
+ const ascii = next;
1877
+ if (rules.idn) next = skipUnicode(email, next, end, rules.domain);
1878
+ let size = next - i;
1879
+ if (next > ascii) {
1880
+ const aLabel = toALabel(email.slice(i, next));
1881
+ if (aLabel === void 0) return fail("syntax.domain.label_invalid", i);
1882
+ size = aLabel.length;
1883
+ growth += size - (next - i);
1884
+ }
1885
+ const bad = checkLabel(email, i, next, size);
1886
+ if (bad >= 0) return fail("syntax.domain.label_invalid", bad);
1887
+ }
1888
+ tld = unfold(email.slice(i, next));
1889
+ domain += tld;
1890
+ labels++;
1891
+ afterLabel = true;
1892
+ dot = -1;
1893
+ space = -1;
1894
+ trailing = comments.length;
1895
+ i = next;
1896
+ } else if (code === 46) {
1897
+ if (edge >= 0) return fail("syntax.comment.not_allowed", edge);
1898
+ if (!afterLabel || literal) return literal ? fail("syntax.domain.invalid_char", i) : fail("syntax.domain.label_invalid", i);
1899
+ domain += ".";
1900
+ afterLabel = false;
1901
+ dot = i;
1902
+ space = -1;
1903
+ i++;
1904
+ } else if (code === 40 && rules.comments) {
1905
+ if (!rules.obs && labels > 0) {
1906
+ if (!afterLabel) return fail("syntax.comment.not_allowed", i);
1907
+ edge = edge < 0 ? i : edge;
1908
+ }
1909
+ const next = skipComment(email, i, end, rules.unicode);
1910
+ if (next < 0) return -1 - next === i ? fail("syntax.comment.unterminated", i) : fail("syntax.domain.invalid_char", -1 - next);
1911
+ comments.push({
1912
+ text: email.slice(i + 1, next - 1),
1913
+ position: labels === 0 ? "before-domain" : "inside-domain"
1914
+ });
1915
+ i = next;
1916
+ } else if (code === 40) return fail("syntax.comment.not_allowed", i);
1917
+ else if (rules.obs && isWhitespace(code)) {
1918
+ const next = skipSpace(email, i, end);
1919
+ if (next < 0) return fail("syntax.domain.invalid_char", i);
1920
+ space = space < 0 ? i : space;
1921
+ i = next;
1922
+ } else return fail("syntax.domain.invalid_char", i);
1923
+ }
1924
+ if (labels === 0) return fail("syntax.domain.empty");
1925
+ if (dot >= 0) return fail("syntax.domain.label_invalid", dot);
1926
+ settle(comments, trailing, "after-domain");
1927
+ const size = domain.length + growth;
1928
+ return literal || labels === 1 ? {
1929
+ domain,
1930
+ literal,
1931
+ size
1932
+ } : {
1933
+ domain,
1934
+ tld,
1935
+ literal,
1936
+ size
1937
+ };
1938
+ }
1939
+ /**
1940
+ * Carries an atom or label on from `i`, where its ASCII run stopped, through
1941
+ * any non-ASCII characters and the class-`flag` characters after them.
1942
+ */
1943
+ function skipUnicode(email, i, end, flag) {
1944
+ let width = i < end ? nonAsciiAt(email, i, end) : 0;
1945
+ while (width > 0) {
1946
+ i += width;
1947
+ while (i < end && inClass(email.charCodeAt(i), flag)) i++;
1948
+ width = i < end ? nonAsciiAt(email, i, end) : 0;
1949
+ }
1950
+ return i;
1951
+ }
1952
+ /**
1953
+ * Checks the hostname label from `start` to `end`, `size` characters long as
1954
+ * an A-label: returns the index of a leading or trailing hyphen, the start of
1955
+ * a label over 63 characters, or -1.
1956
+ */
1957
+ function checkLabel(email, start, end, size) {
1958
+ if (email.charCodeAt(start) === 45) return start;
1959
+ if (size > 63) return start;
1960
+ return email.charCodeAt(end - 1) === 45 ? end - 1 : -1;
1961
+ }
1962
+ /** Marks the comments from `from` on, which no word follows, as trailing. */
1963
+ function settle(comments, from, position) {
1964
+ for (let c = from; c < comments.length; c++) {
1965
+ const comment = comments[c];
1966
+ if (comment.position.startsWith("inside")) comment.position = position;
1967
+ }
1968
+ }
1969
+ /** Removes the CRLFs of folding whitespace, keeping the space or tab after. */
1970
+ function unfold(text) {
1971
+ return text.includes("\r") ? text.replaceAll("\r\n", "") : text;
1972
+ }
1973
+ /**
1974
+ * Skips one character of folding whitespace: a space, a tab, or a CRLF with
1975
+ * a space or tab after it. A lone CR or LF breaks it.
1976
+ */
1977
+ function skipSpace(email, i, end) {
1978
+ const code = email.charCodeAt(i);
1979
+ if (code === 32 || code === 9) return i + 1;
1980
+ return code === 13 && i + 2 < end && email.charCodeAt(i + 1) === 10 && (email.charCodeAt(i + 2) === 32 || email.charCodeAt(i + 2) === 9) ? i + 2 : -1 - i;
1981
+ }
1982
+ /** Whether a backslash may escape `code`: any ASCII with `obs`, else VCHAR and space. */
1983
+ function escapable(code, obs) {
1984
+ return obs ? code < 128 : code >= 32 && code <= 126;
1985
+ }
1986
+ /**
1987
+ * Skips the quoted string opening at `open`. `obs` allows folding whitespace
1988
+ * and control characters other than NUL; without it, the string follows
1989
+ * RFC 5321: printable ASCII and spaces only. `unicode` adds non-ASCII.
1990
+ */
1991
+ function skipQuoted(email, open, end, obs, unicode) {
1992
+ let i = open + 1;
1993
+ while (i < end) {
1994
+ const code = email.charCodeAt(i);
1995
+ if (code === 34) return i + 1;
1996
+ if (code === 92) {
1997
+ if (!escapable(email.charCodeAt(i + 1), obs)) return -2 - i;
1998
+ i += 2;
1999
+ } else if (obs && isWhitespace(code)) {
2000
+ i = skipSpace(email, i, end);
2001
+ if (i < 0) return i;
2002
+ } else if (obs ? code === 0 || code > 127 : code < 32 || code > 126) {
2003
+ const width = unicode ? nonAsciiAt(email, i, end) : 0;
2004
+ if (width === 0) return -1 - i;
2005
+ i += width;
2006
+ } else i++;
2007
+ }
2008
+ return -1 - open;
2009
+ }
2010
+ /**
2011
+ * Skips the comment opening at `open`, nested comments included. Its text
2012
+ * may hold any ASCII but NUL, with folding whitespace, and non-ASCII with
2013
+ * `unicode`; returns `-1 - open` when it never closes.
2014
+ */
2015
+ function skipComment(email, open, end, unicode) {
2016
+ let depth = 1;
2017
+ let i = open + 1;
2018
+ while (i < end) {
2019
+ const code = email.charCodeAt(i);
2020
+ if (code === 40 || code === 41) {
2021
+ depth += code === 40 ? 1 : -1;
2022
+ i++;
2023
+ if (depth === 0) return i;
2024
+ } else if (code === 92) {
2025
+ if (i + 1 >= end) break;
2026
+ if (!escapable(email.charCodeAt(i + 1), true)) return -2 - i;
2027
+ i += 2;
2028
+ } else if (isWhitespace(code)) {
2029
+ i = skipSpace(email, i, end);
2030
+ if (i < 0) return i;
2031
+ } else if (code === 0 || code > 127) {
2032
+ const width = unicode ? nonAsciiAt(email, i, end) : 0;
2033
+ if (width === 0) return -1 - i;
2034
+ i += width;
2035
+ } else i++;
2036
+ }
2037
+ return -1 - open;
2038
+ }
2039
+ /**
2040
+ * Skips the RFC 5322 domain literal opening at `open`: any ASCII but NUL,
2041
+ * `[`, and `]`, with escapes and folding whitespace. Returns -1 when it's
2042
+ * malformed or never closes.
2043
+ */
2044
+ function skipLiteral(email, open, end) {
2045
+ let i = open + 1;
2046
+ while (i < end) {
2047
+ const code = email.charCodeAt(i);
2048
+ if (code === 93) return i + 1;
2049
+ if (code === 92) {
2050
+ if (i + 1 >= end || !escapable(email.charCodeAt(i + 1), true)) return -1;
2051
+ i += 2;
2052
+ } else if (isWhitespace(code)) {
2053
+ i = skipSpace(email, i, end);
2054
+ if (i < 0) return -1;
2055
+ } else if (code === 0 || code === 91 || code > 127) return -1;
2056
+ else i++;
2057
+ }
2058
+ return -1;
2059
+ }
2060
+ /**
2061
+ * Skips the RFC 5321 address literal opening at `open`: an IPv4 address or
2062
+ * `IPv6:` and an IPv6 address. Returns -1 for anything else.
2063
+ */
2064
+ function skipAddressLiteral(email, open, end) {
2065
+ const close = email.indexOf("]", open + 1);
2066
+ return close >= 0 && close < end && isAddressLiteral(email.slice(open + 1, close)) ? close + 1 : -1;
2067
+ }
2068
+ //#endregion
2069
+ //#region src/index.ts
2070
+ /**
2071
+ * Is the address well-formed? A quote-aware parser with four presets:
2072
+ * `practical` (the default), `rfc5321`, `rfc5322`, and `html5`.
2073
+ *
2074
+ * @packageDocumentation
2075
+ */
2076
+ /**
2077
+ * Parses `email` into its local part, domain, TLD, and comments, or returns
2078
+ * the first check it fails.
2079
+ *
2080
+ * @remarks
2081
+ * The local part is checked before the domain, each left to right, then the
2082
+ * lengths, then a dotless domain, then the TLD. `index`, where a failure has
2083
+ * one, points at the offending character.
2084
+ *
2085
+ * @example
2086
+ * ```ts
2087
+ * const result = parseAddress('ada.lovelace@example.co.uk');
2088
+ * if (result.ok) {
2089
+ * result.value.tld; // 'uk'
2090
+ * } else {
2091
+ * result.reason; // e.g. 'syntax.local.invalid_char'
2092
+ * }
2093
+ * ```
2094
+ *
2095
+ * @throws TypeError when `email` isn't a string, or `options` are malformed.
2096
+ */
2097
+ function parseAddress(email, options) {
2098
+ return parse(email, resolve(options));
2099
+ }
2100
+ /**
2101
+ * Whether `email` is well-formed: exactly `parseAddress(email, options).ok`.
2102
+ *
2103
+ * @example
2104
+ * ```ts
2105
+ * isValidSyntax('ada@example.com'); // true
2106
+ * isValidSyntax('"ada"@example.com'); // false
2107
+ * isValidSyntax('"ada"@example.com', { preset: 'rfc5321' }); // true
2108
+ * ```
2109
+ *
2110
+ * @throws TypeError when `email` isn't a string, or `options` are malformed.
2111
+ */
2112
+ function isValidSyntax(email, options) {
2113
+ return parse(email, resolve(options)).ok;
2114
+ }
2115
+ /**
2116
+ * Binds `options` once, checking them up front, and returns
2117
+ * {@link parseAddress} and {@link isValidSyntax} with them applied.
2118
+ *
2119
+ * @example
2120
+ * ```ts
2121
+ * const strict = createSyntaxValidator({ preset: 'rfc5321' });
2122
+ * strict.parse('a b@example.com'); // { ok: false, reason: 'syntax.local.unquoted_space', … }
2123
+ * strict.isValid('"a b"@example.com'); // true
2124
+ * ```
2125
+ *
2126
+ * @throws TypeError when `options` are malformed.
2127
+ */
2128
+ function createSyntaxValidator(options) {
2129
+ const rules = resolve(options);
2130
+ return {
2131
+ parse: (email) => parse(email, rules),
2132
+ isValid: (email) => parse(email, rules).ok
2133
+ };
2134
+ }
2135
+ //#endregion
2136
+ export { createSyntaxValidator, isValidSyntax, parseAddress };
2137
+
2138
+ //# sourceMappingURL=index.mjs.map