@emailcheck/email-validator-js 3.0.1-beta.0 → 3.0.1-beta.2

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 (40) hide show
  1. package/README.md +176 -24
  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 -16
  6. package/dist/cache.d.ts +5 -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 +471 -236
  13. package/dist/index.esm.js.map +1 -1
  14. package/dist/index.js +474 -238
  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.d.ts → smtp-verifier.d.ts} +2 -2
  32. package/dist/types.d.ts +138 -34
  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/serverless/{core.d.ts → verifier.d.ts} +0 -0
  40. /package/dist/serverless/{core.min.js → verifier.min.js} +0 -0
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@
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
8
  var tls = require('node:tls');
9
9
 
@@ -66,31 +66,23 @@ class LRUAdapter {
66
66
  const DEFAULT_CACHE_OPTIONS = {
67
67
  ttl: {
68
68
  mx: 36e5,
69
- // 1 hour
70
69
  disposable: 864e5,
71
- // 24 hours
72
70
  free: 864e5,
73
- // 24 hours
74
71
  domainValid: 864e5,
75
- // 24 hours
76
72
  smtp: 18e5,
77
- // 30 minutes
78
- smtpPort: 36e5,
79
- // 1 hour
73
+ smtpPort: 864e5,
80
74
  domainSuggestion: 864e5,
81
- // 24 hours
82
75
  whois: 36e5
83
- // 1 hour
84
76
  },
85
77
  maxSize: {
86
- mx: 500,
87
- disposable: 1e3,
88
- free: 1e3,
89
- domainValid: 1e3,
90
- smtp: 500,
91
- smtpPort: 500,
92
- domainSuggestion: 1e3,
93
- 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
94
86
  }
95
87
  };
96
88
  let defaultCacheInstance = null;
@@ -128,41 +120,7 @@ function resetDefaultCache() {
128
120
  defaultCacheInstance = null;
129
121
  }
130
122
 
131
- async function resolveMxRecords(params) {
132
- const { domain, cache, logger } = params;
133
- const log = logger || (() => {
134
- });
135
- const cacheStore = getCacheStore(cache, "mx");
136
- const cached = await cacheStore.get(domain);
137
- if (cached !== null && cached !== void 0) {
138
- log(`[resolveMxRecords] Cache hit for ${domain}: ${cached.length} MX records`);
139
- return cached;
140
- }
141
- log(`[resolveMxRecords] Performing DNS MX lookup for ${domain}`);
142
- try {
143
- const records = await node_dns.promises.resolveMx(domain);
144
- records.sort((a, b) => {
145
- if (a.priority < b.priority) {
146
- return -1;
147
- }
148
- if (a.priority > b.priority) {
149
- return 1;
150
- }
151
- return 0;
152
- });
153
- const exchanges = records.map((record) => record.exchange);
154
- log(`[resolveMxRecords] Found ${exchanges.length} MX records for ${domain}: [${exchanges.join(", ")}]`);
155
- await cacheStore.set(domain, exchanges);
156
- log(`[resolveMxRecords] Cached ${exchanges.length} MX records for ${domain}`);
157
- return exchanges;
158
- } catch (error) {
159
- log(`[resolveMxRecords] MX lookup failed for ${domain}, caching empty result`);
160
- await cacheStore.set(domain, []);
161
- throw error;
162
- }
163
- }
164
-
165
- const COMMON_EMAIL_DOMAINS = [
123
+ const commonEmailDomains = [
166
124
  // Popular free email providers
167
125
  "gmail.com",
168
126
  "yahoo.com",
@@ -247,7 +205,7 @@ function getSimilarityThreshold(domain) {
247
205
  return 0.75;
248
206
  }
249
207
  function defaultDomainSuggestionMethod(domain, commonDomains) {
250
- const domainsToCheck = commonDomains || COMMON_EMAIL_DOMAINS;
208
+ const domainsToCheck = commonDomains || commonEmailDomains;
251
209
  const lowerDomain = domain.toLowerCase();
252
210
  if (domainsToCheck.includes(lowerDomain)) {
253
211
  return null;
@@ -303,7 +261,7 @@ async function defaultDomainSuggestionMethodImpl(domain, commonDomains, cache) {
303
261
  if (!domain || domain.length < 3) {
304
262
  return null;
305
263
  }
306
- const domainsToCheck = commonDomains || COMMON_EMAIL_DOMAINS;
264
+ const domainsToCheck = commonDomains || commonEmailDomains;
307
265
  const lowerDomain = domain.toLowerCase();
308
266
  const cacheKey = `${lowerDomain}:${domainsToCheck.length}`;
309
267
  const cacheStore = getCacheStore(cache, "domainSuggestion");
@@ -399,15 +357,107 @@ async function suggestEmailDomain(email, commonDomains, cache) {
399
357
  return null;
400
358
  }
401
359
  function isCommonDomain(domain, commonDomains) {
402
- const domainsToCheck = commonDomains || COMMON_EMAIL_DOMAINS;
360
+ const domainsToCheck = commonDomains || commonEmailDomains;
403
361
  return domainsToCheck.includes(domain.toLowerCase());
404
362
  }
405
363
  function getDomainSimilarity(domain1, domain2) {
406
364
  return stringSimilarityJs.stringSimilarity(domain1.toLowerCase(), domain2.toLowerCase());
407
365
  }
408
366
 
409
- const NAME_SEPARATORS = [".", "_", "-"];
410
- 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 = [
411
461
  "mail",
412
462
  "email",
413
463
  "contact",
@@ -422,7 +472,7 @@ const COMMON_NAME_SUFFIXES = [
422
472
  "notifications",
423
473
  "alerts"
424
474
  ];
425
- const CONTEXTUAL_SUFFIXES = [
475
+ const contextualSuffixes = [
426
476
  "dev",
427
477
  "company",
428
478
  "team",
@@ -454,7 +504,7 @@ const COMMON_TITLES = [
454
504
  "father",
455
505
  "sister"
456
506
  ];
457
- const COMMON_FIRST_NAMES = /* @__PURE__ */ new Set([
507
+ const commonFirstName = /* @__PURE__ */ new Set([
458
508
  // English names
459
509
  "james",
460
510
  "john",
@@ -632,7 +682,7 @@ const COMMON_FIRST_NAMES = /* @__PURE__ */ new Set([
632
682
  "sekou",
633
683
  "mariama"
634
684
  ]);
635
- const COMMON_LAST_NAMES = /* @__PURE__ */ new Set([
685
+ const commonLastName = /* @__PURE__ */ new Set([
636
686
  // English surnames
637
687
  "smith",
638
688
  "johnson",
@@ -752,19 +802,19 @@ function isYearLike(str) {
752
802
  return /^(19|20)\d{2}$/.test(str);
753
803
  }
754
804
  function isKnownFirstName(str) {
755
- return COMMON_FIRST_NAMES.has(str.toLowerCase());
805
+ return commonFirstName.has(str.toLowerCase());
756
806
  }
757
807
  function isKnownLastName(str) {
758
- return COMMON_LAST_NAMES.has(str.toLowerCase());
808
+ return commonLastName.has(str.toLowerCase());
759
809
  }
760
810
  function isTitle(str) {
761
811
  return COMMON_TITLES.includes(str.toLowerCase().replace(".", ""));
762
812
  }
763
813
  function getFirstNameScore(str) {
764
814
  const lower = str.toLowerCase();
765
- if (COMMON_FIRST_NAMES.has(lower))
815
+ if (commonFirstName.has(lower))
766
816
  return 1;
767
- if (COMMON_LAST_NAMES.has(lower))
817
+ if (commonLastName.has(lower))
768
818
  return 0.3;
769
819
  if (str.length >= 2 && str.length <= 15 && /^[a-zA-Z]+$/.test(str))
770
820
  return 0.5;
@@ -772,9 +822,9 @@ function getFirstNameScore(str) {
772
822
  }
773
823
  function getLastNameScore(str) {
774
824
  const lower = str.toLowerCase();
775
- if (COMMON_LAST_NAMES.has(lower))
825
+ if (commonLastName.has(lower))
776
826
  return 1;
777
- if (COMMON_FIRST_NAMES.has(lower))
827
+ if (commonFirstName.has(lower))
778
828
  return 0.3;
779
829
  if (str.length >= 2 && str.length <= 20 && /^[a-zA-Z]+$/.test(str))
780
830
  return 0.5;
@@ -883,7 +933,7 @@ function isLikelyName(str, allowNumbers = false, allowSingleLetter = false) {
883
933
  return false;
884
934
  if (str.length === 1 && allowSingleLetter && /^[a-zA-Z]$/.test(str))
885
935
  return true;
886
- if (COMMON_NAME_SUFFIXES.includes(str.toLowerCase()))
936
+ if (commonNameSuffixes.includes(str.toLowerCase()))
887
937
  return false;
888
938
  if (allowNumbers) {
889
939
  if (/^\d+$/.test(str))
@@ -945,7 +995,7 @@ function defaultNameDetectionMethod(email) {
945
995
  }
946
996
  }
947
997
  if (!firstName && !lastName) {
948
- for (const separator of NAME_SEPARATORS) {
998
+ for (const separator of nameSeparator) {
949
999
  if (cleanedLocal.includes(separator)) {
950
1000
  const parts = cleanedLocal.split(separator).filter((p) => p.length > 0);
951
1001
  if (parts.length === 2) {
@@ -1007,7 +1057,7 @@ function defaultNameDetectionMethod(email) {
1007
1057
  const firstParsed = parseCompositeNamePart(first);
1008
1058
  const middleParsed = parseCompositeNamePart(middle);
1009
1059
  const lastParsed = parseCompositeNamePart(last);
1010
- 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);
1011
1061
  if (isLastSuffix) {
1012
1062
  if (isLikelyName(first, true, true) && isLikelyName(middle, true, true)) {
1013
1063
  const cleanedFirst = firstParsed.hasNumbers ? firstParsed.cleaned : first;
@@ -1040,7 +1090,7 @@ function defaultNameDetectionMethod(email) {
1040
1090
  } else if (parts.length > 3) {
1041
1091
  const firstPart = parts[0];
1042
1092
  const lastPartLower = parts[parts.length - 1].toLowerCase();
1043
- 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]);
1044
1094
  const effectiveLastIndex = isLastPartSuffix ? parts.length - 2 : parts.length - 1;
1045
1095
  const lastToUse = effectiveLastIndex >= 0 ? parts[effectiveLastIndex] : null;
1046
1096
  if (lastToUse && isLikelyName(firstPart, true, true) && isLikelyName(lastToUse, true, true)) {
@@ -1123,7 +1173,7 @@ function detectNameFromEmail(params) {
1123
1173
  }
1124
1174
  return defaultNameDetectionMethod(email);
1125
1175
  }
1126
- function cleanNameForAlgrothin(name) {
1176
+ function cleanNameForAlgorithm(name) {
1127
1177
  if (!name)
1128
1178
  return "";
1129
1179
  let cleaned = name.replace(/[._*]/g, "");
@@ -1133,13 +1183,13 @@ function cleanNameForAlgrothin(name) {
1133
1183
  }
1134
1184
  return cleaned;
1135
1185
  }
1136
- function detectNameForAlgrothin(email) {
1186
+ function detectNameForAlgorithm(email) {
1137
1187
  const detectedName = detectName(email);
1138
1188
  if (!detectedName) {
1139
1189
  return null;
1140
1190
  }
1141
- const cleanedFirstName = detectedName.firstName ? cleanNameForAlgrothin(detectedName.firstName) : void 0;
1142
- 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;
1143
1193
  if (!cleanedFirstName && !cleanedLastName) {
1144
1194
  return null;
1145
1195
  }
@@ -1156,27 +1206,143 @@ function detectName(email) {
1156
1206
 
1157
1207
  exports.VerificationErrorCode = void 0;
1158
1208
  (function(VerificationErrorCode2) {
1159
- VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
1160
- VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
1161
- VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
1162
- VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
1163
- VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
1164
- VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
1165
- VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
1166
- VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
1167
- VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
1168
- VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
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";
1169
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
+ }
1170
1336
  exports.SMTPStep = void 0;
1171
1337
  (function(SMTPStep2) {
1172
- SMTPStep2["GREETING"] = "GREETING";
1173
- SMTPStep2["EHLO"] = "EHLO";
1174
- SMTPStep2["HELO"] = "HELO";
1175
- SMTPStep2["STARTTLS"] = "STARTTLS";
1176
- SMTPStep2["MAIL_FROM"] = "MAIL_FROM";
1177
- SMTPStep2["RCPT_TO"] = "RCPT_TO";
1178
- SMTPStep2["VRFY"] = "VRFY";
1179
- SMTPStep2["QUIT"] = "QUIT";
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";
1180
1346
  })(exports.SMTPStep || (exports.SMTPStep = {}));
1181
1347
 
1182
1348
  function isIPAddress(host) {
@@ -1189,6 +1355,10 @@ function isIPAddress(host) {
1189
1355
  return ipv6Regex.test(host);
1190
1356
  }
1191
1357
  function isOverQuota(smtpReply) {
1358
+ var _a;
1359
+ if ((_a = smtpReply === null || smtpReply === void 0 ? void 0 : smtpReply.toLowerCase()) === null || _a === void 0 ? void 0 : _a.includes("excessively high number of invalid recipients")) {
1360
+ return true;
1361
+ }
1192
1362
  return Boolean(smtpReply && /(over quota)/gi.test(smtpReply));
1193
1363
  }
1194
1364
  function isInvalidMailboxError(smtpReply) {
@@ -1210,9 +1380,38 @@ async function verifyMailboxSMTP(params) {
1210
1380
  const { ports = DEFAULT_PORTS, timeout = DEFAULT_TIMEOUT, maxRetries = DEFAULT_MAX_RETRIES, tls: tlsConfig = true, hostname = "localhost", useVRFY = true, cache, debug = false, sequence } = options;
1211
1381
  const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
1212
1382
  };
1383
+ const createSmtpResult = (result, port, tlsUsed, mxHost2) => {
1384
+ const reason = result === true ? "valid" : result === null ? "ambiguous" : "not_found";
1385
+ const parsedError = parseSmtpError(reason);
1386
+ return {
1387
+ canConnectSmtp: result !== null,
1388
+ hasFullInbox: parsedError.hasFullInbox,
1389
+ isCatchAll: parsedError.isCatchAll,
1390
+ isDeliverable: result === true,
1391
+ isDisabled: result === false && parsedError.isDisabled,
1392
+ error: result === null ? reason : result === false ? reason : void 0,
1393
+ providerUsed: exports.EmailProvider.everythingElse,
1394
+ checkedAt: Date.now()
1395
+ };
1396
+ };
1397
+ const createFailureResult = (error) => ({
1398
+ canConnectSmtp: false,
1399
+ hasFullInbox: false,
1400
+ isCatchAll: false,
1401
+ isDeliverable: false,
1402
+ isDisabled: false,
1403
+ error,
1404
+ providerUsed: exports.EmailProvider.everythingElse,
1405
+ checkedAt: Date.now()
1406
+ });
1213
1407
  if (!mxRecords || mxRecords.length === 0) {
1214
1408
  log("No MX records found");
1215
- return { result: false, cached: false, port: 0, portCached: false };
1409
+ return {
1410
+ smtpResult: createFailureResult("No MX records found"),
1411
+ cached: false,
1412
+ port: 0,
1413
+ portCached: false
1414
+ };
1216
1415
  }
1217
1416
  const mxHost = mxRecords[0];
1218
1417
  log(`Verifying ${local}@${domain} via ${mxHost}`);
@@ -1221,16 +1420,16 @@ async function verifyMailboxSMTP(params) {
1221
1420
  let cachedResult;
1222
1421
  try {
1223
1422
  cachedResult = await smtpCacheStore.get(`${mxHost}:${local}@${domain}`);
1224
- if (cachedResult !== void 0) {
1225
- log(`Using cached SMTP result: ${cachedResult}`);
1423
+ if (cachedResult !== void 0 && cachedResult !== null) {
1424
+ log(`Using cached SMTP result: ${cachedResult.isDeliverable}`);
1226
1425
  return {
1227
- result: typeof cachedResult === "boolean" ? cachedResult : null,
1426
+ smtpResult: cachedResult,
1228
1427
  cached: true,
1229
1428
  port: 0,
1230
1429
  portCached: false
1231
1430
  };
1232
1431
  }
1233
- } catch (_error) {
1432
+ } catch (ignoredError) {
1234
1433
  cachedResult = void 0;
1235
1434
  }
1236
1435
  }
@@ -1239,7 +1438,7 @@ async function verifyMailboxSMTP(params) {
1239
1438
  let cachedPort;
1240
1439
  try {
1241
1440
  cachedPort = await smtpPortCacheStore.get(mxHost);
1242
- } catch (_error) {
1441
+ } catch (ignoredError) {
1243
1442
  cachedPort = null;
1244
1443
  }
1245
1444
  if (cachedPort) {
@@ -1256,21 +1455,22 @@ async function verifyMailboxSMTP(params) {
1256
1455
  sequence,
1257
1456
  log
1258
1457
  });
1259
- if (smtpCacheStore && result !== void 0) {
1458
+ const smtpResult = createSmtpResult(result);
1459
+ if (smtpCacheStore) {
1260
1460
  try {
1261
- await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, result);
1461
+ await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
1262
1462
  log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
1263
- } catch (_error) {
1463
+ } catch (ignoredError) {
1264
1464
  }
1265
1465
  }
1266
- return { result, cached: false, port: cachedPort, portCached: true };
1466
+ return { smtpResult, cached: false, port: cachedPort, portCached: true };
1267
1467
  }
1268
1468
  }
1269
1469
  for (const port of ports) {
1270
1470
  log(`Testing port ${port}`);
1271
1471
  for (let attempt = 0; attempt < maxRetries; attempt++) {
1272
1472
  if (attempt > 0) {
1273
- const delay = Math.min(1e3 * 2 ** (attempt - 1), 5e3);
1473
+ const delay = Math.min(200 * 2 ** (attempt - 1), 800);
1274
1474
  log(`Retry ${attempt + 1}, waiting ${delay}ms`);
1275
1475
  await new Promise((resolve) => setTimeout(resolve, delay));
1276
1476
  }
@@ -1286,11 +1486,12 @@ async function verifyMailboxSMTP(params) {
1286
1486
  sequence,
1287
1487
  log
1288
1488
  });
1289
- if (smtpCacheStore && result !== void 0) {
1489
+ const smtpResult = createSmtpResult(result);
1490
+ if (smtpCacheStore) {
1290
1491
  try {
1291
- await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, result);
1492
+ await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
1292
1493
  log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
1293
- } catch (_error) {
1494
+ } catch (ignoredError) {
1294
1495
  }
1295
1496
  }
1296
1497
  if (result !== null) {
@@ -1298,15 +1499,20 @@ async function verifyMailboxSMTP(params) {
1298
1499
  try {
1299
1500
  await smtpPortCacheStore.set(mxHost, port);
1300
1501
  log(`Cached port ${port} for ${mxHost}`);
1301
- } catch (_error) {
1502
+ } catch (ignoredError) {
1302
1503
  }
1303
1504
  }
1304
- return { result, cached: false, port, portCached: false };
1505
+ return { smtpResult, cached: false, port, portCached: false };
1305
1506
  }
1306
1507
  }
1307
1508
  }
1308
1509
  log("All ports failed");
1309
- return { result: null, cached: false, port: 0, portCached: false };
1510
+ return {
1511
+ smtpResult: createFailureResult("All SMTP connection attempts failed"),
1512
+ cached: false,
1513
+ port: 0,
1514
+ portCached: false
1515
+ };
1310
1516
  }
1311
1517
  async function testSMTPConnection(params) {
1312
1518
  const { mxHost, port, local, domain, timeout, tlsConfig, hostname, useVRFY, sequence, log } = params;
@@ -1314,11 +1520,11 @@ async function testSMTPConnection(params) {
1314
1520
  const useTLS = tlsConfig !== false && (portConfig.tls || portConfig.starttls);
1315
1521
  const implicitTLS = portConfig.tls;
1316
1522
  const defaultSequence = {
1317
- steps: [exports.SMTPStep.GREETING, exports.SMTPStep.EHLO, exports.SMTPStep.MAIL_FROM, exports.SMTPStep.RCPT_TO]
1523
+ steps: [exports.SMTPStep.greeting, exports.SMTPStep.ehlo, exports.SMTPStep.mailFrom, exports.SMTPStep.rcptTo]
1318
1524
  };
1319
1525
  const activeSequence = sequence || defaultSequence;
1320
1526
  if (port === 25) {
1321
- activeSequence.steps = activeSequence.steps.map((step) => step === exports.SMTPStep.EHLO ? exports.SMTPStep.HELO : step);
1527
+ activeSequence.steps = activeSequence.steps.map((step) => step === exports.SMTPStep.ehlo ? exports.SMTPStep.helo : step);
1322
1528
  }
1323
1529
  const tlsOptions = {
1324
1530
  host: mxHost,
@@ -1336,7 +1542,7 @@ async function testSMTPConnection(params) {
1336
1542
  let resolved = false;
1337
1543
  let supportsSTARTTLS = false;
1338
1544
  let supportsVRFY = false;
1339
- const cleanup = () => {
1545
+ let cleanup = () => {
1340
1546
  if (resolved)
1341
1547
  return;
1342
1548
  resolved = true;
@@ -1372,31 +1578,31 @@ async function testSMTPConnection(params) {
1372
1578
  if (resolved)
1373
1579
  return;
1374
1580
  switch (step) {
1375
- case exports.SMTPStep.EHLO:
1581
+ case exports.SMTPStep.ehlo:
1376
1582
  sendCommand(`EHLO ${hostname}`);
1377
1583
  break;
1378
- case exports.SMTPStep.HELO:
1584
+ case exports.SMTPStep.helo:
1379
1585
  sendCommand(`HELO ${domain}`);
1380
1586
  break;
1381
- case exports.SMTPStep.GREETING:
1587
+ case exports.SMTPStep.greeting:
1382
1588
  break;
1383
- case exports.SMTPStep.STARTTLS:
1589
+ case exports.SMTPStep.startTls:
1384
1590
  sendCommand("STARTTLS");
1385
1591
  break;
1386
- case exports.SMTPStep.MAIL_FROM: {
1592
+ case exports.SMTPStep.mailFrom: {
1387
1593
  const from = activeSequence.from || "<>";
1388
1594
  sendCommand(`MAIL FROM:${from}`);
1389
1595
  break;
1390
1596
  }
1391
- case exports.SMTPStep.RCPT_TO:
1597
+ case exports.SMTPStep.rcptTo:
1392
1598
  sendCommand(`RCPT TO:<${local}@${domain}>`);
1393
1599
  break;
1394
- case exports.SMTPStep.VRFY: {
1600
+ case exports.SMTPStep.vrfy: {
1395
1601
  const vrfyTarget = activeSequence.vrfyTarget || local;
1396
1602
  sendCommand(`VRFY ${vrfyTarget}`);
1397
1603
  break;
1398
1604
  }
1399
- case exports.SMTPStep.QUIT:
1605
+ case exports.SMTPStep.quit:
1400
1606
  sendCommand("QUIT");
1401
1607
  break;
1402
1608
  }
@@ -1420,14 +1626,14 @@ async function testSMTPConnection(params) {
1420
1626
  }
1421
1627
  if (isMultiline) {
1422
1628
  const currentStep2 = activeSequence.steps[currentStepIndex];
1423
- if (currentStep2 === exports.SMTPStep.EHLO && code === "250") {
1629
+ if (currentStep2 === exports.SMTPStep.ehlo && code === "250") {
1424
1630
  const upper = response.toUpperCase();
1425
1631
  if (upper.includes("STARTTLS"))
1426
1632
  supportsSTARTTLS = true;
1427
1633
  if (upper.includes("VRFY"))
1428
1634
  supportsVRFY = true;
1429
1635
  }
1430
- if (currentStep2 === exports.SMTPStep.HELO && code === "250") {
1636
+ if (currentStep2 === exports.SMTPStep.helo && code === "250") {
1431
1637
  const upper = response.toUpperCase();
1432
1638
  if (upper.includes("VRFY"))
1433
1639
  supportsVRFY = true;
@@ -1440,19 +1646,19 @@ async function testSMTPConnection(params) {
1440
1646
  }
1441
1647
  const currentStep = activeSequence.steps[currentStepIndex];
1442
1648
  switch (currentStep) {
1443
- case exports.SMTPStep.GREETING:
1649
+ case exports.SMTPStep.greeting:
1444
1650
  if (code.startsWith("220")) {
1445
1651
  nextStep();
1446
1652
  } else {
1447
1653
  finish(null, "no_greeting");
1448
1654
  }
1449
1655
  break;
1450
- case exports.SMTPStep.EHLO:
1656
+ case exports.SMTPStep.ehlo:
1451
1657
  if (code.startsWith("250")) {
1452
- const hasSTARTTLS = activeSequence.steps.includes(exports.SMTPStep.STARTTLS);
1658
+ const hasSTARTTLS = activeSequence.steps.includes(exports.SMTPStep.startTls);
1453
1659
  if (!isTLS && useTLS && supportsSTARTTLS && !implicitTLS && hasSTARTTLS) {
1454
- currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.STARTTLS);
1455
- executeStep(exports.SMTPStep.STARTTLS);
1660
+ currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.startTls);
1661
+ executeStep(exports.SMTPStep.startTls);
1456
1662
  } else {
1457
1663
  nextStep();
1458
1664
  }
@@ -1460,14 +1666,14 @@ async function testSMTPConnection(params) {
1460
1666
  finish(null, "ehlo_failed");
1461
1667
  }
1462
1668
  break;
1463
- case exports.SMTPStep.HELO:
1669
+ case exports.SMTPStep.helo:
1464
1670
  if (code.startsWith("250")) {
1465
1671
  nextStep();
1466
1672
  } else {
1467
1673
  finish(null, "helo_failed");
1468
1674
  }
1469
1675
  break;
1470
- case exports.SMTPStep.STARTTLS:
1676
+ case exports.SMTPStep.startTls:
1471
1677
  if (code.startsWith("220")) {
1472
1678
  const plainSocket = socket;
1473
1679
  socket = tls__namespace.connect({
@@ -1478,7 +1684,7 @@ async function testSMTPConnection(params) {
1478
1684
  isTLS = true;
1479
1685
  log("TLS upgraded");
1480
1686
  buffer = "";
1481
- const starttlsIndex = activeSequence.steps.indexOf(exports.SMTPStep.STARTTLS);
1687
+ const starttlsIndex = activeSequence.steps.indexOf(exports.SMTPStep.startTls);
1482
1688
  currentStepIndex = starttlsIndex;
1483
1689
  nextStep();
1484
1690
  });
@@ -1488,28 +1694,28 @@ async function testSMTPConnection(params) {
1488
1694
  nextStep();
1489
1695
  }
1490
1696
  break;
1491
- case exports.SMTPStep.MAIL_FROM:
1697
+ case exports.SMTPStep.mailFrom:
1492
1698
  if (code.startsWith("250")) {
1493
1699
  nextStep();
1494
1700
  } else {
1495
1701
  finish(null, "mail_from_rejected");
1496
1702
  }
1497
1703
  break;
1498
- case exports.SMTPStep.RCPT_TO:
1704
+ case exports.SMTPStep.rcptTo:
1499
1705
  if (code.startsWith("250") || code.startsWith("251")) {
1500
1706
  finish(true, "valid");
1501
1707
  } else if (code.startsWith("552") || code.startsWith("452")) {
1502
1708
  finish(false, "over_quota");
1503
1709
  } else if (code.startsWith("4")) {
1504
1710
  finish(null, "temporary_failure");
1505
- } else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(exports.SMTPStep.VRFY)) {
1506
- currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.VRFY);
1507
- executeStep(exports.SMTPStep.VRFY);
1711
+ } else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(exports.SMTPStep.vrfy)) {
1712
+ currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.vrfy);
1713
+ executeStep(exports.SMTPStep.vrfy);
1508
1714
  } else {
1509
1715
  finish(null, "ambiguous");
1510
1716
  }
1511
1717
  break;
1512
- case exports.SMTPStep.VRFY:
1718
+ case exports.SMTPStep.vrfy:
1513
1719
  if (code.startsWith("250") || code.startsWith("252")) {
1514
1720
  finish(true, "vrfy_valid");
1515
1721
  } else if (code.startsWith("550")) {
@@ -1518,14 +1724,14 @@ async function testSMTPConnection(params) {
1518
1724
  finish(null, "vrfy_failed");
1519
1725
  }
1520
1726
  break;
1521
- case exports.SMTPStep.QUIT:
1727
+ case exports.SMTPStep.quit:
1522
1728
  if (code.startsWith("221")) {
1523
1729
  finish(null, "quit_received");
1524
1730
  }
1525
1731
  break;
1526
1732
  }
1527
1733
  };
1528
- const handleData = (data) => {
1734
+ let handleData = (data) => {
1529
1735
  if (resolved)
1530
1736
  return;
1531
1737
  buffer += data.toString();
@@ -1561,65 +1767,69 @@ async function testSMTPConnection(params) {
1561
1767
  return;
1562
1768
  }
1563
1769
  const firstStep = activeSequence.steps[0];
1564
- if (firstStep !== exports.SMTPStep.GREETING) {
1770
+ let connectionTimeout;
1771
+ let stepTimeout;
1772
+ const resetActivityTimeout = () => {
1773
+ if (stepTimeout) {
1774
+ clearTimeout(stepTimeout);
1775
+ }
1776
+ stepTimeout = setTimeout(() => {
1777
+ if (!resolved) {
1778
+ log(`Step timeout after ${timeout}ms of inactivity`);
1779
+ finish(null, "step_timeout");
1780
+ }
1781
+ }, timeout);
1782
+ };
1783
+ connectionTimeout = setTimeout(() => {
1784
+ if (!resolved) {
1785
+ log(`Connection timeout after ${timeout}ms`);
1786
+ finish(null, "connection_timeout");
1787
+ }
1788
+ }, timeout);
1789
+ if (firstStep !== exports.SMTPStep.greeting) {
1565
1790
  executeStep(firstStep);
1566
1791
  }
1567
- socket.setTimeout(timeout, () => finish(null, "timeout"));
1568
- socket.on("error", () => finish(null, "connection_error"));
1569
- socket.on("close", () => {
1792
+ socket.setTimeout(timeout, () => {
1793
+ if (!resolved) {
1794
+ log(`Socket timeout after ${timeout}ms`);
1795
+ finish(null, "socket_timeout");
1796
+ }
1797
+ });
1798
+ socket.on("error", (error) => {
1799
+ log(`Socket error: ${error.message}`);
1570
1800
  if (!resolved)
1801
+ finish(null, "connection_error");
1802
+ });
1803
+ socket.on("close", () => {
1804
+ if (!resolved) {
1805
+ log("Socket closed unexpectedly");
1571
1806
  finish(null, "connection_closed");
1807
+ }
1572
1808
  });
1809
+ const originalHandleData = handleData;
1810
+ handleData = (data) => {
1811
+ resetActivityTimeout();
1812
+ originalHandleData(data);
1813
+ };
1814
+ const enhancedCleanup = () => {
1815
+ if (resolved)
1816
+ return;
1817
+ resolved = true;
1818
+ if (connectionTimeout)
1819
+ clearTimeout(connectionTimeout);
1820
+ if (stepTimeout)
1821
+ clearTimeout(stepTimeout);
1822
+ socket.setTimeout(0);
1823
+ try {
1824
+ socket === null || socket === void 0 ? void 0 : socket.write("QUIT\r\n");
1825
+ } catch {
1826
+ }
1827
+ setTimeout(() => socket === null || socket === void 0 ? void 0 : socket.destroy(), 100);
1828
+ };
1829
+ cleanup = enhancedCleanup;
1573
1830
  });
1574
1831
  }
1575
1832
 
1576
- async function isValidEmailDomain(emailOrDomain, cache) {
1577
- let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
1578
- if (!emailDomain) {
1579
- emailDomain = _;
1580
- }
1581
- if (!emailDomain) {
1582
- return false;
1583
- }
1584
- const cacheStore = getCacheStore(cache, "domainValid");
1585
- const cached = await cacheStore.get(emailDomain);
1586
- if (cached !== null && cached !== void 0) {
1587
- return cached;
1588
- }
1589
- try {
1590
- const result = psl.isValid(emailDomain) || false;
1591
- await cacheStore.set(emailDomain, result);
1592
- return result;
1593
- } catch (_e) {
1594
- await cacheStore.set(emailDomain, false);
1595
- return false;
1596
- }
1597
- }
1598
- function isValidEmail(emailAddress) {
1599
- if (!emailAddress || typeof emailAddress !== "string") {
1600
- return false;
1601
- }
1602
- 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,}))$/;
1603
- const emailLower = emailAddress.toLowerCase();
1604
- if (emailLower.indexOf(".+") !== -1)
1605
- return false;
1606
- if (emailLower.indexOf("..") !== -1)
1607
- return false;
1608
- if (emailLower.startsWith(".") || emailLower.endsWith("."))
1609
- return false;
1610
- const parts = emailAddress.split("@");
1611
- if (parts.length !== 2)
1612
- return false;
1613
- const [localPart, domain] = parts;
1614
- if (!localPart || !domain)
1615
- return false;
1616
- if (localPart.length > 64)
1617
- return false;
1618
- if (domain.length > 253)
1619
- return false;
1620
- return re.test(emailLower);
1621
- }
1622
-
1623
1833
  const defaultRegex = {
1624
1834
  domainName: "Domain Name: *([^\\s]+)",
1625
1835
  registrar: "Registrar: *(.+)",
@@ -2037,7 +2247,7 @@ function parseWhoisData({ rawData, domain }) {
2037
2247
  return result;
2038
2248
  }
2039
2249
 
2040
- const WHOIS_SERVERS = {
2250
+ const whoisServers = {
2041
2251
  com: "whois.verisign-grs.com",
2042
2252
  net: "whois.verisign-grs.com",
2043
2253
  org: "whois.pir.org",
@@ -2137,7 +2347,7 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
2137
2347
  throw new Error("Invalid domain");
2138
2348
  }
2139
2349
  log(`[whois] extracted TLD: ${tld} for domain: ${domain}`);
2140
- const whoisServer = WHOIS_SERVERS[tld];
2350
+ const whoisServer = whoisServers[tld];
2141
2351
  if (!whoisServer) {
2142
2352
  log(`[whois] no specific server for TLD ${tld}, trying IANA`);
2143
2353
  const defaultServer = "whois.iana.org";
@@ -2163,8 +2373,8 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
2163
2373
  await cacheStore.set(cacheKey, whoisData);
2164
2374
  log(`[whois] successfully retrieved and cached WHOIS data for ${domain}`);
2165
2375
  return whoisData;
2166
- } catch (_error) {
2167
- log(`[whois] failed to get WHOIS data for ${domain}: ${_error instanceof Error ? _error.message : "Unknown error"}`);
2376
+ } catch (ignoredError) {
2377
+ log(`[whois] failed to get WHOIS data for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
2168
2378
  return null;
2169
2379
  }
2170
2380
  }
@@ -2201,8 +2411,8 @@ async function getDomainAge(domain, timeout = 5e3, debug = false, cache) {
2201
2411
  expirationDate: whoisData.expirationDate ? new Date(whoisData.expirationDate) : null,
2202
2412
  updatedDate: whoisData.updatedDate ? new Date(whoisData.updatedDate) : null
2203
2413
  };
2204
- } catch (_error) {
2205
- log(`[whois] error getting domain age for ${domain}: ${_error instanceof Error ? _error.message : "Unknown error"}`);
2414
+ } catch (ignoredError) {
2415
+ log(`[whois] error getting domain age for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
2206
2416
  return null;
2207
2417
  }
2208
2418
  }
@@ -2273,8 +2483,8 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3, debug = false,
2273
2483
  isPendingDelete,
2274
2484
  isLocked
2275
2485
  };
2276
- } catch (_error) {
2277
- log(`[whois] error getting domain registration status for ${domain}: ${_error instanceof Error ? _error.message : "Unknown error"}`);
2486
+ } catch (ignoredError) {
2487
+ log(`[whois] error getting domain registration status for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
2278
2488
  return null;
2279
2489
  }
2280
2490
  }
@@ -2450,7 +2660,7 @@ function createErrorResult(email, _error) {
2450
2660
  metadata: {
2451
2661
  verificationTime: 0,
2452
2662
  cached: false,
2453
- error: exports.VerificationErrorCode.SMTP_CONNECTION_FAILED
2663
+ error: exports.VerificationErrorCode.smtpConnectionFailed
2454
2664
  }
2455
2665
  };
2456
2666
  }
@@ -2470,25 +2680,31 @@ async function isDisposableEmail(params) {
2470
2680
  let cached;
2471
2681
  try {
2472
2682
  cached = await cacheStore.get(emailDomain);
2473
- } catch (_error) {
2683
+ } catch (ignoredError) {
2474
2684
  cached = null;
2475
2685
  }
2476
2686
  if (cached !== null && cached !== void 0) {
2477
- log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached}`);
2478
- return cached;
2687
+ log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached.isDisposable}`);
2688
+ return cached.isDisposable;
2479
2689
  }
2480
2690
  if (!disposableEmailProviders) {
2481
2691
  disposableEmailProviders = new Set(require("./disposable-email-providers.json"));
2482
2692
  }
2483
- const result = disposableEmailProviders.has(emailDomain);
2693
+ const isDisposable = disposableEmailProviders.has(emailDomain);
2694
+ const richResult = {
2695
+ isDisposable,
2696
+ source: "disposable-email-providers.json",
2697
+ category: isDisposable ? "disposable" : void 0,
2698
+ checkedAt: Date.now()
2699
+ };
2484
2700
  try {
2485
- await cacheStore.set(emailDomain, result);
2486
- log(`[isDisposableEmail] Cached result for ${emailDomain}: ${result}`);
2487
- } catch (_error) {
2701
+ await cacheStore.set(emailDomain, richResult);
2702
+ log(`[isDisposableEmail] Cached result for ${emailDomain}: ${isDisposable}`);
2703
+ } catch (ignoredError) {
2488
2704
  log(`[isDisposableEmail] Cache write error for ${emailDomain}`);
2489
2705
  }
2490
- log(`[isDisposableEmail] Check result for ${emailDomain}: ${result}`);
2491
- return result;
2706
+ log(`[isDisposableEmail] Check result for ${emailDomain}: ${isDisposable}`);
2707
+ return isDisposable;
2492
2708
  }
2493
2709
  async function isFreeEmail(params) {
2494
2710
  const { emailOrDomain, cache, logger } = params;
@@ -2503,25 +2719,30 @@ async function isFreeEmail(params) {
2503
2719
  let cached;
2504
2720
  try {
2505
2721
  cached = await cacheStore.get(emailDomain);
2506
- } catch (_error) {
2722
+ } catch (ignoredError) {
2507
2723
  cached = null;
2508
2724
  }
2509
2725
  if (cached !== null && cached !== void 0) {
2510
- log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached}`);
2511
- return cached;
2726
+ log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached.isFree}`);
2727
+ return cached.isFree;
2512
2728
  }
2513
2729
  if (!freeEmailProviders) {
2514
2730
  freeEmailProviders = new Set(require("./free-email-providers.json"));
2515
2731
  }
2516
- const result = freeEmailProviders.has(emailDomain);
2732
+ const isFree = freeEmailProviders.has(emailDomain);
2733
+ const richResult = {
2734
+ isFree,
2735
+ provider: isFree ? emailDomain : void 0,
2736
+ checkedAt: Date.now()
2737
+ };
2517
2738
  try {
2518
- await cacheStore.set(emailDomain, result);
2519
- log(`[isFreeEmail] Cached result for ${emailDomain}: ${result}`);
2520
- } catch (_error) {
2739
+ await cacheStore.set(emailDomain, richResult);
2740
+ log(`[isFreeEmail] Cached result for ${emailDomain}: ${isFree}`);
2741
+ } catch (ignoredError) {
2521
2742
  log(`[isFreeEmail] Cache write error for ${emailDomain}`);
2522
2743
  }
2523
- log(`[isFreeEmail] Check result for ${emailDomain}: ${result}`);
2524
- return result;
2744
+ log(`[isFreeEmail] Check result for ${emailDomain}: ${isFree}`);
2745
+ return isFree;
2525
2746
  }
2526
2747
  const domainPorts = {
2527
2748
  // 465 or 587
@@ -2529,7 +2750,7 @@ const domainPorts = {
2529
2750
  "ovh.net": 465
2530
2751
  };
2531
2752
  async function verifyEmail(params) {
2532
- var _a;
2753
+ var _a, _b;
2533
2754
  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;
2534
2755
  const startTime = Date.now();
2535
2756
  const log = debug ? console.debug : (..._args) => {
@@ -2549,7 +2770,7 @@ async function verifyEmail(params) {
2549
2770
  if (!isValidEmail(emailAddress)) {
2550
2771
  if (result.metadata) {
2551
2772
  result.metadata.verificationTime = Date.now() - startTime;
2552
- result.metadata.error = exports.VerificationErrorCode.INVALID_FORMAT;
2773
+ result.metadata.error = exports.VerificationErrorCode.invalidFormat;
2553
2774
  }
2554
2775
  return result;
2555
2776
  }
@@ -2575,14 +2796,14 @@ async function verifyEmail(params) {
2575
2796
  if (!domain || !local) {
2576
2797
  if (result.metadata) {
2577
2798
  result.metadata.verificationTime = Date.now() - startTime;
2578
- result.metadata.error = exports.VerificationErrorCode.INVALID_FORMAT;
2799
+ result.metadata.error = exports.VerificationErrorCode.invalidFormat;
2579
2800
  }
2580
2801
  return result;
2581
2802
  }
2582
2803
  if (!await isValidEmailDomain(domain, params.cache)) {
2583
2804
  if (result.metadata) {
2584
2805
  result.metadata.verificationTime = Date.now() - startTime;
2585
- result.metadata.error = exports.VerificationErrorCode.INVALID_DOMAIN;
2806
+ result.metadata.error = exports.VerificationErrorCode.invalidDomain;
2586
2807
  }
2587
2808
  return result;
2588
2809
  }
@@ -2591,7 +2812,7 @@ async function verifyEmail(params) {
2591
2812
  result.isDisposable = await isDisposableEmail({ emailOrDomain: emailAddress, cache: params.cache, logger: log });
2592
2813
  log(`[verifyEmail] Disposable check result: ${result.isDisposable}`);
2593
2814
  if (result.isDisposable && result.metadata) {
2594
- result.metadata.error = exports.VerificationErrorCode.DISPOSABLE_EMAIL;
2815
+ result.metadata.error = exports.VerificationErrorCode.disposableEmail;
2595
2816
  }
2596
2817
  }
2597
2818
  if (checkFree) {
@@ -2612,8 +2833,8 @@ async function verifyEmail(params) {
2612
2833
  try {
2613
2834
  result.domainAge = await getDomainAge(domain, whoisTimeout, debug, params.cache);
2614
2835
  log(`[verifyEmail] Domain age result:`, result.domainAge ? `${result.domainAge.ageInDays} days` : "null");
2615
- } catch (err) {
2616
- log("[verifyEmail] Failed to get domain age", err);
2836
+ } catch (error) {
2837
+ log("[verifyEmail] Failed to get domain age", error);
2617
2838
  result.domainAge = null;
2618
2839
  }
2619
2840
  } else if (checkDomainAge && shouldSkipDomainWhois) {
@@ -2624,8 +2845,8 @@ async function verifyEmail(params) {
2624
2845
  try {
2625
2846
  result.domainRegistration = await getDomainRegistrationStatus(domain, whoisTimeout, debug, params.cache);
2626
2847
  log(`[verifyEmail] Domain registration result:`, ((_a = result.domainRegistration) === null || _a === void 0 ? void 0 : _a.isRegistered) ? "registered" : "not registered");
2627
- } catch (err) {
2628
- log("[verifyEmail] Failed to get domain registration status", err);
2848
+ } catch (error) {
2849
+ log("[verifyEmail] Failed to get domain registration status", error);
2629
2850
  result.domainRegistration = null;
2630
2851
  }
2631
2852
  } else if (checkDomainRegistration && shouldSkipDomainWhois) {
@@ -2638,14 +2859,19 @@ async function verifyEmail(params) {
2638
2859
  result.validMx = mxRecords.length > 0;
2639
2860
  log(`[verifyEmail] MX records found: ${mxRecords.length}, valid: ${result.validMx}`);
2640
2861
  if (!result.validMx && result.metadata) {
2641
- result.metadata.error = exports.VerificationErrorCode.NO_MX_RECORDS;
2862
+ result.metadata.error = exports.VerificationErrorCode.noMxRecords;
2642
2863
  }
2643
2864
  if (verifySmtp && mxRecords.length > 0) {
2644
2865
  const cacheKey = `${emailAddress}:smtp`;
2645
2866
  const smtpCacheInstance = getCacheStore(params.cache, "smtp");
2646
2867
  const cachedSmtp = await smtpCacheInstance.get(cacheKey);
2647
2868
  if (cachedSmtp !== null && cachedSmtp !== void 0) {
2648
- result.validSmtp = cachedSmtp;
2869
+ result.canConnectSmtp = cachedSmtp.canConnectSmtp;
2870
+ result.hasFullInbox = cachedSmtp.hasFullInbox;
2871
+ result.isCatchAll = cachedSmtp.isCatchAll;
2872
+ result.isDeliverable = cachedSmtp.isDeliverable;
2873
+ result.isDisabled = cachedSmtp.isDisabled;
2874
+ result.validSmtp = (_b = cachedSmtp.isDeliverable) !== null && _b !== void 0 ? _b : null;
2649
2875
  log(`[verifyEmail] SMTP result from cache: ${result.validSmtp} for ${emailAddress}`);
2650
2876
  if (result.metadata) {
2651
2877
  result.metadata.cached = true;
@@ -2665,7 +2891,7 @@ async function verifyEmail(params) {
2665
2891
  domainPort = domainPorts[mxDomain.domain];
2666
2892
  }
2667
2893
  }
2668
- const { result: smtpResult, cached, portCached, port } = await verifyMailboxSMTP({
2894
+ const { smtpResult, cached, port } = await verifyMailboxSMTP({
2669
2895
  local,
2670
2896
  domain,
2671
2897
  mxRecords,
@@ -2678,22 +2904,31 @@ async function verifyEmail(params) {
2678
2904
  }
2679
2905
  });
2680
2906
  await smtpCacheInstance.set(cacheKey, smtpResult);
2681
- result.validSmtp = smtpResult;
2907
+ result.canConnectSmtp = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.canConnectSmtp;
2908
+ result.hasFullInbox = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.hasFullInbox;
2909
+ result.isCatchAll = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.isCatchAll;
2910
+ result.isDeliverable = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.isDeliverable;
2911
+ result.isDisabled = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.isDisabled;
2912
+ if (!smtpResult.canConnectSmtp) {
2913
+ result.validSmtp = null;
2914
+ } else {
2915
+ result.validSmtp = smtpResult.isDeliverable;
2916
+ }
2682
2917
  if (result.metadata)
2683
2918
  result.metadata.cached = cached;
2684
2919
  log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
2685
2920
  }
2686
2921
  if (result.validSmtp === false && result.metadata) {
2687
- result.metadata.error = exports.VerificationErrorCode.MAILBOX_NOT_FOUND;
2922
+ result.metadata.error = exports.VerificationErrorCode.mailboxNotFound;
2688
2923
  } else if (result.validSmtp === null && result.metadata) {
2689
- result.metadata.error = exports.VerificationErrorCode.SMTP_CONNECTION_FAILED;
2924
+ result.metadata.error = exports.VerificationErrorCode.smtpConnectionFailed;
2690
2925
  }
2691
2926
  }
2692
- } catch (err) {
2693
- log("[verifyEmail] Failed to resolve MX records", err);
2927
+ } catch (error) {
2928
+ log("[verifyEmail] Failed to resolve MX records", error);
2694
2929
  result.validMx = false;
2695
2930
  if (result.metadata) {
2696
- result.metadata.error = exports.VerificationErrorCode.NO_MX_RECORDS;
2931
+ result.metadata.error = exports.VerificationErrorCode.noMxRecords;
2697
2932
  }
2698
2933
  }
2699
2934
  } else if ((verifyMx || verifySmtp) && shouldSkipMx) {
@@ -2705,16 +2940,16 @@ async function verifyEmail(params) {
2705
2940
  return result;
2706
2941
  }
2707
2942
 
2708
- exports.COMMON_EMAIL_DOMAINS = COMMON_EMAIL_DOMAINS;
2709
2943
  exports.DEFAULT_CACHE_OPTIONS = DEFAULT_CACHE_OPTIONS;
2710
2944
  exports.LRUAdapter = LRUAdapter;
2711
2945
  exports.RedisAdapter = RedisAdapter;
2712
- exports.cleanNameForAlgrothin = cleanNameForAlgrothin;
2946
+ exports.cleanNameForAlgorithm = cleanNameForAlgorithm;
2713
2947
  exports.clearDefaultCache = clearDefaultCache;
2948
+ exports.commonEmailDomains = commonEmailDomains;
2714
2949
  exports.defaultDomainSuggestionMethod = defaultDomainSuggestionMethod;
2715
2950
  exports.defaultNameDetectionMethod = defaultNameDetectionMethod;
2716
2951
  exports.detectName = detectName;
2717
- exports.detectNameForAlgrothin = detectNameForAlgrothin;
2952
+ exports.detectNameForAlgorithm = detectNameForAlgorithm;
2718
2953
  exports.detectNameFromEmail = detectNameFromEmail;
2719
2954
  exports.domainPorts = domainPorts;
2720
2955
  exports.getCacheStore = getCacheStore;
@@ -2727,6 +2962,7 @@ exports.isDisposableEmail = isDisposableEmail;
2727
2962
  exports.isFreeEmail = isFreeEmail;
2728
2963
  exports.isValidEmail = isValidEmail;
2729
2964
  exports.isValidEmailDomain = isValidEmailDomain;
2965
+ exports.parseSmtpError = parseSmtpError;
2730
2966
  exports.resetDefaultCache = resetDefaultCache;
2731
2967
  exports.suggestDomain = suggestDomain;
2732
2968
  exports.suggestEmailDomain = suggestEmailDomain;