@emailcheck/email-validator-js 2.14.2 → 3.0.1-beta.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.
Files changed (41) hide show
  1. package/README.md +279 -18
  2. package/dist/adapters/lru-adapter.d.ts +2 -2
  3. package/dist/adapters/redis-adapter.d.ts +4 -4
  4. package/dist/batch-verifier.d.ts +5 -0
  5. package/dist/cache-interface.d.ts +23 -15
  6. package/dist/cache.d.ts +7 -5
  7. package/dist/check-if-email-exists.d.ts +205 -0
  8. package/dist/domain-suggester.d.ts +6 -6
  9. package/dist/{validator.d.ts → email-validator.d.ts} +2 -2
  10. package/dist/email-verifier-types.d.ts +225 -0
  11. package/dist/index.d.ts +8 -8
  12. package/dist/index.esm.js +779 -266
  13. package/dist/index.esm.js.map +1 -1
  14. package/dist/index.js +783 -268
  15. package/dist/index.js.map +1 -1
  16. package/dist/mx-resolver.d.ts +2 -0
  17. package/dist/name-detector.d.ts +6 -6
  18. package/dist/serverless/adapters/aws-lambda.cjs.js.map +1 -1
  19. package/dist/serverless/adapters/aws-lambda.esm.js.map +1 -1
  20. package/dist/serverless/adapters/cloudflare.cjs.js.map +1 -1
  21. package/dist/serverless/adapters/cloudflare.esm.js.map +1 -1
  22. package/dist/serverless/adapters/vercel.cjs.js.map +1 -1
  23. package/dist/serverless/adapters/vercel.esm.js.map +1 -1
  24. package/dist/serverless/index.cjs.js.map +1 -1
  25. package/dist/serverless/index.d.ts +1 -1
  26. package/dist/serverless/index.esm.js.map +1 -1
  27. package/dist/serverless/{core.cjs.js → verifier.cjs.js} +1 -1
  28. package/dist/serverless/verifier.cjs.js.map +1 -0
  29. package/dist/serverless/{core.esm.js → verifier.esm.js} +1 -1
  30. package/dist/serverless/verifier.esm.js.map +1 -0
  31. package/dist/smtp-verifier.d.ts +7 -0
  32. package/dist/types.d.ts +170 -28
  33. package/dist/whois.d.ts +3 -3
  34. package/package.json +19 -19
  35. package/dist/batch.d.ts +0 -5
  36. package/dist/dns.d.ts +0 -2
  37. package/dist/serverless/core.cjs.js.map +0 -1
  38. package/dist/serverless/core.esm.js.map +0 -1
  39. package/dist/smtp.d.ts +0 -2
  40. /package/dist/serverless/{core.d.ts → verifier.d.ts} +0 -0
  41. /package/dist/serverless/{core.min.js → verifier.min.js} +0 -0
package/dist/index.js CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  var psl = require('psl');
4
4
  var tinyLru = require('tiny-lru');
5
- var node_dns = require('node:dns');
6
5
  var stringSimilarityJs = require('string-similarity-js');
6
+ var node_dns = require('node:dns');
7
7
  var net = require('node:net');
8
+ var tls = require('node:tls');
8
9
 
9
10
  function _interopNamespaceDefault(e) {
10
11
  var n = Object.create(null);
@@ -24,6 +25,7 @@ function _interopNamespaceDefault(e) {
24
25
  }
25
26
 
26
27
  var net__namespace = /*#__PURE__*/_interopNamespaceDefault(net);
28
+ var tls__namespace = /*#__PURE__*/_interopNamespaceDefault(tls);
27
29
 
28
30
  class LRUAdapter {
29
31
  constructor(maxSize = 1e3, ttlMs = 36e5) {
@@ -64,28 +66,23 @@ class LRUAdapter {
64
66
  const DEFAULT_CACHE_OPTIONS = {
65
67
  ttl: {
66
68
  mx: 36e5,
67
- // 1 hour
68
69
  disposable: 864e5,
69
- // 24 hours
70
70
  free: 864e5,
71
- // 24 hours
72
71
  domainValid: 864e5,
73
- // 24 hours
74
72
  smtp: 18e5,
75
- // 30 minutes
73
+ smtpPort: 864e5,
76
74
  domainSuggestion: 864e5,
77
- // 24 hours
78
75
  whois: 36e5
79
- // 1 hour
80
76
  },
81
77
  maxSize: {
82
- mx: 500,
83
- disposable: 1e3,
84
- free: 1e3,
85
- domainValid: 1e3,
86
- smtp: 500,
87
- domainSuggestion: 1e3,
88
- whois: 200
78
+ mx: 1e4,
79
+ disposable: 1e4,
80
+ free: 1e4,
81
+ domainValid: 1e4,
82
+ smtp: 1e4,
83
+ smtpPort: 1e4,
84
+ domainSuggestion: 1e4,
85
+ whois: 1e4
89
86
  }
90
87
  };
91
88
  let defaultCacheInstance = null;
@@ -97,6 +94,7 @@ function getDefaultCache() {
97
94
  free: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.free, DEFAULT_CACHE_OPTIONS.ttl.free),
98
95
  domainValid: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.domainValid, DEFAULT_CACHE_OPTIONS.ttl.domainValid),
99
96
  smtp: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.smtp, DEFAULT_CACHE_OPTIONS.ttl.smtp),
97
+ smtpPort: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.smtpPort, DEFAULT_CACHE_OPTIONS.ttl.smtpPort),
100
98
  domainSuggestion: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.domainSuggestion, DEFAULT_CACHE_OPTIONS.ttl.domainSuggestion),
101
99
  whois: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.whois, DEFAULT_CACHE_OPTIONS.ttl.whois)
102
100
  };
@@ -113,6 +111,7 @@ function clearDefaultCache() {
113
111
  defaultCacheInstance.free.clear();
114
112
  defaultCacheInstance.domainValid.clear();
115
113
  defaultCacheInstance.smtp.clear();
114
+ defaultCacheInstance.smtpPort.clear();
116
115
  defaultCacheInstance.domainSuggestion.clear();
117
116
  defaultCacheInstance.whois.clear();
118
117
  }
@@ -121,41 +120,7 @@ function resetDefaultCache() {
121
120
  defaultCacheInstance = null;
122
121
  }
123
122
 
