bouncevalidator 0.0.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,2041 @@
1
+ // src/utils/normalize.ts
2
+ function normalizeEmail(email, options = {}) {
3
+ let normalized = email.trim().toLowerCase();
4
+ const atIndex = normalized.indexOf("@");
5
+ if (atIndex === -1) {
6
+ return normalized;
7
+ }
8
+ let username = normalized.slice(0, atIndex);
9
+ const domain = normalized.slice(atIndex + 1);
10
+ if (options.removePlusAddressing) {
11
+ const plusIndex = username.indexOf("+");
12
+ if (plusIndex !== -1) {
13
+ username = username.slice(0, plusIndex);
14
+ }
15
+ }
16
+ if (options.removeDots) {
17
+ const gmailDomains = ["gmail.com", "googlemail.com"];
18
+ if (gmailDomains.includes(domain)) {
19
+ username = username.replace(/\./g, "");
20
+ }
21
+ }
22
+ return `${username}@${domain}`;
23
+ }
24
+ function extractParts(email) {
25
+ const atIndex = email.indexOf("@");
26
+ if (atIndex === -1 || atIndex === 0 || atIndex === email.length - 1) {
27
+ return null;
28
+ }
29
+ if (email.indexOf("@", atIndex + 1) !== -1) {
30
+ return null;
31
+ }
32
+ return {
33
+ username: email.slice(0, atIndex),
34
+ domain: email.slice(atIndex + 1)
35
+ };
36
+ }
37
+ function cleanDomain(domain) {
38
+ let cleaned = domain.trim().toLowerCase();
39
+ if (cleaned.endsWith(".")) {
40
+ cleaned = cleaned.slice(0, -1);
41
+ }
42
+ return cleaned;
43
+ }
44
+
45
+ // src/utils/suggestions.ts
46
+ var domainTypos = {
47
+ // Gmail typos
48
+ "gmial.com": "gmail.com",
49
+ "gmal.com": "gmail.com",
50
+ "gamil.com": "gmail.com",
51
+ "gnail.com": "gmail.com",
52
+ "gmaill.com": "gmail.com",
53
+ "gmali.com": "gmail.com",
54
+ "gmai.com": "gmail.com",
55
+ "gmail.co": "gmail.com",
56
+ "gmail.om": "gmail.com",
57
+ "gmail.cm": "gmail.com",
58
+ "gmail.cim": "gmail.com",
59
+ "gmail.con": "gmail.com",
60
+ "gmail.vom": "gmail.com",
61
+ "gmail.xom": "gmail.com",
62
+ "gmail.comm": "gmail.com",
63
+ "gmail.ocm": "gmail.com",
64
+ "gmaul.com": "gmail.com",
65
+ "gmeil.com": "gmail.com",
66
+ "gmsil.com": "gmail.com",
67
+ "gmqil.com": "gmail.com",
68
+ "gemail.com": "gmail.com",
69
+ "g]mail.com": "gmail.com",
70
+ "fmail.com": "gmail.com",
71
+ // Yahoo typos
72
+ "yaho.com": "yahoo.com",
73
+ "yahooo.com": "yahoo.com",
74
+ "yhoo.com": "yahoo.com",
75
+ "yhaoo.com": "yahoo.com",
76
+ "yaoo.com": "yahoo.com",
77
+ "yahoo.co": "yahoo.com",
78
+ "yahoo.om": "yahoo.com",
79
+ "yahoo.cm": "yahoo.com",
80
+ "yahoo.con": "yahoo.com",
81
+ "yahoo.vom": "yahoo.com",
82
+ "tahoo.com": "yahoo.com",
83
+ "uahoo.com": "yahoo.com",
84
+ "yagoo.com": "yahoo.com",
85
+ // Hotmail typos
86
+ "hotmial.com": "hotmail.com",
87
+ "hotmal.com": "hotmail.com",
88
+ "hotmai.com": "hotmail.com",
89
+ "homail.com": "hotmail.com",
90
+ "hotmil.com": "hotmail.com",
91
+ "hotamail.com": "hotmail.com",
92
+ "hotmaill.com": "hotmail.com",
93
+ "hotmail.co": "hotmail.com",
94
+ "hotmail.om": "hotmail.com",
95
+ "hotmail.cm": "hotmail.com",
96
+ "hotmail.con": "hotmail.com",
97
+ "hitmail.com": "hotmail.com",
98
+ "hormail.com": "hotmail.com",
99
+ "hptmail.com": "hotmail.com",
100
+ "hotmaiil.com": "hotmail.com",
101
+ // Outlook typos
102
+ "outlok.com": "outlook.com",
103
+ "outloo.com": "outlook.com",
104
+ "outlook.co": "outlook.com",
105
+ "outlook.om": "outlook.com",
106
+ "outlook.cm": "outlook.com",
107
+ "outlook.con": "outlook.com",
108
+ "outllook.com": "outlook.com",
109
+ "outlookk.com": "outlook.com",
110
+ "outloook.com": "outlook.com",
111
+ "putlook.com": "outlook.com",
112
+ "oitlook.com": "outlook.com",
113
+ "outlool.com": "outlook.com",
114
+ // iCloud typos
115
+ "iclould.com": "icloud.com",
116
+ "icloud.co": "icloud.com",
117
+ "icloud.om": "icloud.com",
118
+ "icoud.com": "icloud.com",
119
+ "icloude.com": "icloud.com",
120
+ "iclud.com": "icloud.com",
121
+ "iclod.com": "icloud.com",
122
+ // Protonmail typos
123
+ "protonmal.com": "protonmail.com",
124
+ "protonmial.com": "protonmail.com",
125
+ "protonmai.com": "protonmail.com",
126
+ "protonmail.co": "protonmail.com",
127
+ "protonmail.om": "protonmail.com",
128
+ "protonmail.cm": "protonmail.com",
129
+ "protnmail.com": "protonmail.com",
130
+ "protommail.com": "protonmail.com",
131
+ // AOL typos
132
+ "aol.co": "aol.com",
133
+ "aol.om": "aol.com",
134
+ "aol.cm": "aol.com",
135
+ "ao.com": "aol.com",
136
+ "aoll.com": "aol.com",
137
+ // Live typos
138
+ "live.co": "live.com",
139
+ "live.om": "live.com",
140
+ "live.cm": "live.com",
141
+ "live.con": "live.com",
142
+ "livve.com": "live.com",
143
+ // MSN typos
144
+ "msn.co": "msn.com",
145
+ "msn.om": "msn.com",
146
+ "msn.cm": "msn.com",
147
+ "mns.com": "msn.com",
148
+ // Common TLD typos
149
+ ".con": ".com",
150
+ ".vom": ".com",
151
+ ".cpm": ".com",
152
+ ".ocm": ".com",
153
+ ".co,": ".com",
154
+ ".xom": ".com",
155
+ ".cim": ".com",
156
+ ".comm": ".com",
157
+ ".ner": ".net",
158
+ ".nte": ".net",
159
+ ".nett": ".net",
160
+ ".rog": ".org",
161
+ ".ogr": ".org",
162
+ ".orgg": ".org"
163
+ };
164
+ var popularDomains = [
165
+ "gmail.com",
166
+ "yahoo.com",
167
+ "hotmail.com",
168
+ "outlook.com",
169
+ "icloud.com",
170
+ "aol.com",
171
+ "protonmail.com",
172
+ "live.com",
173
+ "msn.com",
174
+ "mail.com",
175
+ "zoho.com",
176
+ "yandex.com",
177
+ "gmx.com",
178
+ "fastmail.com"
179
+ ];
180
+ function levenshteinDistance(str1, str2) {
181
+ const m = str1.length;
182
+ const n = str2.length;
183
+ const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
184
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
185
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
186
+ for (let i = 1; i <= m; i++) {
187
+ for (let j = 1; j <= n; j++) {
188
+ if (str1[i - 1] === str2[j - 1]) {
189
+ dp[i][j] = dp[i - 1][j - 1];
190
+ } else {
191
+ dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
192
+ }
193
+ }
194
+ }
195
+ return dp[m][n];
196
+ }
197
+ function getSuggestion(domain) {
198
+ const lowerDomain = domain.toLowerCase();
199
+ if (popularDomains.includes(lowerDomain)) {
200
+ return null;
201
+ }
202
+ if (domainTypos[lowerDomain]) {
203
+ return domainTypos[lowerDomain];
204
+ }
205
+ for (const [typo, correction] of Object.entries(domainTypos)) {
206
+ if (typo.startsWith(".") && lowerDomain.endsWith(typo)) {
207
+ return lowerDomain.slice(0, -typo.length) + correction;
208
+ }
209
+ }
210
+ let bestMatch = null;
211
+ let bestDistance = Infinity;
212
+ for (const popular of popularDomains) {
213
+ const distance = levenshteinDistance(lowerDomain, popular);
214
+ if (distance > 0 && distance <= 2 && distance < bestDistance) {
215
+ bestDistance = distance;
216
+ bestMatch = popular;
217
+ }
218
+ }
219
+ return bestMatch;
220
+ }
221
+ function getEmailSuggestion(email) {
222
+ const atIndex = email.indexOf("@");
223
+ if (atIndex === -1) return null;
224
+ const username = email.slice(0, atIndex);
225
+ const domain = email.slice(atIndex + 1);
226
+ const suggestedDomain = getSuggestion(domain);
227
+ if (suggestedDomain && suggestedDomain !== domain.toLowerCase()) {
228
+ return `${username}@${suggestedDomain}`;
229
+ }
230
+ return null;
231
+ }
232
+
233
+ // src/validators/syntax.ts
234
+ var EMAIL_REGEX = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i;
235
+ var SIMPLE_EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
236
+ var MAX_EMAIL_LENGTH = 254;
237
+ var MAX_LOCAL_LENGTH = 64;
238
+ var MAX_DOMAIN_LENGTH = 253;
239
+ function validateSyntax(email) {
240
+ const trimmedEmail = email.trim();
241
+ const normalizedEmail = normalizeEmail(trimmedEmail);
242
+ const invalidResult = {
243
+ address: trimmedEmail,
244
+ domain: "",
245
+ username: "",
246
+ isValidSyntax: false,
247
+ normalizedEmail,
248
+ suggestion: getEmailSuggestion(normalizedEmail)
249
+ };
250
+ if (!trimmedEmail) {
251
+ return invalidResult;
252
+ }
253
+ if (trimmedEmail.length > MAX_EMAIL_LENGTH) {
254
+ return invalidResult;
255
+ }
256
+ const parts = extractParts(trimmedEmail);
257
+ if (!parts) {
258
+ return invalidResult;
259
+ }
260
+ const { username, domain } = parts;
261
+ if (username.length > MAX_LOCAL_LENGTH) {
262
+ return {
263
+ ...invalidResult,
264
+ domain,
265
+ username
266
+ };
267
+ }
268
+ if (domain.length > MAX_DOMAIN_LENGTH) {
269
+ return {
270
+ ...invalidResult,
271
+ domain,
272
+ username
273
+ };
274
+ }
275
+ if (!username || !domain) {
276
+ return {
277
+ ...invalidResult,
278
+ domain,
279
+ username
280
+ };
281
+ }
282
+ if (!domain.includes(".") && !domain.startsWith("[")) {
283
+ return {
284
+ ...invalidResult,
285
+ domain,
286
+ username
287
+ };
288
+ }
289
+ const labels = domain.split(".");
290
+ for (const label of labels) {
291
+ if (label.length === 0 || label.length > 63) {
292
+ return {
293
+ ...invalidResult,
294
+ domain,
295
+ username
296
+ };
297
+ }
298
+ if (label.startsWith("-") || label.endsWith("-")) {
299
+ return {
300
+ ...invalidResult,
301
+ domain,
302
+ username
303
+ };
304
+ }
305
+ if (!/^[a-z0-9-]+$/i.test(label)) {
306
+ return {
307
+ ...invalidResult,
308
+ domain,
309
+ username
310
+ };
311
+ }
312
+ }
313
+ const tld = labels[labels.length - 1];
314
+ if (tld.length < 2 || !/^[a-z]+$/i.test(tld)) {
315
+ return {
316
+ ...invalidResult,
317
+ domain,
318
+ username
319
+ };
320
+ }
321
+ if (!SIMPLE_EMAIL_REGEX.test(trimmedEmail)) {
322
+ return {
323
+ ...invalidResult,
324
+ domain,
325
+ username
326
+ };
327
+ }
328
+ const isValid = EMAIL_REGEX.test(trimmedEmail);
329
+ return {
330
+ address: trimmedEmail,
331
+ domain: domain.toLowerCase(),
332
+ username,
333
+ isValidSyntax: isValid,
334
+ normalizedEmail,
335
+ suggestion: isValid ? getEmailSuggestion(normalizedEmail) : invalidResult.suggestion
336
+ };
337
+ }
338
+ function isValidSyntax(email) {
339
+ return validateSyntax(email).isValidSyntax;
340
+ }
341
+
342
+ // src/validators/dns.ts
343
+ import dns from "dns";
344
+ import { promisify } from "util";
345
+
346
+ // src/utils/cache.ts
347
+ var DnsCache = class {
348
+ cache = /* @__PURE__ */ new Map();
349
+ ttl;
350
+ /**
351
+ * Create a new DNS cache
352
+ * @param ttl Time-to-live in milliseconds (default: 5 minutes)
353
+ */
354
+ constructor(ttl = 3e5) {
355
+ this.ttl = ttl;
356
+ }
357
+ /**
358
+ * Get a cached entry for a domain
359
+ * @param domain The domain to look up
360
+ * @returns The cached entry or null if not found/expired
361
+ */
362
+ get(domain) {
363
+ const key = domain.toLowerCase();
364
+ const entry = this.cache.get(key);
365
+ if (!entry) {
366
+ return null;
367
+ }
368
+ if (Date.now() - entry.timestamp > this.ttl) {
369
+ this.cache.delete(key);
370
+ return null;
371
+ }
372
+ return entry;
373
+ }
374
+ /**
375
+ * Set a cache entry for a domain
376
+ * @param domain The domain to cache
377
+ * @param records The MX records
378
+ * @param acceptsMail Whether the domain accepts mail
379
+ */
380
+ set(domain, records, acceptsMail) {
381
+ const key = domain.toLowerCase();
382
+ this.cache.set(key, {
383
+ records,
384
+ acceptsMail,
385
+ timestamp: Date.now()
386
+ });
387
+ }
388
+ /**
389
+ * Clear all cached entries
390
+ */
391
+ clear() {
392
+ this.cache.clear();
393
+ }
394
+ /**
395
+ * Remove expired entries from the cache
396
+ */
397
+ cleanup() {
398
+ const now = Date.now();
399
+ for (const [key, entry] of this.cache.entries()) {
400
+ if (now - entry.timestamp > this.ttl) {
401
+ this.cache.delete(key);
402
+ }
403
+ }
404
+ }
405
+ /**
406
+ * Get the number of cached entries
407
+ */
408
+ get size() {
409
+ return this.cache.size;
410
+ }
411
+ /**
412
+ * Set the TTL for the cache
413
+ * @param ttl Time-to-live in milliseconds
414
+ */
415
+ setTtl(ttl) {
416
+ this.ttl = ttl;
417
+ }
418
+ };
419
+ var globalDnsCache = new DnsCache();
420
+
421
+ // src/validators/dns.ts
422
+ var resolveMx = promisify(dns.resolveMx);
423
+ var resolve4 = promisify(dns.resolve4);
424
+ var resolve6 = promisify(dns.resolve6);
425
+ function sortMxRecords(records) {
426
+ return records.sort((a, b) => a.priority - b.priority).map((r) => cleanDomain(r.exchange));
427
+ }
428
+ async function verifyMx(domain, options = {}, cache) {
429
+ const cleanedDomain = cleanDomain(domain);
430
+ const dnsCache = cache || globalDnsCache;
431
+ if (options.useDnsCache !== false) {
432
+ const cached = dnsCache.get(cleanedDomain);
433
+ if (cached) {
434
+ return {
435
+ acceptsMail: cached.acceptsMail,
436
+ records: cached.records
437
+ };
438
+ }
439
+ }
440
+ try {
441
+ const mxRecords = await resolveMx(cleanedDomain);
442
+ if (!mxRecords || mxRecords.length === 0) {
443
+ return await fallbackToAddressRecords(cleanedDomain, dnsCache, options);
444
+ }
445
+ if (mxRecords.length === 1 && mxRecords[0].priority === 0 && (mxRecords[0].exchange === "" || mxRecords[0].exchange === "." || mxRecords[0].exchange === "0.0.0.0")) {
446
+ const result2 = {
447
+ acceptsMail: false,
448
+ records: []
449
+ };
450
+ if (options.useDnsCache !== false) {
451
+ dnsCache.set(cleanedDomain, [], false);
452
+ }
453
+ return result2;
454
+ }
455
+ const sortedRecords = sortMxRecords(mxRecords);
456
+ const validRecords = sortedRecords.filter(
457
+ (r) => r && r !== "" && r !== "." && r !== "0.0.0.0" && r !== "localhost"
458
+ );
459
+ if (validRecords.length === 0) {
460
+ return await fallbackToAddressRecords(cleanedDomain, dnsCache, options);
461
+ }
462
+ const result = {
463
+ acceptsMail: true,
464
+ records: validRecords
465
+ };
466
+ if (options.useDnsCache !== false) {
467
+ dnsCache.set(cleanedDomain, validRecords, true);
468
+ }
469
+ return result;
470
+ } catch (error) {
471
+ const err = error;
472
+ if (err.code === "ENODATA" || err.code === "ENOTFOUND") {
473
+ return await fallbackToAddressRecords(cleanedDomain, dnsCache, options);
474
+ }
475
+ if (err.code === "ENOTFOUND" || err.code === "SERVFAIL") {
476
+ const result = {
477
+ acceptsMail: false,
478
+ records: []
479
+ };
480
+ if (options.useDnsCache !== false) {
481
+ dnsCache.set(cleanedDomain, [], false);
482
+ }
483
+ return result;
484
+ }
485
+ return {
486
+ acceptsMail: false,
487
+ records: []
488
+ };
489
+ }
490
+ }
491
+ async function fallbackToAddressRecords(domain, cache, options) {
492
+ try {
493
+ const aRecords = await resolve4(domain);
494
+ if (aRecords && aRecords.length > 0) {
495
+ const result2 = {
496
+ acceptsMail: true,
497
+ records: [domain]
498
+ // Use the domain itself as the mail host
499
+ };
500
+ if (options.useDnsCache !== false) {
501
+ cache.set(domain, [domain], true);
502
+ }
503
+ return result2;
504
+ }
505
+ } catch {
506
+ }
507
+ try {
508
+ const aaaaRecords = await resolve6(domain);
509
+ if (aaaaRecords && aaaaRecords.length > 0) {
510
+ const result2 = {
511
+ acceptsMail: true,
512
+ records: [domain]
513
+ // Use the domain itself as the mail host
514
+ };
515
+ if (options.useDnsCache !== false) {
516
+ cache.set(domain, [domain], true);
517
+ }
518
+ return result2;
519
+ }
520
+ } catch {
521
+ }
522
+ const result = {
523
+ acceptsMail: false,
524
+ records: []
525
+ };
526
+ if (options.useDnsCache !== false) {
527
+ cache.set(domain, [], false);
528
+ }
529
+ return result;
530
+ }
531
+ async function hasMxRecords(domain) {
532
+ const result = await verifyMx(domain);
533
+ return result.acceptsMail;
534
+ }
535
+ async function getMxRecords(domain) {
536
+ const result = await verifyMx(domain);
537
+ return result.records;
538
+ }
539
+
540
+ // src/validators/smtp.ts
541
+ import net from "net";
542
+ import os from "os";
543
+ var SMTP_PORT = 25;
544
+ var DEFAULT_TIMEOUT = 1e4;
545
+ var SmtpErrorType = {
546
+ CONNECTION_FAILED: "ConnectionFailed",
547
+ TIMEOUT: "Timeout",
548
+ INVALID_RESPONSE: "InvalidResponse",
549
+ HELO_FAILED: "HeloFailed",
550
+ MAIL_FROM_FAILED: "MailFromFailed",
551
+ RCPT_TO_FAILED: "RcptToFailed",
552
+ MAILBOX_NOT_FOUND: "MailboxNotFound",
553
+ MAILBOX_DISABLED: "MailboxDisabled",
554
+ GREYLISTED: "Greylisted",
555
+ BLOCKED: "Blocked",
556
+ UNKNOWN: "Unknown"
557
+ };
558
+ function parseResponse(data) {
559
+ const lines = data.trim().split("\r\n");
560
+ const lastLine = lines[lines.length - 1];
561
+ const code = parseInt(lastLine.substring(0, 3), 10);
562
+ const message = lastLine.substring(4);
563
+ return { code, message };
564
+ }
565
+ function isSuccessResponse(code) {
566
+ return code >= 200 && code < 400;
567
+ }
568
+ function sendCommand(socket, command, timeout) {
569
+ return new Promise((resolve, reject) => {
570
+ const timeoutId = setTimeout(() => {
571
+ reject(new Error("Command timeout"));
572
+ }, timeout);
573
+ const onData = (data) => {
574
+ clearTimeout(timeoutId);
575
+ socket.removeListener("data", onData);
576
+ socket.removeListener("error", onError);
577
+ const response = parseResponse(data.toString());
578
+ resolve(response);
579
+ };
580
+ const onError = (err) => {
581
+ clearTimeout(timeoutId);
582
+ socket.removeListener("data", onData);
583
+ reject(err);
584
+ };
585
+ socket.once("data", onData);
586
+ socket.once("error", onError);
587
+ if (command) {
588
+ socket.write(command + "\r\n");
589
+ }
590
+ });
591
+ }
592
+ function waitForGreeting(socket, timeout) {
593
+ return new Promise((resolve, reject) => {
594
+ const timeoutId = setTimeout(() => {
595
+ reject(new Error("Greeting timeout"));
596
+ }, timeout);
597
+ const onData = (data) => {
598
+ clearTimeout(timeoutId);
599
+ socket.removeListener("data", onData);
600
+ socket.removeListener("error", onError);
601
+ const response = parseResponse(data.toString());
602
+ resolve(response);
603
+ };
604
+ const onError = (err) => {
605
+ clearTimeout(timeoutId);
606
+ socket.removeListener("data", onData);
607
+ reject(err);
608
+ };
609
+ socket.once("data", onData);
610
+ socket.once("error", onError);
611
+ });
612
+ }
613
+ function generateRandomEmail(domain) {
614
+ const random = Math.random().toString(36).substring(2, 15);
615
+ return `${random}@${domain}`;
616
+ }
617
+ async function verifySmtp(email, mxHost, options = {}) {
618
+ const timeout = options.smtpTimeout || DEFAULT_TIMEOUT;
619
+ const heloHost = options.heloHost || os.hostname() || "localhost";
620
+ const senderAddress = options.senderAddress || "";
621
+ const detectCatchAll = options.detectCatchAll !== false;
622
+ const domain = email.split("@")[1];
623
+ return new Promise((resolve) => {
624
+ const socket = new net.Socket();
625
+ let resolved = false;
626
+ const cleanup = () => {
627
+ if (!socket.destroyed) {
628
+ try {
629
+ socket.write("QUIT\r\n");
630
+ } catch {
631
+ }
632
+ socket.destroy();
633
+ }
634
+ };
635
+ const finish = (result) => {
636
+ if (!resolved) {
637
+ resolved = true;
638
+ cleanup();
639
+ resolve(result);
640
+ }
641
+ };
642
+ socket.setTimeout(timeout);
643
+ socket.on("timeout", () => {
644
+ finish({
645
+ canConnectSmtp: false,
646
+ isDeliverable: false,
647
+ isCatchAll: false,
648
+ error: {
649
+ type: SmtpErrorType.TIMEOUT,
650
+ message: "Connection timed out"
651
+ }
652
+ });
653
+ });
654
+ socket.on("error", (err) => {
655
+ finish({
656
+ canConnectSmtp: false,
657
+ isDeliverable: false,
658
+ isCatchAll: false,
659
+ error: {
660
+ type: SmtpErrorType.CONNECTION_FAILED,
661
+ message: err.message
662
+ }
663
+ });
664
+ });
665
+ socket.connect(SMTP_PORT, mxHost, async () => {
666
+ try {
667
+ const greeting = await waitForGreeting(socket, timeout);
668
+ if (!isSuccessResponse(greeting.code)) {
669
+ finish({
670
+ canConnectSmtp: false,
671
+ isDeliverable: false,
672
+ isCatchAll: false,
673
+ error: {
674
+ type: SmtpErrorType.BLOCKED,
675
+ message: `Server rejected connection: ${greeting.message}`
676
+ }
677
+ });
678
+ return;
679
+ }
680
+ let heloResponse = await sendCommand(socket, `EHLO ${heloHost}`, timeout);
681
+ if (!isSuccessResponse(heloResponse.code)) {
682
+ heloResponse = await sendCommand(socket, `HELO ${heloHost}`, timeout);
683
+ if (!isSuccessResponse(heloResponse.code)) {
684
+ finish({
685
+ canConnectSmtp: true,
686
+ isDeliverable: false,
687
+ isCatchAll: false,
688
+ error: {
689
+ type: SmtpErrorType.HELO_FAILED,
690
+ message: `HELO failed: ${heloResponse.message}`
691
+ }
692
+ });
693
+ return;
694
+ }
695
+ }
696
+ const mailFromResponse = await sendCommand(
697
+ socket,
698
+ `MAIL FROM:<${senderAddress}>`,
699
+ timeout
700
+ );
701
+ if (!isSuccessResponse(mailFromResponse.code)) {
702
+ finish({
703
+ canConnectSmtp: true,
704
+ isDeliverable: false,
705
+ isCatchAll: false,
706
+ error: {
707
+ type: SmtpErrorType.MAIL_FROM_FAILED,
708
+ message: `MAIL FROM failed: ${mailFromResponse.message}`
709
+ }
710
+ });
711
+ return;
712
+ }
713
+ const rcptResponse = await sendCommand(socket, `RCPT TO:<${email}>`, timeout);
714
+ let isCatchAll = false;
715
+ if (detectCatchAll && isSuccessResponse(rcptResponse.code)) {
716
+ const randomEmail = generateRandomEmail(domain);
717
+ const catchAllResponse = await sendCommand(
718
+ socket,
719
+ `RCPT TO:<${randomEmail}>`,
720
+ timeout
721
+ );
722
+ isCatchAll = isSuccessResponse(catchAllResponse.code);
723
+ }
724
+ if (isSuccessResponse(rcptResponse.code)) {
725
+ finish({
726
+ canConnectSmtp: true,
727
+ isDeliverable: true,
728
+ isCatchAll,
729
+ error: null
730
+ });
731
+ } else if (rcptResponse.code === 550) {
732
+ finish({
733
+ canConnectSmtp: true,
734
+ isDeliverable: false,
735
+ isCatchAll: false,
736
+ error: {
737
+ type: SmtpErrorType.MAILBOX_NOT_FOUND,
738
+ message: rcptResponse.message
739
+ }
740
+ });
741
+ } else if (rcptResponse.code === 551 || rcptResponse.code === 553) {
742
+ finish({
743
+ canConnectSmtp: true,
744
+ isDeliverable: false,
745
+ isCatchAll: false,
746
+ error: {
747
+ type: SmtpErrorType.MAILBOX_NOT_FOUND,
748
+ message: rcptResponse.message
749
+ }
750
+ });
751
+ } else if (rcptResponse.code === 552) {
752
+ finish({
753
+ canConnectSmtp: true,
754
+ isDeliverable: false,
755
+ isCatchAll: false,
756
+ error: {
757
+ type: SmtpErrorType.MAILBOX_DISABLED,
758
+ message: rcptResponse.message
759
+ }
760
+ });
761
+ } else if (rcptResponse.code === 450 || rcptResponse.code === 451) {
762
+ finish({
763
+ canConnectSmtp: true,
764
+ isDeliverable: false,
765
+ isCatchAll: false,
766
+ error: {
767
+ type: SmtpErrorType.GREYLISTED,
768
+ message: rcptResponse.message
769
+ }
770
+ });
771
+ } else if (rcptResponse.code >= 500) {
772
+ finish({
773
+ canConnectSmtp: true,
774
+ isDeliverable: false,
775
+ isCatchAll: false,
776
+ error: {
777
+ type: SmtpErrorType.RCPT_TO_FAILED,
778
+ message: rcptResponse.message
779
+ }
780
+ });
781
+ } else {
782
+ finish({
783
+ canConnectSmtp: true,
784
+ isDeliverable: false,
785
+ isCatchAll: false,
786
+ error: {
787
+ type: SmtpErrorType.UNKNOWN,
788
+ message: `Unexpected response code ${rcptResponse.code}: ${rcptResponse.message}`
789
+ }
790
+ });
791
+ }
792
+ } catch (err) {
793
+ const error = err;
794
+ finish({
795
+ canConnectSmtp: true,
796
+ isDeliverable: false,
797
+ isCatchAll: false,
798
+ error: {
799
+ type: SmtpErrorType.UNKNOWN,
800
+ message: error.message
801
+ }
802
+ });
803
+ }
804
+ });
805
+ });
806
+ }
807
+ async function verifySmtpWithFallback(email, mxHosts, options = {}) {
808
+ if (mxHosts.length === 0) {
809
+ return {
810
+ canConnectSmtp: false,
811
+ isDeliverable: false,
812
+ isCatchAll: false,
813
+ error: {
814
+ type: SmtpErrorType.CONNECTION_FAILED,
815
+ message: "No MX hosts available"
816
+ }
817
+ };
818
+ }
819
+ let lastResult = null;
820
+ for (const mxHost of mxHosts) {
821
+ const result = await verifySmtp(email, mxHost, options);
822
+ if (result.isDeliverable || result.error?.type === SmtpErrorType.MAILBOX_NOT_FOUND) {
823
+ return result;
824
+ }
825
+ if (result.canConnectSmtp) {
826
+ lastResult = result;
827
+ }
828
+ if (!result.canConnectSmtp) {
829
+ lastResult = result;
830
+ continue;
831
+ }
832
+ }
833
+ return lastResult || {
834
+ canConnectSmtp: false,
835
+ isDeliverable: false,
836
+ isCatchAll: false,
837
+ error: {
838
+ type: SmtpErrorType.CONNECTION_FAILED,
839
+ message: "Failed to connect to any MX host"
840
+ }
841
+ };
842
+ }
843
+
844
+ // src/validators/misc.ts
845
+ import crypto from "crypto";
846
+ import https from "https";
847
+
848
+ // src/data/disposable.ts
849
+ var disposableDomains = /* @__PURE__ */ new Set([
850
+ // Popular disposable email services
851
+ "10minutemail.com",
852
+ "10minutemail.net",
853
+ "20minutemail.com",
854
+ "33mail.com",
855
+ "guerrillamail.com",
856
+ "guerrillamail.org",
857
+ "guerrillamail.net",
858
+ "guerrillamail.biz",
859
+ "guerrillamail.de",
860
+ "guerrillamailblock.com",
861
+ "sharklasers.com",
862
+ "grr.la",
863
+ "pokemail.net",
864
+ "spam4.me",
865
+ "mailinator.com",
866
+ "mailinator.net",
867
+ "mailinator.org",
868
+ "mailinator2.com",
869
+ "mailinater.com",
870
+ "tempmail.com",
871
+ "tempmail.net",
872
+ "temp-mail.org",
873
+ "temp-mail.io",
874
+ "tempail.com",
875
+ "tempmailaddress.com",
876
+ "throwaway.email",
877
+ "throwawaymail.com",
878
+ "trashmail.com",
879
+ "trashmail.net",
880
+ "trashmail.org",
881
+ "trashmail.me",
882
+ "trashemail.de",
883
+ "fakeinbox.com",
884
+ "fakemailgenerator.com",
885
+ "getnada.com",
886
+ "nada.email",
887
+ "getairmail.com",
888
+ "airmail.cc",
889
+ "dispostable.com",
890
+ "disposableemailaddresses.com",
891
+ "emailondeck.com",
892
+ "yopmail.com",
893
+ "yopmail.fr",
894
+ "yopmail.net",
895
+ "cool.fr.nf",
896
+ "jetable.fr.nf",
897
+ "nospam.ze.tc",
898
+ "nomail.xl.cx",
899
+ "mega.zik.dj",
900
+ "speed.1s.fr",
901
+ "courriel.fr.nf",
902
+ "moncourrier.fr.nf",
903
+ "monemail.fr.nf",
904
+ "monmail.fr.nf",
905
+ "mohmal.com",
906
+ "mailnesia.com",
907
+ "maildrop.cc",
908
+ "mailsac.com",
909
+ "mintemail.com",
910
+ "mytemp.email",
911
+ "mytrashmail.com",
912
+ "nowmymail.com",
913
+ "spambox.us",
914
+ "spamfree24.org",
915
+ "spamgourmet.com",
916
+ "spamspot.com",
917
+ "tempinbox.com",
918
+ "tempomail.fr",
919
+ "temporaryemail.net",
920
+ "temporaryforwarding.com",
921
+ "temporaryinbox.com",
922
+ "thankyou2010.com",
923
+ "thisisnotmyrealemail.com",
924
+ "throam.com",
925
+ "tmail.ws",
926
+ "tmpmail.net",
927
+ "tmpmail.org",
928
+ "wegwerfmail.de",
929
+ "wegwerfmail.net",
930
+ "wegwerfmail.org",
931
+ "wh4f.org",
932
+ "willhackforfood.biz",
933
+ "willselfdestruct.com",
934
+ "emailfake.com",
935
+ "emkei.cz",
936
+ "anonymbox.com",
937
+ "discard.email",
938
+ "discardmail.com",
939
+ "discardmail.de",
940
+ "spambog.com",
941
+ "spambog.de",
942
+ "spambog.ru",
943
+ "mailcatch.com",
944
+ "mail-temp.com",
945
+ "mailtemp.info",
946
+ "mailzilla.com",
947
+ "mailzilla.org",
948
+ "binkmail.com",
949
+ "bobmail.info",
950
+ "burnthespam.info",
951
+ "buyusedlibrarybooks.org",
952
+ "byom.de",
953
+ "deadaddress.com",
954
+ "despam.it",
955
+ "devnullmail.com",
956
+ "dfgh.net",
957
+ "e4ward.com",
958
+ "emailias.com",
959
+ "emailigo.de",
960
+ "emailsensei.com",
961
+ "emailtemporario.com.br",
962
+ "emailwarden.com",
963
+ "explodemail.com",
964
+ "fastacura.com",
965
+ "filzmail.com",
966
+ "fizmail.com",
967
+ "frapmail.com",
968
+ "gishpuppy.com",
969
+ "great-host.in",
970
+ "greensloth.com",
971
+ "haltospam.com",
972
+ "hidzz.com",
973
+ "hmamail.com",
974
+ "imails.info",
975
+ "incognitomail.com",
976
+ "incognitomail.net",
977
+ "incognitomail.org",
978
+ "infocom.zp.ua",
979
+ "instantemailaddress.com",
980
+ "ipoo.org",
981
+ "irish2me.com",
982
+ "jetable.com",
983
+ "kasmail.com",
984
+ "klassmaster.com",
985
+ "klzlv.com",
986
+ "koszmail.pl",
987
+ "kulturbetrieb.info",
988
+ "kurzepost.de",
989
+ "lawlita.com",
990
+ "letthemeatspam.com",
991
+ "lhsdv.com",
992
+ "lifebyfood.com",
993
+ "link2mail.net",
994
+ "litedrop.com",
995
+ "lol.ovpn.to",
996
+ "lookugly.com",
997
+ "lortemail.dk",
998
+ "lovemeleaveme.com",
999
+ "lr78.com",
1000
+ "maboard.com",
1001
+ "mail2rss.org",
1002
+ "mail333.com",
1003
+ "mailbidon.com",
1004
+ "mailblocks.com",
1005
+ "maildu.de",
1006
+ "maileater.com",
1007
+ "mailexpire.com",
1008
+ "mailfa.tk",
1009
+ "mailfork.com",
1010
+ "mailfreeonline.com",
1011
+ "mailguard.me",
1012
+ "mailimate.com",
1013
+ "mailin8r.com",
1014
+ "mailinblack.com",
1015
+ "mailincubator.com",
1016
+ "mailme.ir",
1017
+ "mailme.lv",
1018
+ "mailmetrash.com",
1019
+ "mailmoat.com",
1020
+ "mailnull.com",
1021
+ "mailorg.org",
1022
+ "mailscrap.com",
1023
+ "mailshell.com",
1024
+ "mailsiphon.com",
1025
+ "mailslite.com",
1026
+ "mailtemp.net",
1027
+ "mailtothis.com",
1028
+ "mailzi.ru",
1029
+ "makemetheking.com",
1030
+ "mbx.cc",
1031
+ "mega.zik.dj",
1032
+ "meltmail.com",
1033
+ "messagebeamer.de",
1034
+ "mezimages.net",
1035
+ "mierdamail.com",
1036
+ "migmail.pl",
1037
+ "migumail.com",
1038
+ "mintemail.com",
1039
+ "moburl.com",
1040
+ "moncourrier.fr.nf",
1041
+ "monemail.fr.nf",
1042
+ "monmail.fr.nf",
1043
+ "monumentmail.com",
1044
+ "ms51.hinet.net",
1045
+ "msb.minsmail.com",
1046
+ "mt2009.com",
1047
+ "mx0.wwwnew.eu",
1048
+ "mycleaninbox.net",
1049
+ "mypartyclip.de",
1050
+ "myphantomemail.com",
1051
+ "mysamp.de",
1052
+ "myspaceinc.com",
1053
+ "myspaceinc.net",
1054
+ "myspacepimpedup.com",
1055
+ "mytempemail.com",
1056
+ "neomailbox.com",
1057
+ "nepwk.com",
1058
+ "nervmich.net",
1059
+ "nervtmansen.de",
1060
+ "netmails.com",
1061
+ "netmails.net",
1062
+ "netzidiot.de",
1063
+ "neverbox.com",
1064
+ "no-spam.ws",
1065
+ "nobulk.com",
1066
+ "noclickemail.com",
1067
+ "nogmailspam.info",
1068
+ "nomail.xl.cx",
1069
+ "nomail2me.com",
1070
+ "nomorespamemails.com",
1071
+ "nospam.ze.tc",
1072
+ "nospam4.us",
1073
+ "nospamfor.us",
1074
+ "nospammail.net",
1075
+ "notmailinator.com",
1076
+ "nowhere.org",
1077
+ "nowmymail.com",
1078
+ "nurfuerspam.de",
1079
+ "nus.edu.sg",
1080
+ "objectmail.com",
1081
+ "obobbo.com",
1082
+ "oneoffemail.com",
1083
+ "onewaymail.com",
1084
+ "onlatedotcom.info",
1085
+ "online.ms",
1086
+ "oopi.org",
1087
+ "opayq.com",
1088
+ "ordinaryamerican.net",
1089
+ "otherinbox.com",
1090
+ "ourklips.com",
1091
+ "outlawspam.com",
1092
+ "ovpn.to",
1093
+ "owlpic.com",
1094
+ "pancakemail.com",
1095
+ "pjjkp.com",
1096
+ "plexolan.de",
1097
+ "poczta.onet.pl",
1098
+ "politikerclub.de",
1099
+ "poofy.org",
1100
+ "pookmail.com",
1101
+ "privacy.net",
1102
+ "privy-mail.com",
1103
+ "privymail.de",
1104
+ "proxymail.eu",
1105
+ "prtnx.com",
1106
+ "punkass.com",
1107
+ "putthisinyourspamdatabase.com",
1108
+ "qq.com",
1109
+ "quickinbox.com",
1110
+ "quickmail.nl",
1111
+ "rcpt.at",
1112
+ "reallymymail.com",
1113
+ "realtyalerts.ca",
1114
+ "recode.me",
1115
+ "recursor.net",
1116
+ "recyclemail.dk",
1117
+ "regbypass.com",
1118
+ "regbypass.comsafe-mail.net",
1119
+ "rejectmail.com",
1120
+ "remail.cf",
1121
+ "rhyta.com",
1122
+ "rklips.com",
1123
+ "rmqkr.net",
1124
+ "rppkn.com",
1125
+ "rtrtr.com",
1126
+ "s0ny.net",
1127
+ "safe-mail.net",
1128
+ "safersignup.de",
1129
+ "safetymail.info",
1130
+ "safetypost.de",
1131
+ "sandelf.de",
1132
+ "saynotospams.com",
1133
+ "schafmail.de",
1134
+ "selfdestructingmail.com",
1135
+ "sendspamhere.com",
1136
+ "shieldemail.com",
1137
+ "shiftmail.com",
1138
+ "shitmail.me",
1139
+ "shortmail.net",
1140
+ "shut.name",
1141
+ "shut.ws",
1142
+ "sibmail.com",
1143
+ "sinnlos-mail.de",
1144
+ "siteposter.net",
1145
+ "skeefmail.com",
1146
+ "slaskpost.se",
1147
+ "slopsbox.com",
1148
+ "slowfoodfoothills.xyz",
1149
+ "smashmail.de",
1150
+ "smellfear.com",
1151
+ "snakemail.com",
1152
+ "sneakemail.com",
1153
+ "sneakmail.de",
1154
+ "snkmail.com",
1155
+ "sofimail.com",
1156
+ "sofort-mail.de",
1157
+ "softpls.asia",
1158
+ "sogetthis.com",
1159
+ "sohu.com",
1160
+ "soisz.com",
1161
+ "solvemail.info",
1162
+ "soodonims.com",
1163
+ "spam.la",
1164
+ "spam.su",
1165
+ "spamavert.com",
1166
+ "spambob.com",
1167
+ "spambob.net",
1168
+ "spambob.org",
1169
+ "spambog.com",
1170
+ "spambog.de",
1171
+ "spambog.ru",
1172
+ "spambox.info",
1173
+ "spambox.irishspringrealty.com",
1174
+ "spambox.us",
1175
+ "spamcannon.com",
1176
+ "spamcannon.net",
1177
+ "spamcero.com",
1178
+ "spamcon.org",
1179
+ "spamcorptastic.com",
1180
+ "spamcowboy.com",
1181
+ "spamcowboy.net",
1182
+ "spamcowboy.org",
1183
+ "spamday.com",
1184
+ "spamex.com",
1185
+ "spamfree.eu",
1186
+ "spamfree24.com",
1187
+ "spamfree24.de",
1188
+ "spamfree24.eu",
1189
+ "spamfree24.info",
1190
+ "spamfree24.net",
1191
+ "spamfree24.org",
1192
+ "spamgoes.in",
1193
+ "spamherelots.com",
1194
+ "spamhereplease.com",
1195
+ "spamhole.com",
1196
+ "spamify.com",
1197
+ "spaminator.de",
1198
+ "spamkill.info",
1199
+ "spaml.com",
1200
+ "spaml.de",
1201
+ "spammotel.com",
1202
+ "spamobox.com",
1203
+ "spamoff.de",
1204
+ "spamsalad.in",
1205
+ "spamslicer.com",
1206
+ "spamspot.com",
1207
+ "spamstack.net",
1208
+ "spamthis.co.uk",
1209
+ "spamtroll.net",
1210
+ "speed.1s.fr",
1211
+ "spoofmail.de",
1212
+ "squizzy.de",
1213
+ "ssoia.com",
1214
+ "startkeys.com",
1215
+ "stinkefinger.net",
1216
+ "stop-my-spam.cf",
1217
+ "stop-my-spam.com",
1218
+ "stop-my-spam.ga",
1219
+ "stop-my-spam.ml",
1220
+ "stop-my-spam.tk",
1221
+ "streetwisemail.com",
1222
+ "stuffmail.de",
1223
+ "supergreatmail.com",
1224
+ "supermailer.jp",
1225
+ "superrito.com",
1226
+ "superstachel.de",
1227
+ "suremail.info",
1228
+ "svk.jp",
1229
+ "sweetxxx.de",
1230
+ "tagyourself.com",
1231
+ "talkinator.com",
1232
+ "tapchicuoihoi.com",
1233
+ "techemail.com",
1234
+ "techgroup.me",
1235
+ "teewars.org",
1236
+ "teleosaurs.xyz",
1237
+ "teleworm.com",
1238
+ "teleworm.us",
1239
+ "temp.emeraldwebmail.com",
1240
+ "tempail.com",
1241
+ "tempalias.com",
1242
+ "tempe-mail.com",
1243
+ "tempemail.biz",
1244
+ "tempemail.co.za",
1245
+ "tempemail.com",
1246
+ "tempemail.net",
1247
+ "tempinbox.co.uk",
1248
+ "tempinbox.com",
1249
+ "tempmail.co",
1250
+ "tempmail.de",
1251
+ "tempmail.eu",
1252
+ "tempmail.it",
1253
+ "tempmail.net",
1254
+ "tempmail.us",
1255
+ "tempmail2.com",
1256
+ "tempmaildemo.com",
1257
+ "tempmailer.com",
1258
+ "tempmailer.de",
1259
+ "tempomail.fr",
1260
+ "temporarioemail.com.br",
1261
+ "temporaryemail.net",
1262
+ "temporaryemail.us",
1263
+ "temporaryforwarding.com",
1264
+ "temporaryinbox.com",
1265
+ "temporarymailaddress.com",
1266
+ "tempthe.net",
1267
+ "thankspam.info",
1268
+ "thankyou2010.com",
1269
+ "thecloudindex.com",
1270
+ "thelimestones.com",
1271
+ "thisisnotmyrealemail.com",
1272
+ "throam.com",
1273
+ "throwam.com",
1274
+ "throwawayemailaddress.com",
1275
+ "throwawaymail.com",
1276
+ "tilien.com",
1277
+ "tittbit.in",
1278
+ "tmailinator.com",
1279
+ "tmail.ws",
1280
+ "toiea.com",
1281
+ "tokenmail.de",
1282
+ "toomail.biz",
1283
+ "topranklist.de",
1284
+ "tradermail.info",
1285
+ "trash-amil.com",
1286
+ "trash-mail.at",
1287
+ "trash-mail.com",
1288
+ "trash-mail.de",
1289
+ "trash2009.com",
1290
+ "trash2010.com",
1291
+ "trash2011.com",
1292
+ "trashcanmail.com",
1293
+ "trashdevil.com",
1294
+ "trashdevil.de",
1295
+ "trashemail.de",
1296
+ "trashmail.at",
1297
+ "trashmail.com",
1298
+ "trashmail.de",
1299
+ "trashmail.me",
1300
+ "trashmail.net",
1301
+ "trashmail.org",
1302
+ "trashmail.ws",
1303
+ "trashmailer.com",
1304
+ "trashymail.com",
1305
+ "trashymail.net",
1306
+ "trbvm.com",
1307
+ "trickmail.net",
1308
+ "trillianpro.com",
1309
+ "tryalert.com",
1310
+ "turual.com",
1311
+ "twinmail.de",
1312
+ "tyldd.com",
1313
+ "uggsrock.com",
1314
+ "umail.net",
1315
+ "upliftnow.com",
1316
+ "uplipht.com",
1317
+ "uroid.com",
1318
+ "us.af",
1319
+ "valemail.net",
1320
+ "venompen.com",
1321
+ "veryrealemail.com",
1322
+ "viditag.com",
1323
+ "viralplays.com",
1324
+ "vpn.st",
1325
+ "vsimcard.com",
1326
+ "vubby.com",
1327
+ "wasteland.rfc822.org",
1328
+ "webemail.me",
1329
+ "webm4il.info",
1330
+ "webuser.in",
1331
+ "wee.my",
1332
+ "weg-werf-email.de",
1333
+ "wegwerf-emails.de",
1334
+ "wegwerfadresse.de",
1335
+ "wegwerfemail.com",
1336
+ "wegwerfemail.de",
1337
+ "wegwerfmail.de",
1338
+ "wegwerfmail.info",
1339
+ "wegwerfmail.net",
1340
+ "wegwerfmail.org",
1341
+ "wetrainbayarea.com",
1342
+ "wetrainbayarea.org",
1343
+ "wh4f.org",
1344
+ "whatiaas.com",
1345
+ "whatpaas.com",
1346
+ "whopy.com",
1347
+ "whtjddn.33mail.com",
1348
+ "whyspam.me",
1349
+ "willhackforfood.biz",
1350
+ "willselfdestruct.com",
1351
+ "winemaven.info",
1352
+ "wolfsmail.tk",
1353
+ "wollan.info",
1354
+ "worldspace.link",
1355
+ "wpdork.com",
1356
+ "wronghead.com",
1357
+ "wuzup.net",
1358
+ "wuzupmail.net",
1359
+ "wwwnew.eu",
1360
+ "xagloo.co",
1361
+ "xagloo.com",
1362
+ "xemaps.com",
1363
+ "xents.com",
1364
+ "xmaily.com",
1365
+ "xoxy.net",
1366
+ "yapped.net",
1367
+ "yep.it",
1368
+ "yogamaven.com",
1369
+ "yomail.info",
1370
+ "yopmail.com",
1371
+ "yopmail.fr",
1372
+ "yopmail.gq",
1373
+ "yopmail.net",
1374
+ "you-spam.com",
1375
+ "yourdomain.com",
1376
+ "ypmail.webarnak.fr.eu.org",
1377
+ "yuurok.com",
1378
+ "za.com",
1379
+ "zehnminuten.de",
1380
+ "zehnminutenmail.de",
1381
+ "zippymail.info",
1382
+ "zoaxe.com",
1383
+ "zoemail.com",
1384
+ "zoemail.net",
1385
+ "zoemail.org",
1386
+ "zomg.info",
1387
+ "zxcv.com",
1388
+ "zxcvbnm.com",
1389
+ "zzz.com"
1390
+ ]);
1391
+ function isDisposableDomain(domain) {
1392
+ return disposableDomains.has(domain.toLowerCase());
1393
+ }
1394
+
1395
+ // src/data/roles.ts
1396
+ var rolePrefixes = /* @__PURE__ */ new Set([
1397
+ // Administrative
1398
+ "admin",
1399
+ "administrator",
1400
+ "postmaster",
1401
+ "hostmaster",
1402
+ "webmaster",
1403
+ "root",
1404
+ "sysadmin",
1405
+ "it",
1406
+ "tech",
1407
+ "technical",
1408
+ // Support
1409
+ "support",
1410
+ "help",
1411
+ "helpdesk",
1412
+ "customerservice",
1413
+ "customer-service",
1414
+ "customercare",
1415
+ "customer-care",
1416
+ "service",
1417
+ "assist",
1418
+ "assistance",
1419
+ // Sales and Marketing
1420
+ "sales",
1421
+ "marketing",
1422
+ "advertising",
1423
+ "ads",
1424
+ "promo",
1425
+ "promotions",
1426
+ "partners",
1427
+ "partnership",
1428
+ "partnerships",
1429
+ "affiliate",
1430
+ "affiliates",
1431
+ "leads",
1432
+ "enquiry",
1433
+ "enquiries",
1434
+ "inquiry",
1435
+ "inquiries",
1436
+ // General Contact
1437
+ "info",
1438
+ "information",
1439
+ "contact",
1440
+ "contactus",
1441
+ "contact-us",
1442
+ "hello",
1443
+ "hi",
1444
+ "hey",
1445
+ "mail",
1446
+ "email",
1447
+ "office",
1448
+ "general",
1449
+ "reception",
1450
+ // Human Resources
1451
+ "hr",
1452
+ "humanresources",
1453
+ "human-resources",
1454
+ "jobs",
1455
+ "careers",
1456
+ "career",
1457
+ "recruitment",
1458
+ "recruiting",
1459
+ "talent",
1460
+ "hiring",
1461
+ "resume",
1462
+ "resumes",
1463
+ "cv",
1464
+ "employment",
1465
+ // Finance and Legal
1466
+ "billing",
1467
+ "finance",
1468
+ "accounting",
1469
+ "accounts",
1470
+ "payroll",
1471
+ "invoices",
1472
+ "invoice",
1473
+ "payments",
1474
+ "payment",
1475
+ "legal",
1476
+ "compliance",
1477
+ "privacy",
1478
+ "gdpr",
1479
+ // Media and PR
1480
+ "press",
1481
+ "media",
1482
+ "pr",
1483
+ "publicrelations",
1484
+ "public-relations",
1485
+ "news",
1486
+ "newsroom",
1487
+ "communications",
1488
+ "comms",
1489
+ // Security
1490
+ "security",
1491
+ "abuse",
1492
+ "spam",
1493
+ "phishing",
1494
+ "fraud",
1495
+ "noc",
1496
+ "cert",
1497
+ // Operations
1498
+ "operations",
1499
+ "ops",
1500
+ "devops",
1501
+ "engineering",
1502
+ "dev",
1503
+ "development",
1504
+ "product",
1505
+ "feedback",
1506
+ // Team/Group addresses
1507
+ "team",
1508
+ "staff",
1509
+ "all",
1510
+ "everyone",
1511
+ "group",
1512
+ "department",
1513
+ "board",
1514
+ "management",
1515
+ "exec",
1516
+ "executive",
1517
+ "executives",
1518
+ // E-commerce
1519
+ "orders",
1520
+ "order",
1521
+ "shipping",
1522
+ "returns",
1523
+ "refunds",
1524
+ "shop",
1525
+ "store",
1526
+ "buy",
1527
+ "purchase",
1528
+ "checkout",
1529
+ // Automated/System
1530
+ "noreply",
1531
+ "no-reply",
1532
+ "donotreply",
1533
+ "do-not-reply",
1534
+ "mailer-daemon",
1535
+ "mailerdaemon",
1536
+ "bounce",
1537
+ "bounces",
1538
+ "auto",
1539
+ "autoresponder",
1540
+ "notifications",
1541
+ "notification",
1542
+ "alerts",
1543
+ "alert",
1544
+ "updates",
1545
+ "newsletter",
1546
+ "newsletters",
1547
+ "subscribe",
1548
+ "unsubscribe",
1549
+ // Miscellaneous
1550
+ "ftp",
1551
+ "www",
1552
+ "web",
1553
+ "api",
1554
+ "test",
1555
+ "testing",
1556
+ "demo",
1557
+ "sample",
1558
+ "example"
1559
+ ]);
1560
+ function isRolePrefix(prefix) {
1561
+ return rolePrefixes.has(prefix.toLowerCase());
1562
+ }
1563
+
1564
+ // src/data/free-providers.ts
1565
+ var freeProviders = /* @__PURE__ */ new Set([
1566
+ // Google
1567
+ "gmail.com",
1568
+ "googlemail.com",
1569
+ // Microsoft
1570
+ "outlook.com",
1571
+ "outlook.co.uk",
1572
+ "outlook.com.br",
1573
+ "outlook.com.au",
1574
+ "outlook.de",
1575
+ "outlook.fr",
1576
+ "outlook.it",
1577
+ "outlook.es",
1578
+ "outlook.jp",
1579
+ "hotmail.com",
1580
+ "hotmail.co.uk",
1581
+ "hotmail.com.br",
1582
+ "hotmail.com.au",
1583
+ "hotmail.de",
1584
+ "hotmail.fr",
1585
+ "hotmail.it",
1586
+ "hotmail.es",
1587
+ "hotmail.co.jp",
1588
+ "live.com",
1589
+ "live.co.uk",
1590
+ "live.com.au",
1591
+ "live.de",
1592
+ "live.fr",
1593
+ "live.it",
1594
+ "live.nl",
1595
+ "live.se",
1596
+ "msn.com",
1597
+ // Yahoo
1598
+ "yahoo.com",
1599
+ "yahoo.co.uk",
1600
+ "yahoo.com.br",
1601
+ "yahoo.com.au",
1602
+ "yahoo.de",
1603
+ "yahoo.fr",
1604
+ "yahoo.it",
1605
+ "yahoo.es",
1606
+ "yahoo.co.jp",
1607
+ "yahoo.co.in",
1608
+ "yahoo.ca",
1609
+ "yahoo.com.mx",
1610
+ "yahoo.com.ar",
1611
+ "yahoo.com.sg",
1612
+ "yahoo.co.id",
1613
+ "yahoo.com.ph",
1614
+ "yahoo.com.tw",
1615
+ "yahoo.com.hk",
1616
+ "yahoo.ie",
1617
+ "yahoo.co.nz",
1618
+ "yahoo.com.vn",
1619
+ "ymail.com",
1620
+ "rocketmail.com",
1621
+ // Apple
1622
+ "icloud.com",
1623
+ "me.com",
1624
+ "mac.com",
1625
+ // AOL
1626
+ "aol.com",
1627
+ "aol.co.uk",
1628
+ "aol.de",
1629
+ "aol.fr",
1630
+ "aim.com",
1631
+ // Proton
1632
+ "protonmail.com",
1633
+ "protonmail.ch",
1634
+ "proton.me",
1635
+ "pm.me",
1636
+ // Zoho
1637
+ "zoho.com",
1638
+ "zohomail.com",
1639
+ "zohomail.in",
1640
+ "zohomail.eu",
1641
+ // Mail.com
1642
+ "mail.com",
1643
+ "email.com",
1644
+ "usa.com",
1645
+ "myself.com",
1646
+ "consultant.com",
1647
+ "post.com",
1648
+ "europe.com",
1649
+ "asia.com",
1650
+ "iname.com",
1651
+ "writeme.com",
1652
+ "dr.com",
1653
+ "cheerful.com",
1654
+ "accountant.com",
1655
+ "techie.com",
1656
+ "engineer.com",
1657
+ "activist.com",
1658
+ "contractor.com",
1659
+ "artlover.com",
1660
+ // GMX
1661
+ "gmx.com",
1662
+ "gmx.net",
1663
+ "gmx.de",
1664
+ "gmx.at",
1665
+ "gmx.ch",
1666
+ "gmx.fr",
1667
+ "gmx.co.uk",
1668
+ "gmx.us",
1669
+ // Tutanota/Tuta
1670
+ "tutanota.com",
1671
+ "tutanota.de",
1672
+ "tutamail.com",
1673
+ "tuta.io",
1674
+ "keemail.me",
1675
+ // Fastmail
1676
+ "fastmail.com",
1677
+ "fastmail.fm",
1678
+ // Mailfence
1679
+ "mailfence.com",
1680
+ // Hushmail
1681
+ "hushmail.com",
1682
+ "hush.com",
1683
+ "hush.ai",
1684
+ "mac.hush.com",
1685
+ // Runbox
1686
+ "runbox.com",
1687
+ // Posteo
1688
+ "posteo.de",
1689
+ "posteo.net",
1690
+ // Yandex
1691
+ "yandex.com",
1692
+ "yandex.ru",
1693
+ "yandex.ua",
1694
+ "yandex.by",
1695
+ "yandex.kz",
1696
+ "ya.ru",
1697
+ // Mail.ru
1698
+ "mail.ru",
1699
+ "inbox.ru",
1700
+ "list.ru",
1701
+ "bk.ru",
1702
+ // Chinese providers
1703
+ "qq.com",
1704
+ "163.com",
1705
+ "126.com",
1706
+ "sina.com",
1707
+ "sina.cn",
1708
+ "sohu.com",
1709
+ "aliyun.com",
1710
+ "foxmail.com",
1711
+ // Indian providers
1712
+ "rediffmail.com",
1713
+ "rediff.com",
1714
+ // European providers
1715
+ "web.de",
1716
+ "t-online.de",
1717
+ "freenet.de",
1718
+ "arcor.de",
1719
+ "orange.fr",
1720
+ "laposte.net",
1721
+ "free.fr",
1722
+ "sfr.fr",
1723
+ "wanadoo.fr",
1724
+ "libero.it",
1725
+ "virgilio.it",
1726
+ "tin.it",
1727
+ "alice.it",
1728
+ "tiscali.it",
1729
+ "tiscali.co.uk",
1730
+ "terra.com.br",
1731
+ "uol.com.br",
1732
+ "bol.com.br",
1733
+ "ig.com.br",
1734
+ "wp.pl",
1735
+ "onet.pl",
1736
+ "interia.pl",
1737
+ "o2.pl",
1738
+ "seznam.cz",
1739
+ "centrum.cz",
1740
+ "azet.sk",
1741
+ // Misc
1742
+ "inbox.com",
1743
+ "lycos.com",
1744
+ "naver.com",
1745
+ "daum.net",
1746
+ "hanmail.net",
1747
+ "comcast.net",
1748
+ "verizon.net",
1749
+ "att.net",
1750
+ "sbcglobal.net",
1751
+ "bellsouth.net",
1752
+ "charter.net",
1753
+ "cox.net",
1754
+ "earthlink.net",
1755
+ "juno.com",
1756
+ "netzero.net",
1757
+ "optonline.net",
1758
+ "btinternet.com",
1759
+ "talktalk.net",
1760
+ "virginmedia.com",
1761
+ "sky.com",
1762
+ "ntlworld.com",
1763
+ "shaw.ca",
1764
+ "rogers.com",
1765
+ "sympatico.ca",
1766
+ "telus.net",
1767
+ "bigpond.com",
1768
+ "bigpond.net.au",
1769
+ "optusnet.com.au",
1770
+ "ozemail.com.au"
1771
+ ]);
1772
+ function isFreeProvider(domain) {
1773
+ return freeProviders.has(domain.toLowerCase());
1774
+ }
1775
+
1776
+ // src/validators/misc.ts
1777
+ function checkDisposable(domain) {
1778
+ return isDisposableDomain(domain.toLowerCase());
1779
+ }
1780
+ function checkRoleAccount(username) {
1781
+ const cleanUsername = username.split("+")[0];
1782
+ return isRolePrefix(cleanUsername.toLowerCase());
1783
+ }
1784
+ function checkFreeProvider(domain) {
1785
+ return isFreeProvider(domain.toLowerCase());
1786
+ }
1787
+ function md5(str) {
1788
+ return crypto.createHash("md5").update(str.trim().toLowerCase()).digest("hex");
1789
+ }
1790
+ function checkGravatar(email) {
1791
+ return new Promise((resolve) => {
1792
+ const hash = md5(email);
1793
+ const url = `https://www.gravatar.com/avatar/${hash}?d=404`;
1794
+ const req = https.get(url, { timeout: 5e3 }, (res) => {
1795
+ if (res.statusCode === 200) {
1796
+ resolve(`https://www.gravatar.com/avatar/${hash}`);
1797
+ } else {
1798
+ resolve(null);
1799
+ }
1800
+ res.resume();
1801
+ });
1802
+ req.on("error", () => {
1803
+ resolve(null);
1804
+ });
1805
+ req.on("timeout", () => {
1806
+ req.destroy();
1807
+ resolve(null);
1808
+ });
1809
+ });
1810
+ }
1811
+ async function checkMisc(email, options = {}) {
1812
+ const atIndex = email.indexOf("@");
1813
+ if (atIndex === -1) {
1814
+ return {
1815
+ isDisposable: false,
1816
+ isRoleAccount: false,
1817
+ isFreeProvider: false,
1818
+ gravatarUrl: null
1819
+ };
1820
+ }
1821
+ const username = email.slice(0, atIndex);
1822
+ const domain = email.slice(atIndex + 1);
1823
+ const isDisposable = options.checkDisposable !== false ? checkDisposable(domain) : false;
1824
+ const isRoleAccount = options.checkRoleAccount !== false ? checkRoleAccount(username) : false;
1825
+ const isFree = options.checkFreeProvider !== false ? checkFreeProvider(domain) : false;
1826
+ let gravatarUrl = null;
1827
+ if (options.checkGravatar === true) {
1828
+ gravatarUrl = await checkGravatar(email);
1829
+ }
1830
+ return {
1831
+ isDisposable,
1832
+ isRoleAccount,
1833
+ isFreeProvider: isFree,
1834
+ gravatarUrl
1835
+ };
1836
+ }
1837
+
1838
+ // src/index.ts
1839
+ var defaultOptions = {
1840
+ smtpTimeout: 1e4,
1841
+ verifySmtp: true,
1842
+ checkDisposable: true,
1843
+ checkRoleAccount: true,
1844
+ checkFreeProvider: true,
1845
+ checkGravatar: false,
1846
+ heloHost: "",
1847
+ senderAddress: "",
1848
+ dnsCacheTtl: 3e5,
1849
+ useDnsCache: true,
1850
+ detectCatchAll: true,
1851
+ proxy: void 0
1852
+ };
1853
+ function determineReachability(syntaxValid, mxValid, smtpResult, options) {
1854
+ if (!syntaxValid) {
1855
+ return "invalid";
1856
+ }
1857
+ if (!mxValid) {
1858
+ return "invalid";
1859
+ }
1860
+ if (options.verifySmtp === false) {
1861
+ return "risky";
1862
+ }
1863
+ if (!smtpResult) {
1864
+ return "unknown";
1865
+ }
1866
+ if (smtpResult.isCatchAll) {
1867
+ return "risky";
1868
+ }
1869
+ if (smtpResult.isDeliverable) {
1870
+ return "safe";
1871
+ }
1872
+ if (!smtpResult.canConnectSmtp) {
1873
+ return "unknown";
1874
+ }
1875
+ if (smtpResult.error?.type === SmtpErrorType.MAILBOX_NOT_FOUND) {
1876
+ return "invalid";
1877
+ }
1878
+ if (smtpResult.error?.type === SmtpErrorType.GREYLISTED) {
1879
+ return "risky";
1880
+ }
1881
+ return "risky";
1882
+ }
1883
+ async function validate(email, options = {}) {
1884
+ const opts = { ...defaultOptions, ...options };
1885
+ if (opts.useDnsCache && opts.dnsCacheTtl) {
1886
+ globalDnsCache.setTtl(opts.dnsCacheTtl);
1887
+ }
1888
+ const syntaxResult = validateSyntax(email);
1889
+ if (!syntaxResult.isValidSyntax) {
1890
+ return {
1891
+ input: email,
1892
+ isReachable: "invalid",
1893
+ syntax: syntaxResult,
1894
+ mx: {
1895
+ acceptsMail: false,
1896
+ records: []
1897
+ },
1898
+ smtp: {
1899
+ canConnectSmtp: false,
1900
+ isDeliverable: false,
1901
+ isCatchAll: false,
1902
+ error: null
1903
+ },
1904
+ misc: {
1905
+ isDisposable: false,
1906
+ isRoleAccount: false,
1907
+ isFreeProvider: false,
1908
+ gravatarUrl: null
1909
+ }
1910
+ };
1911
+ }
1912
+ const mxResult = await verifyMx(syntaxResult.domain, opts);
1913
+ if (!mxResult.acceptsMail) {
1914
+ const miscResult2 = await checkMisc(email, opts);
1915
+ return {
1916
+ input: email,
1917
+ isReachable: "invalid",
1918
+ syntax: syntaxResult,
1919
+ mx: mxResult,
1920
+ smtp: {
1921
+ canConnectSmtp: false,
1922
+ isDeliverable: false,
1923
+ isCatchAll: false,
1924
+ error: {
1925
+ type: "NoMxRecords",
1926
+ message: "Domain does not accept mail"
1927
+ }
1928
+ },
1929
+ misc: miscResult2
1930
+ };
1931
+ }
1932
+ let smtpResult = {
1933
+ canConnectSmtp: false,
1934
+ isDeliverable: false,
1935
+ isCatchAll: false,
1936
+ error: null
1937
+ };
1938
+ if (opts.verifySmtp) {
1939
+ smtpResult = await verifySmtpWithFallback(email, mxResult.records, opts);
1940
+ }
1941
+ const miscResult = await checkMisc(email, opts);
1942
+ const isReachable = determineReachability(
1943
+ syntaxResult.isValidSyntax,
1944
+ mxResult.acceptsMail,
1945
+ smtpResult,
1946
+ opts
1947
+ );
1948
+ return {
1949
+ input: email,
1950
+ isReachable,
1951
+ syntax: syntaxResult,
1952
+ mx: mxResult,
1953
+ smtp: smtpResult,
1954
+ misc: miscResult
1955
+ };
1956
+ }
1957
+ async function validateBulk(emails, options = {}) {
1958
+ const { concurrency = 5, delayBetween = 0, onProgress, ...validateOptions } = options;
1959
+ const results = [];
1960
+ let completed = 0;
1961
+ for (let i = 0; i < emails.length; i += concurrency) {
1962
+ const batch = emails.slice(i, i + concurrency);
1963
+ const batchPromises = batch.map(async (email) => {
1964
+ const result = await validate(email, validateOptions);
1965
+ completed++;
1966
+ if (onProgress) {
1967
+ onProgress(completed, emails.length, result);
1968
+ }
1969
+ return result;
1970
+ });
1971
+ const batchResults = await Promise.all(batchPromises);
1972
+ results.push(...batchResults);
1973
+ if (delayBetween > 0 && i + concurrency < emails.length) {
1974
+ await new Promise((resolve) => setTimeout(resolve, delayBetween));
1975
+ }
1976
+ }
1977
+ return results;
1978
+ }
1979
+ async function validateQuick(email) {
1980
+ return validate(email, { verifySmtp: false });
1981
+ }
1982
+ function clearDnsCache() {
1983
+ globalDnsCache.clear();
1984
+ }
1985
+ function getDnsCacheSize() {
1986
+ return globalDnsCache.size;
1987
+ }
1988
+ var index_default = {
1989
+ validate,
1990
+ validateBulk,
1991
+ validateQuick,
1992
+ clearDnsCache,
1993
+ getDnsCacheSize,
1994
+ // Utilities
1995
+ normalizeEmail,
1996
+ extractParts,
1997
+ getSuggestion,
1998
+ getEmailSuggestion,
1999
+ // Individual validators
2000
+ validateSyntax,
2001
+ isValidSyntax,
2002
+ verifyMx,
2003
+ hasMxRecords,
2004
+ getMxRecords,
2005
+ checkMisc,
2006
+ checkDisposable,
2007
+ checkRoleAccount,
2008
+ checkFreeProvider
2009
+ };
2010
+ export {
2011
+ DnsCache,
2012
+ SmtpErrorType,
2013
+ checkDisposable,
2014
+ checkFreeProvider,
2015
+ checkMisc,
2016
+ checkRoleAccount,
2017
+ clearDnsCache,
2018
+ index_default as default,
2019
+ disposableDomains,
2020
+ extractParts,
2021
+ freeProviders,
2022
+ getDnsCacheSize,
2023
+ getEmailSuggestion,
2024
+ getMxRecords,
2025
+ getSuggestion,
2026
+ globalDnsCache,
2027
+ hasMxRecords,
2028
+ isDisposableDomain,
2029
+ isFreeProvider,
2030
+ isRolePrefix,
2031
+ isValidSyntax,
2032
+ normalizeEmail,
2033
+ rolePrefixes,
2034
+ validate,
2035
+ validateBulk,
2036
+ validateQuick,
2037
+ validateSyntax,
2038
+ verifyMx,
2039
+ verifySmtpWithFallback as verifySmtp
2040
+ };
2041
+ //# sourceMappingURL=index.mjs.map