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