124
- async function resolveMxRecords(params) {
125
- const { domain, cache, logger } = params;
126
- const log = logger || (() => {
127
- });
128
- const cacheStore = getCacheStore(cache, "mx");
129
- const cached = await cacheStore.get(domain);
130
- if (cached !== null && cached !== void 0) {
131
- log(`[resolveMxRecords] Cache hit for ${domain}: ${cached.length} MX records`);
132
- return cached;
133
- }
134
- log(`[resolveMxRecords] Performing DNS MX lookup for ${domain}`);
135
- try {
136
- const records = await node_dns.promises.resolveMx(domain);
137
- records.sort((a, b) => {
138
- if (a.priority < b.priority) {
139
- return -1;
140
- }
141
- if (a.priority > b.priority) {
142
- return 1;
143
- }
144
- return 0;
145
- });
146
- const exchanges = records.map((record) => record.exchange);
147
- log(`[resolveMxRecords] Found ${exchanges.length} MX records for ${domain}: [${exchanges.join(", ")}]`);
148
- await cacheStore.set(domain, exchanges);
149
- log(`[resolveMxRecords] Cached ${exchanges.length} MX records for ${domain}`);
150
- return exchanges;
151
- } catch (error) {
152
- log(`[resolveMxRecords] MX lookup failed for ${domain}, caching empty result`);
153
- await cacheStore.set(domain, []);
154
- throw error;
155
- }
156
- }
157
-
158
- const COMMON_EMAIL_DOMAINS = [
123
+ const commonEmailDomains = [
159
124
  // Popular free email providers
160
125
  "gmail.com",
161
126
  "yahoo.com",
@@ -240,7 +205,7 @@ function getSimilarityThreshold(domain) {
240
205
  return 0.75;
241
206
  }
242
207
  function defaultDomainSuggestionMethod(domain, commonDomains) {
243
- const domainsToCheck = commonDomains || COMMON_EMAIL_DOMAINS;
208
+ const domainsToCheck = commonDomains || commonEmailDomains;
244
209
  const lowerDomain = domain.toLowerCase();
245
210
  if (domainsToCheck.includes(lowerDomain)) {
246
211
  return null;
@@ -296,7 +261,7 @@ async function defaultDomainSuggestionMethodImpl(domain, commonDomains, cache) {
296
261
  if (!domain || domain.length < 3) {
297
262
  return null;
298
263
  }
299
- const domainsToCheck = commonDomains || COMMON_EMAIL_DOMAINS;
264
+ const domainsToCheck = commonDomains || commonEmailDomains;
300
265
  const lowerDomain = domain.toLowerCase();
301
266
  const cacheKey = `${lowerDomain}:${domainsToCheck.length}`;
302
267
  const cacheStore = getCacheStore(cache, "domainSuggestion");
@@ -392,15 +357,107 @@ async function suggestEmailDomain(email, commonDomains, cache) {
392
357
  return null;
393
358
  }
394
359
  function isCommonDomain(domain, commonDomains) {
395
- const domainsToCheck = commonDomains || COMMON_EMAIL_DOMAINS;
360
+ const domainsToCheck = commonDomains || commonEmailDomains;
396
361
  return domainsToCheck.includes(domain.toLowerCase());
397
362
  }
398
363
  function getDomainSimilarity(domain1, domain2) {
399
364
  return stringSimilarityJs.stringSimilarity(domain1.toLowerCase(), domain2.toLowerCase());
400
365
  }
401
366
 
402
- const NAME_SEPARATORS = [".", "_", "-"];
403
- const COMMON_NAME_SUFFIXES = [
367
+ async function isValidEmailDomain(emailOrDomain, cache) {
368
+ let [localPart, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
369
+ if (!emailDomain) {
370
+ emailDomain = localPart;
371
+ }
372
+ if (!emailDomain) {
373
+ return false;
374
+ }
375
+ const cacheStore = getCacheStore(cache, "domainValid");
376
+ const cached = await cacheStore.get(emailDomain);
377
+ if (cached !== null && cached !== void 0) {
378
+ return cached.isValid;
379
+ }
380
+ try {
381
+ const isValidResult = psl.isValid(emailDomain) || false;
382
+ const richResult = {
383
+ isValid: isValidResult,
384
+ hasMX: false,
385
+ // MX not checked in this function
386
+ checkedAt: Date.now()
387
+ };
388
+ await cacheStore.set(emailDomain, richResult);
389
+ return isValidResult;
390
+ } catch (validationError) {
391
+ const errorResult = {
392
+ isValid: false,
393
+ hasMX: false,
394
+ checkedAt: Date.now()
395
+ };
396
+ await cacheStore.set(emailDomain, errorResult);
397
+ return false;
398
+ }
399
+ }
400
+ function isValidEmail(emailAddress) {
401
+ if (!emailAddress || typeof emailAddress !== "string") {
402
+ return false;
403
+ }
404
+ const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
405
+ const emailLower = emailAddress.toLowerCase();
406
+ if (emailLower.indexOf(".+") !== -1)
407
+ return false;
408
+ if (emailLower.indexOf("..") !== -1)
409
+ return false;
410
+ if (emailLower.startsWith(".") || emailLower.endsWith("."))
411
+ return false;
412
+ const parts = emailAddress.split("@");
413
+ if (parts.length !== 2)
414
+ return false;
415
+ const [localPart, domain] = parts;
416
+ if (!localPart || !domain)
417
+ return false;
418
+ if (localPart.length > 64)
419
+ return false;
420
+ if (domain.length > 253)
421
+ return false;
422
+ return re.test(emailLower);
423
+ }
424
+
425
+ async function resolveMxRecords(params) {
426
+ const { domain, cache, logger } = params;
427
+ const log = logger || (() => {
428
+ });
429
+ const cacheStore = getCacheStore(cache, "mx");
430
+ const cached = await cacheStore.get(domain);
431
+ if (cached !== null && cached !== void 0) {
432
+ log(`[resolveMxRecords] Cache hit for ${domain}: ${cached === null || cached === void 0 ? void 0 : cached.length} MX records`);
433
+ return cached;
434
+ }
435
+ log(`[resolveMxRecords] Performing DNS MX lookup for ${domain}`);
436
+ try {
437
+ const records = await node_dns.promises.resolveMx(domain);
438
+ records === null || records === void 0 ? void 0 : records.sort((a, b) => {
439
+ if (a.priority < b.priority) {
440
+ return -1;
441
+ }
442
+ if (a.priority > b.priority) {
443
+ return 1;
444
+ }
445
+ return 0;
446
+ });
447
+ const exchanges = records === null || records === void 0 ? void 0 : records.map((record) => record.exchange);
448
+ log(`[resolveMxRecords] Found ${exchanges === null || exchanges === void 0 ? void 0 : exchanges.length} MX records for ${domain}: [${exchanges === null || exchanges === void 0 ? void 0 : exchanges.join(", ")}]`);
449
+ await cacheStore.set(domain, exchanges);
450
+ log(`[resolveMxRecords] Cached ${exchanges === null || exchanges === void 0 ? void 0 : exchanges.length} MX records for ${domain}`);
451
+ return exchanges;
452
+ } catch (error) {
453
+ log(`[resolveMxRecords] MX lookup failed for ${domain}, caching empty result`);
454
+ await cacheStore.set(domain, []);
455
+ throw error;
456
+ }
457
+ }
458
+
459
+ const nameSeparator = [".", "_", "-"];
460
+ const commonNameSuffixes = [
404
461
  "mail",
405
462
  "email",
406
463
  "contact",
@@ -415,7 +472,7 @@ const COMMON_NAME_SUFFIXES = [
415
472
  "notifications",
416
473
  "alerts"
417
474
  ];
418
- const CONTEXTUAL_SUFFIXES = [
475
+ const contextualSuffixes = [
419
476
  "dev",
420
477
  "company",
421
478
  "team",
@@ -447,7 +504,7 @@ const COMMON_TITLES = [
447
504
  "father",
448
505
  "sister"
449
506
  ];
450
- const COMMON_FIRST_NAMES = /* @__PURE__ */ new Set([
507
+ const commonFirstName = /* @__PURE__ */ new Set([
451
508
  // English names
452
509
  "james",
453
510
  "john",
@@ -625,7 +682,7 @@ const COMMON_FIRST_NAMES = /* @__PURE__ */ new Set([
625
682
  "sekou",
626
683
  "mariama"
627
684
  ]);
628
- const COMMON_LAST_NAMES = /* @__PURE__ */ new Set([
685
+ const commonLastName = /* @__PURE__ */ new Set([
629
686
  // English surnames
630
687
  "smith",
631
688
  "johnson",
@@ -745,19 +802,19 @@ function isYearLike(str) {
745
802
  return /^(19|20)\d{2}$/.test(str);
746
803
  }
747
804
  function isKnownFirstName(str) {
748
- return COMMON_FIRST_NAMES.has(str.toLowerCase());
805
+ return commonFirstName.has(str.toLowerCase());
749
806
  }
750
807
  function isKnownLastName(str) {
751
- return COMMON_LAST_NAMES.has(str.toLowerCase());
808
+ return commonLastName.has(str.toLowerCase());
752
809
  }
753
810
  function isTitle(str) {
754
811
  return COMMON_TITLES.includes(str.toLowerCase().replace(".", ""));
755
812
  }
756
813
  function getFirstNameScore(str) {
757
814
  const lower = str.toLowerCase();
758
- if (COMMON_FIRST_NAMES.has(lower))
815
+ if (commonFirstName.has(lower))
759
816
  return 1;
760
- if (COMMON_LAST_NAMES.has(lower))
817
+ if (commonLastName.has(lower))
761
818
  return 0.3;
762
819
  if (str.length >= 2 && str.length <= 15 && /^[a-zA-Z]+$/.test(str))
763
820
  return 0.5;
@@ -765,9 +822,9 @@ function getFirstNameScore(str) {
765
822
  }
766
823
  function getLastNameScore(str) {
767
824
  const lower = str.toLowerCase();
768
- if (COMMON_LAST_NAMES.has(lower))
825
+ if (commonLastName.has(lower))
769
826
  return 1;
770
- if (COMMON_FIRST_NAMES.has(lower))
827
+ if (commonFirstName.has(lower))
771
828
  return 0.3;
772
829
  if (str.length >= 2 && str.length <= 20 && /^[a-zA-Z]+$/.test(str))
773
830
  return 0.5;
@@ -876,7 +933,7 @@ function isLikelyName(str, allowNumbers = false, allowSingleLetter = false) {
876
933
  return false;
877
934
  if (str.length === 1 && allowSingleLetter && /^[a-zA-Z]$/.test(str))
878
935
  return true;
879
- if (COMMON_NAME_SUFFIXES.includes(str.toLowerCase()))
936
+ if (commonNameSuffixes.includes(str.toLowerCase()))
880
937
  return false;
881
938
  if (allowNumbers) {
882
939
  if (/^\d+$/.test(str))
@@ -938,7 +995,7 @@ function defaultNameDetectionMethod(email) {
938
995
  }
939
996
  }
940
997
  if (!firstName && !lastName) {
941
- for (const separator of NAME_SEPARATORS) {
998
+ for (const separator of nameSeparator) {
942
999
  if (cleanedLocal.includes(separator)) {
943
1000
  const parts = cleanedLocal.split(separator).filter((p) => p.length > 0);
944
1001
  if (parts.length === 2) {
@@ -1000,7 +1057,7 @@ function defaultNameDetectionMethod(email) {
1000
1057
  const firstParsed = parseCompositeNamePart(first);
1001
1058
  const middleParsed = parseCompositeNamePart(middle);
1002
1059
  const lastParsed = parseCompositeNamePart(last);
1003
- const isLastSuffix = COMMON_NAME_SUFFIXES.includes(last.toLowerCase()) || CONTEXTUAL_SUFFIXES.includes(last.toLowerCase()) || isYearLike(last);
1060
+ const isLastSuffix = commonNameSuffixes.includes(last.toLowerCase()) || contextualSuffixes.includes(last.toLowerCase()) || isYearLike(last);
1004
1061
  if (isLastSuffix) {
1005
1062
  if (isLikelyName(first, true, true) && isLikelyName(middle, true, true)) {
1006
1063
  const cleanedFirst = firstParsed.hasNumbers ? firstParsed.cleaned : first;
@@ -1033,7 +1090,7 @@ function defaultNameDetectionMethod(email) {
1033
1090
  } else if (parts.length > 3) {
1034
1091
  const firstPart = parts[0];
1035
1092
  const lastPartLower = parts[parts.length - 1].toLowerCase();
1036
- const isLastPartSuffix = COMMON_NAME_SUFFIXES.includes(lastPartLower) || CONTEXTUAL_SUFFIXES.includes(lastPartLower) || isYearLike(parts[parts.length - 1]);
1093
+ const isLastPartSuffix = commonNameSuffixes.includes(lastPartLower) || contextualSuffixes.includes(lastPartLower) || isYearLike(parts[parts.length - 1]);
1037
1094
  const effectiveLastIndex = isLastPartSuffix ? parts.length - 2 : parts.length - 1;
1038
1095
  const lastToUse = effectiveLastIndex >= 0 ? parts[effectiveLastIndex] : null;
1039
1096
  if (lastToUse && isLikelyName(firstPart, true, true) && isLikelyName(lastToUse, true, true)) {
@@ -1116,7 +1173,7 @@ function detectNameFromEmail(params) {
1116
1173
  }
1117
1174
  return defaultNameDetectionMethod(email);
1118
1175
  }
1119
- function cleanNameForAlgrothin(name) {
1176
+ function cleanNameForAlgorithm(name) {
1120
1177
  if (!name)
1121
1178
  return "";
1122
1179
  let cleaned = name.replace(/[._*]/g, "");
@@ -1126,13 +1183,13 @@ function cleanNameForAlgrothin(name) {
1126
1183
  }
1127
1184
  return cleaned;
1128
1185
  }
1129
- function detectNameForAlgrothin(email) {
1186
+ function detectNameForAlgorithm(email) {
1130
1187
  const detectedName = detectName(email);
1131
1188
  if (!detectedName) {
1132
1189
  return null;
1133
1190
  }
1134
- const cleanedFirstName = detectedName.firstName ? cleanNameForAlgrothin(detectedName.firstName) : void 0;
1135
- const cleanedLastName = detectedName.lastName ? cleanNameForAlgrothin(detectedName.lastName) : void 0;
1191
+ const cleanedFirstName = detectedName.firstName ? cleanNameForAlgorithm(detectedName.firstName) : void 0;
1192
+ const cleanedLastName = detectedName.lastName ? cleanNameForAlgorithm(detectedName.lastName) : void 0;
1136
1193
  if (!cleanedFirstName && !cleanedLastName) {
1137
1194
  return null;
1138
1195
  }
@@ -1147,6 +1204,156 @@ function detectName(email) {
1147
1204
  return detectNameFromEmail({ email });
1148
1205
  }
1149
1206
 
1207
+ exports.VerificationErrorCode = void 0;
1208
+ (function(VerificationErrorCode2) {
1209
+ VerificationErrorCode2["invalidFormat"] = "INVALID_FORMAT";
1210
+ VerificationErrorCode2["invalidDomain"] = "INVALID_DOMAIN";
1211
+ VerificationErrorCode2["noMxRecords"] = "NO_MX_RECORDS";
1212
+ VerificationErrorCode2["smtpConnectionFailed"] = "SMTP_CONNECTION_FAILED";
1213
+ VerificationErrorCode2["smtpTimeout"] = "SMTP_TIMEOUT";
1214
+ VerificationErrorCode2["mailboxNotFound"] = "MAILBOX_NOT_FOUND";
1215
+ VerificationErrorCode2["mailboxFull"] = "MAILBOX_FULL";
1216
+ VerificationErrorCode2["networkError"] = "NETWORK_ERROR";
1217
+ VerificationErrorCode2["disposableEmail"] = "DISPOSABLE_EMAIL";
1218
+ VerificationErrorCode2["freeEmailProvider"] = "FREE_EMAIL_PROVIDER";
1219
+ })(exports.VerificationErrorCode || (exports.VerificationErrorCode = {}));
1220
+ exports.EmailProvider = void 0;
1221
+ (function(EmailProvider2) {
1222
+ EmailProvider2["gmail"] = "gmail";
1223
+ EmailProvider2["hotmailB2b"] = "hotmail_b2b";
1224
+ EmailProvider2["hotmailB2c"] = "hotmail_b2c";
1225
+ EmailProvider2["proofpoint"] = "proofpoint";
1226
+ EmailProvider2["mimecast"] = "mimecast";
1227
+ EmailProvider2["yahoo"] = "yahoo";
1228
+ EmailProvider2["everythingElse"] = "everything_else";
1229
+ })(exports.EmailProvider || (exports.EmailProvider = {}));
1230
+ function parseSmtpError(errorMessage) {
1231
+ const lowerError = errorMessage.toLowerCase();
1232
+ const networkErrorPatterns = [
1233
+ "etimedout",
1234
+ "econnrefused",
1235
+ "enotfound",
1236
+ "econnreset",
1237
+ "socket hang up",
1238
+ "connection_timeout",
1239
+ "socket_timeout",
1240
+ "connection_error",
1241
+ "connection_closed"
1242
+ ];
1243
+ const isNetworkError = networkErrorPatterns.some((pattern) => lowerError.includes(pattern));
1244
+ if (isNetworkError) {
1245
+ return {
1246
+ isDisabled: false,
1247
+ hasFullInbox: false,
1248
+ isInvalid: true,
1249
+ isCatchAll: false
1250
+ };
1251
+ }
1252
+ const disabledPatterns = [
1253
+ "account disabled",
1254
+ "account is disabled",
1255
+ "user disabled",
1256
+ "user is disabled",
1257
+ "account locked",
1258
+ "account is locked",
1259
+ "user blocked",
1260
+ "user is blocked",
1261
+ "mailbox disabled",
1262
+ "delivery not authorized",
1263
+ "message rejected",
1264
+ "access denied",
1265
+ "permission denied",
1266
+ "recipient unknown",
1267
+ "recipient address rejected",
1268
+ "user unknown",
1269
+ "address unknown",
1270
+ "invalid recipient",
1271
+ "not a valid recipient",
1272
+ "recipient does not exist",
1273
+ "no such user",
1274
+ "user does not exist",
1275
+ "mailbox unavailable",
1276
+ "recipient unavailable",
1277
+ "address rejected",
1278
+ "550",
1279
+ "551",
1280
+ "553",
1281
+ "not_found",
1282
+ "ambiguous"
1283
+ ];
1284
+ const fullInboxPatterns = [
1285
+ "mailbox full",
1286
+ "inbox full",
1287
+ "quota exceeded",
1288
+ "over quota",
1289
+ "storage limit exceeded",
1290
+ "message too large",
1291
+ "insufficient storage",
1292
+ "mailbox over quota",
1293
+ "over the quota",
1294
+ "mailbox size limit exceeded",
1295
+ "account over quota",
1296
+ "storage space",
1297
+ "overquota",
1298
+ "452",
1299
+ "552",
1300
+ "over_quota"
1301
+ ];
1302
+ const catchAllPatterns = [
1303
+ "accept all mail",
1304
+ "catch-all",
1305
+ "catchall",
1306
+ "wildcard",
1307
+ "accepts any recipient",
1308
+ "recipient address accepted"
1309
+ ];
1310
+ const rateLimitPatterns = [
1311
+ "receiving mail at a rate that",
1312
+ "rate limit",
1313
+ "too many messages",
1314
+ "temporarily rejected",
1315
+ "try again later",
1316
+ "greylisted",
1317
+ "greylist",
1318
+ "deferring",
1319
+ "temporarily deferred",
1320
+ "421",
1321
+ "450",
1322
+ "451",
1323
+ "temporary_failure"
1324
+ ];
1325
+ const isDisabled = disabledPatterns.some((pattern) => lowerError.includes(pattern)) || lowerError.startsWith("550") || lowerError.startsWith("551") || lowerError.startsWith("553");
1326
+ const hasFullInbox = fullInboxPatterns.some((pattern) => lowerError.includes(pattern)) || lowerError.startsWith("452") || lowerError.startsWith("552");
1327
+ const isCatchAll = catchAllPatterns.some((pattern) => lowerError.includes(pattern));
1328
+ const isInvalid = !isDisabled && !hasFullInbox && !isCatchAll && !rateLimitPatterns.some((pattern) => lowerError.includes(pattern)) && !lowerError.startsWith("421") && !lowerError.startsWith("450") && !lowerError.startsWith("451");
1329
+ return {
1330
+ isDisabled,
1331
+ hasFullInbox,
1332
+ isInvalid,
1333
+ isCatchAll
1334
+ };
1335
+ }
1336
+ exports.SMTPStep = void 0;
1337
+ (function(SMTPStep2) {
1338
+ SMTPStep2["greeting"] = "GREETING";
1339
+ SMTPStep2["ehlo"] = "EHLO";
1340
+ SMTPStep2["helo"] = "HELO";
1341
+ SMTPStep2["startTls"] = "STARTTLS";
1342
+ SMTPStep2["mailFrom"] = "MAIL_FROM";
1343
+ SMTPStep2["rcptTo"] = "RCPT_TO";
1344
+ SMTPStep2["vrfy"] = "VRFY";
1345
+ SMTPStep2["quit"] = "QUIT";
1346
+ })(exports.SMTPStep || (exports.SMTPStep = {}));
1347
+
1348
+ function isIPAddress(host) {
1349
+ const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
1350
+ if (ipv4Regex.test(host)) {
1351
+ const octets = host.split(".");
1352
+ return octets.every((octet) => parseInt(octet, 10) <= 255);
1353
+ }
1354
+ const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,7}:$|^(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}$|^(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}$|^(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})$|^::1(?::(?::[0-9a-fA-F]{1,4}){1,7})|$|:(?:(?::[0-9a-fA-F]{1,4}){1,7}:)$/;
1355
+ return ipv6Regex.test(host);
1356
+ }
1150
1357
  function isOverQuota(smtpReply) {
1151
1358
  return Boolean(smtpReply && /(over quota)/gi.test(smtpReply));
1152
1359
  }
@@ -1156,182 +1363,469 @@ function isInvalidMailboxError(smtpReply) {
1156
1363
  function isMultilineGreet(smtpReply) {
1157
1364
  return Boolean(smtpReply && /^(250|220)-/.test(smtpReply));
1158
1365
  }
1366
+ const DEFAULT_PORTS = [25, 587, 465];
1367
+ const DEFAULT_TIMEOUT = 3e3;
1368
+ const DEFAULT_MAX_RETRIES = 1;
1369
+ const PORT_CONFIGS = {
1370
+ 25: { tls: false, starttls: true },
1371
+ 587: { tls: false, starttls: true },
1372
+ 465: { tls: true, starttls: false }
1373
+ };
1159
1374
  async function verifyMailboxSMTP(params) {
1160
- const { local, domain, mxRecords = [], timeout, debug, port = 25, retryAttempts = 1 } = params;
1161
- const log = debug ? console.debug : (..._args) => {
1375
+ const { local, domain, mxRecords = [], options = {} } = params;
1376
+ const { ports = DEFAULT_PORTS, timeout = DEFAULT_TIMEOUT, maxRetries = DEFAULT_MAX_RETRIES, tls: tlsConfig = true, hostname = "localhost", useVRFY = true, cache, debug = false, sequence } = options;
1377
+ const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
1378
+ };
1379
+ const createSmtpResult = (result, port, tlsUsed, mxHost2) => {
1380
+ const reason = result === true ? "valid" : result === null ? "ambiguous" : "not_found";
1381
+ const parsedError = parseSmtpError(reason);
1382
+ return {
1383
+ canConnectSmtp: result !== null,
1384
+ hasFullInbox: parsedError.hasFullInbox,
1385
+ isCatchAll: parsedError.isCatchAll,
1386
+ isDeliverable: result === true,
1387
+ isDisabled: result === false && parsedError.isDisabled,
1388
+ error: result === null ? reason : result === false ? reason : void 0,
1389
+ providerUsed: exports.EmailProvider.everythingElse,
1390
+ checkedAt: Date.now()
1391
+ };
1162
1392
  };
1393
+ const createFailureResult = (error) => ({
1394
+ canConnectSmtp: false,
1395
+ hasFullInbox: false,
1396
+ isCatchAll: false,
1397
+ isDeliverable: false,
1398
+ isDisabled: false,
1399
+ error,
1400
+ providerUsed: exports.EmailProvider.everythingElse,
1401
+ checkedAt: Date.now()
1402
+ });
1163
1403
  if (!mxRecords || mxRecords.length === 0) {
1164
- return false;
1404
+ log("No MX records found");
1405
+ return {
1406
+ smtpResult: createFailureResult("No MX records found"),
1407
+ cached: false,
1408
+ port: 0,
1409
+ portCached: false
1410
+ };
1165
1411
  }
1166
- const mxIndex = 0;
1167
- const mxRecord = mxRecords[mxIndex];
1168
- for (let attempt = 0; attempt < retryAttempts; attempt++) {
1169
- const result = await attemptVerification({
1170
- mxRecord,
1171
- local,
1172
- domain,
1173
- port,
1174
- timeout,
1175
- log,
1176
- attempt
1177
- });
1178
- if (result !== null) {
1179
- return result;
1412
+ const mxHost = mxRecords[0];
1413
+ log(`Verifying ${local}@${domain} via ${mxHost}`);
1414
+ const smtpCacheStore = cache ? getCacheStore(cache, "smtp") : null;
1415
+ if (smtpCacheStore) {
1416
+ let cachedResult;
1417
+ try {
1418
+ cachedResult = await smtpCacheStore.get(`${mxHost}:${local}@${domain}`);
1419
+ if (cachedResult !== void 0 && cachedResult !== null) {
1420
+ log(`Using cached SMTP result: ${cachedResult.isDeliverable}`);
1421
+ return {
1422
+ smtpResult: cachedResult,
1423
+ cached: true,
1424
+ port: 0,
1425
+ portCached: false
1426
+ };
1427
+ }
1428
+ } catch (ignoredError) {
1429
+ cachedResult = void 0;
1430
+ }
1431
+ }
1432
+ const smtpPortCacheStore = cache ? getCacheStore(cache, "smtpPort") : null;
1433
+ if (smtpPortCacheStore) {
1434
+ let cachedPort;
1435
+ try {
1436
+ cachedPort = await smtpPortCacheStore.get(mxHost);
1437
+ } catch (ignoredError) {
1438
+ cachedPort = null;
1180
1439
  }
1181
- if (attempt < retryAttempts - 1) {
1182
- await new Promise((resolve) => setTimeout(resolve, Math.min(1e3 * (attempt + 1), 3e3)));
1440
+ if (cachedPort) {
1441
+ log(`Using cached port: ${cachedPort}`);
1442
+ const result = await testSMTPConnection({
1443
+ mxHost,
1444
+ port: cachedPort,
1445
+ local,
1446
+ domain,
1447
+ timeout,
1448
+ tlsConfig,
1449
+ hostname,
1450
+ useVRFY,
1451
+ sequence,
1452
+ log
1453
+ });
1454
+ const smtpResult = createSmtpResult(result);
1455
+ if (smtpCacheStore) {
1456
+ try {
1457
+ await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
1458
+ log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
1459
+ } catch (ignoredError) {
1460
+ }
1461
+ }
1462
+ return { smtpResult, cached: false, port: cachedPort, portCached: true };
1183
1463
  }
1184
1464
  }
1185
- return null;
1465
+ for (const port of ports) {
1466
+ log(`Testing port ${port}`);
1467
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1468
+ if (attempt > 0) {
1469
+ const delay = Math.min(200 * 2 ** (attempt - 1), 800);
1470
+ log(`Retry ${attempt + 1}, waiting ${delay}ms`);
1471
+ await new Promise((resolve) => setTimeout(resolve, delay));
1472
+ }
1473
+ const result = await testSMTPConnection({
1474
+ mxHost,
1475
+ port,
1476
+ local,
1477
+ domain,
1478
+ timeout,
1479
+ tlsConfig,
1480
+ hostname,
1481
+ useVRFY,
1482
+ sequence,
1483
+ log
1484
+ });
1485
+ const smtpResult = createSmtpResult(result);
1486
+ if (smtpCacheStore) {
1487
+ try {
1488
+ await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
1489
+ log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
1490
+ } catch (ignoredError) {
1491
+ }
1492
+ }
1493
+ if (result !== null) {
1494
+ if (smtpPortCacheStore) {
1495
+ try {
1496
+ await smtpPortCacheStore.set(mxHost, port);
1497
+ log(`Cached port ${port} for ${mxHost}`);
1498
+ } catch (ignoredError) {
1499
+ }
1500
+ }
1501
+ return { smtpResult, cached: false, port, portCached: false };
1502
+ }
1503
+ }
1504
+ }
1505
+ log("All ports failed");
1506
+ return {
1507
+ smtpResult: createFailureResult("All SMTP connection attempts failed"),
1508
+ cached: false,
1509
+ port: 0,
1510
+ portCached: false
1511
+ };
1186
1512
  }
1187
- async function attemptVerification(params) {
1188
- const { mxRecord, local, domain, port, timeout, log, attempt } = params;
1513
+ async function testSMTPConnection(params) {
1514
+ const { mxHost, port, local, domain, timeout, tlsConfig, hostname, useVRFY, sequence, log } = params;
1515
+ const portConfig = PORT_CONFIGS[port] || { tls: false, starttls: false };
1516
+ const useTLS = tlsConfig !== false && (portConfig.tls || portConfig.starttls);
1517
+ const implicitTLS = portConfig.tls;
1518
+ const defaultSequence = {
1519
+ steps: [exports.SMTPStep.greeting, exports.SMTPStep.ehlo, exports.SMTPStep.mailFrom, exports.SMTPStep.rcptTo]
1520
+ };
1521
+ const activeSequence = sequence || defaultSequence;
1522
+ if (port === 25) {
1523
+ activeSequence.steps = activeSequence.steps.map((step) => step === exports.SMTPStep.ehlo ? exports.SMTPStep.helo : step);
1524
+ }
1525
+ const tlsOptions = {
1526
+ host: mxHost,
1527
+ servername: isIPAddress(mxHost) ? void 0 : mxHost,
1528
+ // Don't set servername for IP addresses
1529
+ rejectUnauthorized: false,
1530
+ minVersion: "TLSv1.2",
1531
+ ...typeof tlsConfig === "object" ? tlsConfig : {}
1532
+ };
1189
1533
  return new Promise((resolve) => {
1190
- log(`[verifyMailboxSMTP] connecting to ${mxRecord}:${port} (attempt ${attempt + 1})`);
1191
- const socket = net__namespace.connect({
1192
- host: mxRecord,
1193
- port
1194
- });
1195
- let resTimeout = null;
1534
+ let socket;
1535
+ let buffer = "";
1536
+ let isTLS = implicitTLS;
1537
+ let currentStepIndex = 0;
1196
1538
  let resolved = false;
1197
- let cleaned = false;
1198
- const cleanup = () => {
1199
- if (cleaned)
1200
- return;
1201
- cleaned = true;
1202
- if (resTimeout) {
1203
- clearTimeout(resTimeout);
1204
- resTimeout = null;
1205
- }
1206
- if (socket && !socket.destroyed) {
1207
- socket.removeAllListeners();
1208
- socket.destroy();
1209
- }
1210
- };
1211
- const ret = (result) => {
1539
+ let supportsSTARTTLS = false;
1540
+ let supportsVRFY = false;
1541
+ let cleanup = () => {
1212
1542
  if (resolved)
1213
1543
  return;
1214
1544
  resolved = true;
1215
- if (!(socket === null || socket === void 0 ? void 0 : socket.destroyed)) {
1216
- log("[verifyMailboxSMTP] closing socket");
1545
+ try {
1217
1546
  socket === null || socket === void 0 ? void 0 : socket.write("QUIT\r\n");
1218
- socket === null || socket === void 0 ? void 0 : socket.end();
1547
+ } catch {
1219
1548
  }
1549
+ setTimeout(() => socket === null || socket === void 0 ? void 0 : socket.destroy(), 100);
1550
+ };
1551
+ const finish = (result, reason) => {
1552
+ if (resolved)
1553
+ return;
1554
+ log(`${port}: ${reason || (result ? "valid" : "invalid")}`);
1220
1555
  cleanup();
1221
1556
  resolve(result);
1222
1557
  };
1223
- const messages = [`HELO ${domain}`, `MAIL FROM: <>`, `RCPT TO: <${local}@${domain}>`];
1224
- log("[verifyMailboxSMTP] writing messages", messages);
1225
- socket.on("data", (data) => {
1226
- const dataString = String(data);
1227
- log("[verifyMailboxSMTP] got data", dataString);
1228
- if (isInvalidMailboxError(dataString)) {
1229
- log("[verifyMailboxSMTP] invalid mailbox error detected");
1230
- return ret(false);
1558
+ const sendCommand = (cmd) => {
1559
+ if (resolved)
1560
+ return;
1561
+ log(`\u2192 ${cmd}`);
1562
+ socket === null || socket === void 0 ? void 0 : socket.write(`${cmd}\r
1563
+ `);
1564
+ };
1565
+ const nextStep = () => {
1566
+ currentStepIndex++;
1567
+ if (currentStepIndex >= activeSequence.steps.length) {
1568
+ finish(true, "sequence_complete");
1569
+ return;
1231
1570
  }
1232
- if (isOverQuota(dataString)) {
1233
- log("[verifyMailboxSMTP] mailbox over quota");
1234
- return ret(false);
1571
+ executeStep(activeSequence.steps[currentStepIndex]);
1572
+ };
1573
+ const executeStep = (step) => {
1574
+ if (resolved)
1575
+ return;
1576
+ switch (step) {
1577
+ case exports.SMTPStep.ehlo:
1578
+ sendCommand(`EHLO ${hostname}`);
1579
+ break;
1580
+ case exports.SMTPStep.helo:
1581
+ sendCommand(`HELO ${domain}`);
1582
+ break;
1583
+ case exports.SMTPStep.greeting:
1584
+ break;
1585
+ case exports.SMTPStep.startTls:
1586
+ sendCommand("STARTTLS");
1587
+ break;
1588
+ case exports.SMTPStep.mailFrom: {
1589
+ const from = activeSequence.from || "<>";
1590
+ sendCommand(`MAIL FROM:${from}`);
1591
+ break;
1592
+ }
1593
+ case exports.SMTPStep.rcptTo:
1594
+ sendCommand(`RCPT TO:<${local}@${domain}>`);
1595
+ break;
1596
+ case exports.SMTPStep.vrfy: {
1597
+ const vrfyTarget = activeSequence.vrfyTarget || local;
1598
+ sendCommand(`VRFY ${vrfyTarget}`);
1599
+ break;
1600
+ }
1601
+ case exports.SMTPStep.quit:
1602
+ sendCommand("QUIT");
1603
+ break;
1235
1604
  }
1236
- if (!dataString.includes("220") && !dataString.includes("250")) {
1237
- log("[verifyMailboxSMTP] unrecognized response, returning null");
1238
- return ret(null);
1605
+ };
1606
+ const processResponse = (response) => {
1607
+ if (resolved)
1608
+ return;
1609
+ const code = response.substring(0, 3);
1610
+ const isMultiline = response.length > 3 && response[3] === "-";
1611
+ log(`\u2190 ${response}`);
1612
+ if (isMultilineGreet(response)) {
1613
+ return;
1239
1614
  }
1240
- if (isMultilineGreet(dataString))
1615
+ if (isOverQuota(response)) {
1616
+ finish(false, "over_quota");
1241
1617
  return;
1242
- if (messages.length > 0) {
1243
- const message = messages.shift();
1244
- log("[verifyMailboxSMTP] writing message", message);
1245
- return socket.write(`${message}\r
1246
- `);
1247
1618
  }
1248
- ret(true);
1249
- });
1250
- socket.on("error", (err) => {
1251
- log("[verifyMailboxSMTP] error in socket", err);
1252
- ret(null);
1253
- });
1254
- socket.on("close", (err) => {
1619
+ if (isInvalidMailboxError(response)) {
1620
+ finish(false, "not_found");
1621
+ return;
1622
+ }
1623
+ if (isMultiline) {
1624
+ const currentStep2 = activeSequence.steps[currentStepIndex];
1625
+ if (currentStep2 === exports.SMTPStep.ehlo && code === "250") {
1626
+ const upper = response.toUpperCase();
1627
+ if (upper.includes("STARTTLS"))
1628
+ supportsSTARTTLS = true;
1629
+ if (upper.includes("VRFY"))
1630
+ supportsVRFY = true;
1631
+ }
1632
+ if (currentStep2 === exports.SMTPStep.helo && code === "250") {
1633
+ const upper = response.toUpperCase();
1634
+ if (upper.includes("VRFY"))
1635
+ supportsVRFY = true;
1636
+ }
1637
+ return;
1638
+ }
1639
+ if (!response.includes("220") && !response.includes("250") && !response.includes("550") && !response.includes("552")) {
1640
+ finish(null, "unrecognized_response");
1641
+ return;
1642
+ }
1643
+ const currentStep = activeSequence.steps[currentStepIndex];
1644
+ switch (currentStep) {
1645
+ case exports.SMTPStep.greeting:
1646
+ if (code.startsWith("220")) {
1647
+ nextStep();
1648
+ } else {
1649
+ finish(null, "no_greeting");
1650
+ }
1651
+ break;
1652
+ case exports.SMTPStep.ehlo:
1653
+ if (code.startsWith("250")) {
1654
+ const hasSTARTTLS = activeSequence.steps.includes(exports.SMTPStep.startTls);
1655
+ if (!isTLS && useTLS && supportsSTARTTLS && !implicitTLS && hasSTARTTLS) {
1656
+ currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.startTls);
1657
+ executeStep(exports.SMTPStep.startTls);
1658
+ } else {
1659
+ nextStep();
1660
+ }
1661
+ } else {
1662
+ finish(null, "ehlo_failed");
1663
+ }
1664
+ break;
1665
+ case exports.SMTPStep.helo:
1666
+ if (code.startsWith("250")) {
1667
+ nextStep();
1668
+ } else {
1669
+ finish(null, "helo_failed");
1670
+ }
1671
+ break;
1672
+ case exports.SMTPStep.startTls:
1673
+ if (code.startsWith("220")) {
1674
+ const plainSocket = socket;
1675
+ socket = tls__namespace.connect({
1676
+ ...tlsOptions,
1677
+ socket: plainSocket,
1678
+ servername: isIPAddress(mxHost) ? void 0 : mxHost
1679
+ }, () => {
1680
+ isTLS = true;
1681
+ log("TLS upgraded");
1682
+ buffer = "";
1683
+ const starttlsIndex = activeSequence.steps.indexOf(exports.SMTPStep.startTls);
1684
+ currentStepIndex = starttlsIndex;
1685
+ nextStep();
1686
+ });
1687
+ socket.on("data", handleData);
1688
+ socket.on("error", () => finish(null, "tls_error"));
1689
+ } else {
1690
+ nextStep();
1691
+ }
1692
+ break;
1693
+ case exports.SMTPStep.mailFrom:
1694
+ if (code.startsWith("250")) {
1695
+ nextStep();
1696
+ } else {
1697
+ finish(null, "mail_from_rejected");
1698
+ }
1699
+ break;
1700
+ case exports.SMTPStep.rcptTo:
1701
+ if (code.startsWith("250") || code.startsWith("251")) {
1702
+ finish(true, "valid");
1703
+ } else if (code.startsWith("552") || code.startsWith("452")) {
1704
+ finish(false, "over_quota");
1705
+ } else if (code.startsWith("4")) {
1706
+ finish(null, "temporary_failure");
1707
+ } else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(exports.SMTPStep.vrfy)) {
1708
+ currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.vrfy);
1709
+ executeStep(exports.SMTPStep.vrfy);
1710
+ } else {
1711
+ finish(null, "ambiguous");
1712
+ }
1713
+ break;
1714
+ case exports.SMTPStep.vrfy:
1715
+ if (code.startsWith("250") || code.startsWith("252")) {
1716
+ finish(true, "vrfy_valid");
1717
+ } else if (code.startsWith("550")) {
1718
+ finish(false, "vrfy_invalid");
1719
+ } else {
1720
+ finish(null, "vrfy_failed");
1721
+ }
1722
+ break;
1723
+ case exports.SMTPStep.quit:
1724
+ if (code.startsWith("221")) {
1725
+ finish(null, "quit_received");
1726
+ }
1727
+ break;
1728
+ }
1729
+ };
1730
+ let handleData = (data) => {
1731
+ if (resolved)
1732
+ return;
1733
+ buffer += data.toString();
1734
+ let pos;
1735
+ while ((pos = buffer.indexOf("\r\n")) !== -1) {
1736
+ const line = buffer.substring(0, pos);
1737
+ buffer = buffer.substring(pos + 2);
1738
+ processResponse(line.trim());
1739
+ }
1740
+ };
1741
+ if (port < 0 || port > 65535 || !Number.isInteger(port)) {
1742
+ finish(null, "invalid_port");
1743
+ return;
1744
+ }
1745
+ if (implicitTLS) {
1746
+ const connectOptions = {
1747
+ ...tlsOptions,
1748
+ port,
1749
+ servername: isIPAddress(mxHost) ? void 0 : mxHost
1750
+ };
1751
+ socket = tls__namespace.connect(connectOptions, () => {
1752
+ log(`Connected to ${mxHost}:${port} with TLS`);
1753
+ socket.on("data", handleData);
1754
+ });
1755
+ } else {
1756
+ socket = net__namespace.connect({ host: mxHost, port }, () => {
1757
+ log(`Connected to ${mxHost}:${port}`);
1758
+ socket.on("data", handleData);
1759
+ });
1760
+ }
1761
+ if (activeSequence.steps.length === 0) {
1762
+ finish(true, "sequence_complete");
1763
+ return;
1764
+ }
1765
+ const firstStep = activeSequence.steps[0];
1766
+ let connectionTimeout;
1767
+ let stepTimeout;
1768
+ const resetActivityTimeout = () => {
1769
+ if (stepTimeout) {
1770
+ clearTimeout(stepTimeout);
1771
+ }
1772
+ stepTimeout = setTimeout(() => {
1773
+ if (!resolved) {
1774
+ log(`Step timeout after ${timeout}ms of inactivity`);
1775
+ finish(null, "step_timeout");
1776
+ }
1777
+ }, timeout);
1778
+ };
1779
+ connectionTimeout = setTimeout(() => {
1780
+ if (!resolved) {
1781
+ log(`Connection timeout after ${timeout}ms`);
1782
+ finish(null, "connection_timeout");
1783
+ }
1784
+ }, timeout);
1785
+ if (firstStep !== exports.SMTPStep.greeting) {
1786
+ executeStep(firstStep);
1787
+ }
1788
+ socket.setTimeout(timeout, () => {
1255
1789
  if (!resolved) {
1256
- log("[verifyMailboxSMTP] close socket", err);
1257
- ret(null);
1790
+ log(`Socket timeout after ${timeout}ms`);
1791
+ finish(null, "socket_timeout");
1258
1792
  }
1259
1793
  });
1260
- socket.on("timeout", () => {
1261
- log("[verifyMailboxSMTP] timeout socket");
1262
- ret(null);
1794
+ socket.on("error", (error) => {
1795
+ log(`Socket error: ${error.message}`);
1796
+ if (!resolved)
1797
+ finish(null, "connection_error");
1263
1798
  });
1264
- resTimeout = setTimeout(() => {
1265
- log(`[verifyMailboxSMTP] timed out (${timeout} ms)`);
1799
+ socket.on("close", () => {
1266
1800
  if (!resolved) {
1267
- socket.destroy();
1268
- ret(null);
1801
+ log("Socket closed unexpectedly");
1802
+ finish(null, "connection_closed");
1269
1803
  }
1270
- }, timeout);
1804
+ });
1805
+ const originalHandleData = handleData;
1806
+ handleData = (data) => {
1807
+ resetActivityTimeout();
1808
+ originalHandleData(data);
1809
+ };
1810
+ const enhancedCleanup = () => {
1811
+ if (resolved)
1812
+ return;
1813
+ resolved = true;
1814
+ if (connectionTimeout)
1815
+ clearTimeout(connectionTimeout);
1816
+ if (stepTimeout)
1817
+ clearTimeout(stepTimeout);
1818
+ socket.setTimeout(0);
1819
+ try {
1820
+ socket === null || socket === void 0 ? void 0 : socket.write("QUIT\r\n");
1821
+ } catch {
1822
+ }
1823
+ setTimeout(() => socket === null || socket === void 0 ? void 0 : socket.destroy(), 100);
1824
+ };
1825
+ cleanup = enhancedCleanup;
1271
1826
  });
1272
1827
  }
1273
1828
 
1274
- exports.VerificationErrorCode = void 0;
1275
- (function(VerificationErrorCode2) {
1276
- VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
1277
- VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
1278
- VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
1279
- VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
1280
- VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
1281
- VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
1282
- VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
1283
- VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
1284
- VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
1285
- VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
1286
- })(exports.VerificationErrorCode || (exports.VerificationErrorCode = {}));
1287
-
1288
- async function isValidEmailDomain(emailOrDomain, cache) {
1289
- let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
1290
- if (!emailDomain) {
1291
- emailDomain = _;
1292
- }
1293
- if (!emailDomain) {
1294
- return false;
1295
- }
1296
- const cacheStore = getCacheStore(cache, "domainValid");
1297
- const cached = await cacheStore.get(emailDomain);
1298
- if (cached !== null && cached !== void 0) {
1299
- return cached;
1300
- }
1301
- try {
1302
- const result = psl.isValid(emailDomain) || false;
1303
- await cacheStore.set(emailDomain, result);
1304
- return result;
1305
- } catch (_e) {
1306
- await cacheStore.set(emailDomain, false);
1307
- return false;
1308
- }
1309
- }
1310
- function isValidEmail(emailAddress) {
1311
- if (!emailAddress || typeof emailAddress !== "string") {
1312
- return false;
1313
- }
1314
- const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
1315
- const emailLower = emailAddress.toLowerCase();
1316
- if (emailLower.indexOf(".+") !== -1)
1317
- return false;
1318
- if (emailLower.indexOf("..") !== -1)
1319
- return false;
1320
- if (emailLower.startsWith(".") || emailLower.endsWith("."))
1321
- return false;
1322
- const parts = emailAddress.split("@");
1323
- if (parts.length !== 2)
1324
- return false;
1325
- const [localPart, domain] = parts;
1326
- if (!localPart || !domain)
1327
- return false;
1328
- if (localPart.length > 64)
1329
- return false;
1330
- if (domain.length > 253)
1331
- return false;
1332
- return re.test(emailLower);
1333
- }
1334
-
1335
1829
  const defaultRegex = {
1336
1830
  domainName: "Domain Name: *([^\\s]+)",
1337
1831
  registrar: "Registrar: *(.+)",
@@ -1749,7 +2243,7 @@ function parseWhoisData({ rawData, domain }) {
1749
2243
  return result;
1750
2244
  }
1751
2245
 
1752
- const WHOIS_SERVERS = {
2246
+ const whoisServers = {
1753
2247
  com: "whois.verisign-grs.com",
1754
2248
  net: "whois.verisign-grs.com",
1755
2249
  org: "whois.pir.org",
@@ -1849,7 +2343,7 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
1849
2343
  throw new Error("Invalid domain");
1850
2344
  }
1851
2345
  log(`[whois] extracted TLD: ${tld} for domain: ${domain}`);
1852
- const whoisServer = WHOIS_SERVERS[tld];
2346
+ const whoisServer = whoisServers[tld];
1853
2347
  if (!whoisServer) {
1854
2348
  log(`[whois] no specific server for TLD ${tld}, trying IANA`);
1855
2349
  const defaultServer = "whois.iana.org";
@@ -1875,8 +2369,8 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
1875
2369
  await cacheStore.set(cacheKey, whoisData);
1876
2370
  log(`[whois] successfully retrieved and cached WHOIS data for ${domain}`);
1877
2371
  return whoisData;
1878
- } catch (_error) {
1879
- log(`[whois] failed to get WHOIS data for ${domain}: ${_error instanceof Error ? _error.message : "Unknown error"}`);
2372
+ } catch (ignoredError) {
2373
+ log(`[whois] failed to get WHOIS data for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
1880
2374
  return null;
1881
2375
  }
1882
2376
  }
@@ -1913,8 +2407,8 @@ async function getDomainAge(domain, timeout = 5e3, debug = false, cache) {
1913
2407
  expirationDate: whoisData.expirationDate ? new Date(whoisData.expirationDate) : null,
1914
2408
  updatedDate: whoisData.updatedDate ? new Date(whoisData.updatedDate) : null
1915
2409
  };
1916
- } catch (_error) {
1917
- log(`[whois] error getting domain age for ${domain}: ${_error instanceof Error ? _error.message : "Unknown error"}`);
2410
+ } catch (ignoredError) {
2411
+ log(`[whois] error getting domain age for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
1918
2412
  return null;
1919
2413
  }
1920
2414
  }
@@ -1985,8 +2479,8 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3, debug = false,
1985
2479
  isPendingDelete,
1986
2480
  isLocked
1987
2481
  };
1988
- } catch (_error) {
1989
- log(`[whois] error getting domain registration status for ${domain}: ${_error instanceof Error ? _error.message : "Unknown error"}`);
2482
+ } catch (ignoredError) {
2483
+ log(`[whois] error getting domain registration status for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
1990
2484
  return null;
1991
2485
  }
1992
2486
  }
@@ -2162,7 +2656,7 @@ function createErrorResult(email, _error) {
2162
2656
  metadata: {
2163
2657
  verificationTime: 0,
2164
2658
  cached: false,
2165
- error: exports.VerificationErrorCode.SMTP_CONNECTION_FAILED
2659
+ error: exports.VerificationErrorCode.smtpConnectionFailed
2166
2660
  }
2167
2661
  };
2168
2662
  }
@@ -2182,25 +2676,31 @@ async function isDisposableEmail(params) {
2182
2676
  let cached;
2183
2677
  try {
2184
2678
  cached = await cacheStore.get(emailDomain);
2185
- } catch (_error) {
2679
+ } catch (ignoredError) {
2186
2680
  cached = null;
2187
2681
  }
2188
2682
  if (cached !== null && cached !== void 0) {
2189
- log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached}`);
2190
- return cached;
2683
+ log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached.isDisposable}`);
2684
+ return cached.isDisposable;
2191
2685
  }
2192
2686
  if (!disposableEmailProviders) {
2193
2687
  disposableEmailProviders = new Set(require("./disposable-email-providers.json"));
2194
2688
  }
2195
- const result = disposableEmailProviders.has(emailDomain);
2689
+ const isDisposable = disposableEmailProviders.has(emailDomain);
2690
+ const richResult = {
2691
+ isDisposable,
2692
+ source: "disposable-email-providers.json",
2693
+ category: isDisposable ? "disposable" : void 0,
2694
+ checkedAt: Date.now()
2695
+ };
2196
2696
  try {
2197
- await cacheStore.set(emailDomain, result);
2198
- log(`[isDisposableEmail] Cached result for ${emailDomain}: ${result}`);
2199
- } catch (_error) {
2697
+ await cacheStore.set(emailDomain, richResult);
2698
+ log(`[isDisposableEmail] Cached result for ${emailDomain}: ${isDisposable}`);
2699
+ } catch (ignoredError) {
2200
2700
  log(`[isDisposableEmail] Cache write error for ${emailDomain}`);
2201
2701
  }
2202
- log(`[isDisposableEmail] Check result for ${emailDomain}: ${result}`);
2203
- return result;
2702
+ log(`[isDisposableEmail] Check result for ${emailDomain}: ${isDisposable}`);
2703
+ return isDisposable;
2204
2704
  }
2205
2705
  async function isFreeEmail(params) {
2206
2706
  const { emailOrDomain, cache, logger } = params;
@@ -2215,25 +2715,30 @@ async function isFreeEmail(params) {
2215
2715
  let cached;
2216
2716
  try {
2217
2717
  cached = await cacheStore.get(emailDomain);
2218
- } catch (_error) {
2718
+ } catch (ignoredError) {
2219
2719
  cached = null;
2220
2720
  }
2221
2721
  if (cached !== null && cached !== void 0) {
2222
- log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached}`);
2223
- return cached;
2722
+ log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached.isFree}`);
2723
+ return cached.isFree;
2224
2724
  }
2225
2725
  if (!freeEmailProviders) {
2226
2726
  freeEmailProviders = new Set(require("./free-email-providers.json"));
2227
2727
  }
2228
- const result = freeEmailProviders.has(emailDomain);
2728
+ const isFree = freeEmailProviders.has(emailDomain);
2729
+ const richResult = {
2730
+ isFree,
2731
+ provider: isFree ? emailDomain : void 0,
2732
+ checkedAt: Date.now()
2733
+ };
2229
2734
  try {
2230
- await cacheStore.set(emailDomain, result);
2231
- log(`[isFreeEmail] Cached result for ${emailDomain}: ${result}`);
2232
- } catch (_error) {
2735
+ await cacheStore.set(emailDomain, richResult);
2736
+ log(`[isFreeEmail] Cached result for ${emailDomain}: ${isFree}`);
2737
+ } catch (ignoredError) {
2233
2738
  log(`[isFreeEmail] Cache write error for ${emailDomain}`);
2234
2739
  }
2235
- log(`[isFreeEmail] Check result for ${emailDomain}: ${result}`);
2236
- return result;
2740
+ log(`[isFreeEmail] Check result for ${emailDomain}: ${isFree}`);
2741
+ return isFree;
2237
2742
  }
2238
2743
  const domainPorts = {
2239
2744
  // 465 or 587
@@ -2241,7 +2746,7 @@ const domainPorts = {
2241
2746
  "ovh.net": 465
2242
2747
  };
2243
2748
  async function verifyEmail(params) {
2244
- var _a;
2749
+ var _a, _b;
2245
2750
  const { emailAddress, timeout = 4e3, verifyMx = true, verifySmtp = false, debug = false, checkDisposable = true, checkFree = true, detectName: detectName2 = false, nameDetectionMethod, suggestDomain: suggestDomain2 = true, domainSuggestionMethod, commonDomains, checkDomainAge = false, checkDomainRegistration = false, whoisTimeout = 5e3, skipMxForDisposable = false, skipDomainWhoisForDisposable = false } = params;
2246
2751
  const startTime = Date.now();
2247
2752
  const log = debug ? console.debug : (..._args) => {
@@ -2261,7 +2766,7 @@ async function verifyEmail(params) {
2261
2766
  if (!isValidEmail(emailAddress)) {
2262
2767
  if (result.metadata) {
2263
2768
  result.metadata.verificationTime = Date.now() - startTime;
2264
- result.metadata.error = exports.VerificationErrorCode.INVALID_FORMAT;
2769
+ result.metadata.error = exports.VerificationErrorCode.invalidFormat;
2265
2770
  }
2266
2771
  return result;
2267
2772
  }
@@ -2287,14 +2792,14 @@ async function verifyEmail(params) {
2287
2792
  if (!domain || !local) {
2288
2793
  if (result.metadata) {
2289
2794
  result.metadata.verificationTime = Date.now() - startTime;
2290
- result.metadata.error = exports.VerificationErrorCode.INVALID_FORMAT;
2795
+ result.metadata.error = exports.VerificationErrorCode.invalidFormat;
2291
2796
  }
2292
2797
  return result;
2293
2798
  }
2294
2799
  if (!await isValidEmailDomain(domain, params.cache)) {
2295
2800
  if (result.metadata) {
2296
2801
  result.metadata.verificationTime = Date.now() - startTime;
2297
- result.metadata.error = exports.VerificationErrorCode.INVALID_DOMAIN;
2802
+ result.metadata.error = exports.VerificationErrorCode.invalidDomain;
2298
2803
  }
2299
2804
  return result;
2300
2805
  }
@@ -2303,7 +2808,7 @@ async function verifyEmail(params) {
2303
2808
  result.isDisposable = await isDisposableEmail({ emailOrDomain: emailAddress, cache: params.cache, logger: log });
2304
2809
  log(`[verifyEmail] Disposable check result: ${result.isDisposable}`);
2305
2810
  if (result.isDisposable && result.metadata) {
2306
- result.metadata.error = exports.VerificationErrorCode.DISPOSABLE_EMAIL;
2811
+ result.metadata.error = exports.VerificationErrorCode.disposableEmail;
2307
2812
  }
2308
2813
  }
2309
2814
  if (checkFree) {
@@ -2324,8 +2829,8 @@ async function verifyEmail(params) {
2324
2829
  try {
2325
2830
  result.domainAge = await getDomainAge(domain, whoisTimeout, debug, params.cache);
2326
2831
  log(`[verifyEmail] Domain age result:`, result.domainAge ? `${result.domainAge.ageInDays} days` : "null");
2327
- } catch (err) {
2328
- log("[verifyEmail] Failed to get domain age", err);
2832
+ } catch (error) {
2833
+ log("[verifyEmail] Failed to get domain age", error);
2329
2834
  result.domainAge = null;
2330
2835
  }
2331
2836
  } else if (checkDomainAge && shouldSkipDomainWhois) {
@@ -2336,8 +2841,8 @@ async function verifyEmail(params) {
2336
2841
  try {
2337
2842
  result.domainRegistration = await getDomainRegistrationStatus(domain, whoisTimeout, debug, params.cache);
2338
2843
  log(`[verifyEmail] Domain registration result:`, ((_a = result.domainRegistration) === null || _a === void 0 ? void 0 : _a.isRegistered) ? "registered" : "not registered");
2339
- } catch (err) {
2340
- log("[verifyEmail] Failed to get domain registration status", err);
2844
+ } catch (error) {
2845
+ log("[verifyEmail] Failed to get domain registration status", error);
2341
2846
  result.domainRegistration = null;
2342
2847
  }
2343
2848
  } else if (checkDomainRegistration && shouldSkipDomainWhois) {
@@ -2350,14 +2855,14 @@ async function verifyEmail(params) {
2350
2855
  result.validMx = mxRecords.length > 0;
2351
2856
  log(`[verifyEmail] MX records found: ${mxRecords.length}, valid: ${result.validMx}`);
2352
2857
  if (!result.validMx && result.metadata) {
2353
- result.metadata.error = exports.VerificationErrorCode.NO_MX_RECORDS;
2858
+ result.metadata.error = exports.VerificationErrorCode.noMxRecords;
2354
2859
  }
2355
2860
  if (verifySmtp && mxRecords.length > 0) {
2356
2861
  const cacheKey = `${emailAddress}:smtp`;
2357
2862
  const smtpCacheInstance = getCacheStore(params.cache, "smtp");
2358
2863
  const cachedSmtp = await smtpCacheInstance.get(cacheKey);
2359
2864
  if (cachedSmtp !== null && cachedSmtp !== void 0) {
2360
- result.validSmtp = cachedSmtp;
2865
+ result.validSmtp = (_b = cachedSmtp.isDeliverable) !== null && _b !== void 0 ? _b : null;
2361
2866
  log(`[verifyEmail] SMTP result from cache: ${result.validSmtp} for ${emailAddress}`);
2362
2867
  if (result.metadata) {
2363
2868
  result.metadata.cached = true;
@@ -2377,30 +2882,39 @@ async function verifyEmail(params) {
2377
2882
  domainPort = domainPorts[mxDomain.domain];
2378
2883
  }
2379
2884
  }
2380
- const smtpResult = await verifyMailboxSMTP({
2885
+ const { smtpResult, cached, port } = await verifyMailboxSMTP({
2381
2886
  local,
2382
2887
  domain,
2383
2888
  mxRecords,
2384
- timeout,
2385
- debug,
2386
- port: domainPort,
2387
- retryAttempts: params.retryAttempts
2889
+ options: {
2890
+ cache: params.cache,
2891
+ ports: domainPort ? [domainPort] : void 0,
2892
+ timeout,
2893
+ debug,
2894
+ maxRetries: params.retryAttempts
2895
+ }
2388
2896
  });
2389
2897
  await smtpCacheInstance.set(cacheKey, smtpResult);
2390
- result.validSmtp = smtpResult;
2898
+ if (!smtpResult.canConnectSmtp) {
2899
+ result.validSmtp = null;
2900
+ } else {
2901
+ result.validSmtp = smtpResult.isDeliverable;
2902
+ }
2903
+ if (result.metadata)
2904
+ result.metadata.cached = cached;
2391
2905
  log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
2392
2906
  }
2393
2907
  if (result.validSmtp === false && result.metadata) {
2394
- result.metadata.error = exports.VerificationErrorCode.MAILBOX_NOT_FOUND;
2908
+ result.metadata.error = exports.VerificationErrorCode.mailboxNotFound;
2395
2909
  } else if (result.validSmtp === null && result.metadata) {
2396
- result.metadata.error = exports.VerificationErrorCode.SMTP_CONNECTION_FAILED;
2910
+ result.metadata.error = exports.VerificationErrorCode.smtpConnectionFailed;
2397
2911
  }
2398
2912
  }
2399
- } catch (err) {
2400
- log("[verifyEmail] Failed to resolve MX records", err);
2913
+ } catch (error) {
2914
+ log("[verifyEmail] Failed to resolve MX records", error);
2401
2915
  result.validMx = false;
2402
2916
  if (result.metadata) {
2403
- result.metadata.error = exports.VerificationErrorCode.NO_MX_RECORDS;
2917
+ result.metadata.error = exports.VerificationErrorCode.noMxRecords;
2404
2918
  }
2405
2919
  }
2406
2920
  } else if ((verifyMx || verifySmtp) && shouldSkipMx) {
@@ -2412,16 +2926,16 @@ async function verifyEmail(params) {
2412
2926
  return result;
2413
2927
  }
2414
2928
 
2415
- exports.COMMON_EMAIL_DOMAINS = COMMON_EMAIL_DOMAINS;
2416
2929
  exports.DEFAULT_CACHE_OPTIONS = DEFAULT_CACHE_OPTIONS;
2417
2930
  exports.LRUAdapter = LRUAdapter;
2418
2931
  exports.RedisAdapter = RedisAdapter;
2419
- exports.cleanNameForAlgrothin = cleanNameForAlgrothin;
2932
+ exports.cleanNameForAlgorithm = cleanNameForAlgorithm;
2420
2933
  exports.clearDefaultCache = clearDefaultCache;
2934
+ exports.commonEmailDomains = commonEmailDomains;
2421
2935
  exports.defaultDomainSuggestionMethod = defaultDomainSuggestionMethod;
2422
2936
  exports.defaultNameDetectionMethod = defaultNameDetectionMethod;
2423
2937
  exports.detectName = detectName;
2424
- exports.detectNameForAlgrothin = detectNameForAlgrothin;
2938
+ exports.detectNameForAlgorithm = detectNameForAlgorithm;
2425
2939
  exports.detectNameFromEmail = detectNameFromEmail;
2426
2940
  exports.domainPorts = domainPorts;
2427
2941
  exports.getCacheStore = getCacheStore;
@@ -2434,6 +2948,7 @@ exports.isDisposableEmail = isDisposableEmail;
2434
2948
  exports.isFreeEmail = isFreeEmail;
2435
2949
  exports.isValidEmail = isValidEmail;
2436
2950
  exports.isValidEmailDomain = isValidEmailDomain;
2951
+ exports.parseSmtpError = parseSmtpError;
2437
2952
  exports.resetDefaultCache = resetDefaultCache;
2438
2953
  exports.suggestDomain = suggestDomain;
2439
2954
  exports.suggestEmailDomain = suggestEmailDomain;