@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.
- package/README.md +176 -24
- package/dist/adapters/lru-adapter.d.ts +2 -2
- package/dist/adapters/redis-adapter.d.ts +4 -4
- package/dist/batch-verifier.d.ts +5 -0
- package/dist/cache-interface.d.ts +23 -16
- package/dist/cache.d.ts +5 -5
- package/dist/check-if-email-exists.d.ts +205 -0
- package/dist/domain-suggester.d.ts +6 -6
- package/dist/{validator.d.ts → email-validator.d.ts} +2 -2
- package/dist/email-verifier-types.d.ts +225 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.esm.js +471 -236
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +474 -238
- package/dist/index.js.map +1 -1
- package/dist/mx-resolver.d.ts +2 -0
- package/dist/name-detector.d.ts +6 -6
- package/dist/serverless/adapters/aws-lambda.cjs.js.map +1 -1
- package/dist/serverless/adapters/aws-lambda.esm.js.map +1 -1
- package/dist/serverless/adapters/cloudflare.cjs.js.map +1 -1
- package/dist/serverless/adapters/cloudflare.esm.js.map +1 -1
- package/dist/serverless/adapters/vercel.cjs.js.map +1 -1
- package/dist/serverless/adapters/vercel.esm.js.map +1 -1
- package/dist/serverless/index.cjs.js.map +1 -1
- package/dist/serverless/index.d.ts +1 -1
- package/dist/serverless/index.esm.js.map +1 -1
- package/dist/serverless/{core.cjs.js → verifier.cjs.js} +1 -1
- package/dist/serverless/verifier.cjs.js.map +1 -0
- package/dist/serverless/{core.esm.js → verifier.esm.js} +1 -1
- package/dist/serverless/verifier.esm.js.map +1 -0
- package/dist/{smtp.d.ts → smtp-verifier.d.ts} +2 -2
- package/dist/types.d.ts +138 -34
- package/dist/whois.d.ts +3 -3
- package/package.json +19 -19
- package/dist/batch.d.ts +0 -5
- package/dist/dns.d.ts +0 -2
- package/dist/serverless/core.cjs.js.map +0 -1
- package/dist/serverless/core.esm.js.map +0 -1
- /package/dist/serverless/{core.d.ts → verifier.d.ts} +0 -0
- /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
|
-
|
|
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:
|
|
87
|
-
disposable:
|
|
88
|
-
free:
|
|
89
|
-
domainValid:
|
|
90
|
-
smtp:
|
|
91
|
-
smtpPort:
|
|
92
|
-
domainSuggestion:
|
|
93
|
-
whois:
|
|
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
|
-
|
|
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 ||
|
|
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 ||
|
|
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 ||
|
|
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
|
-
|
|
410
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
805
|
+
return commonFirstName.has(str.toLowerCase());
|
|
756
806
|
}
|
|
757
807
|
function isKnownLastName(str) {
|
|
758
|
-
return
|
|
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 (
|
|
815
|
+
if (commonFirstName.has(lower))
|
|
766
816
|
return 1;
|
|
767
|
-
if (
|
|
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 (
|
|
825
|
+
if (commonLastName.has(lower))
|
|
776
826
|
return 1;
|
|
777
|
-
if (
|
|
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 (
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 ?
|
|
1142
|
-
const cleanedLastName = detectedName.lastName ?
|
|
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["
|
|
1160
|
-
VerificationErrorCode2["
|
|
1161
|
-
VerificationErrorCode2["
|
|
1162
|
-
VerificationErrorCode2["
|
|
1163
|
-
VerificationErrorCode2["
|
|
1164
|
-
VerificationErrorCode2["
|
|
1165
|
-
VerificationErrorCode2["
|
|
1166
|
-
VerificationErrorCode2["
|
|
1167
|
-
VerificationErrorCode2["
|
|
1168
|
-
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";
|
|
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["
|
|
1173
|
-
SMTPStep2["
|
|
1174
|
-
SMTPStep2["
|
|
1175
|
-
SMTPStep2["
|
|
1176
|
-
SMTPStep2["
|
|
1177
|
-
SMTPStep2["
|
|
1178
|
-
SMTPStep2["
|
|
1179
|
-
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";
|
|
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 {
|
|
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
|
-
|
|
1426
|
+
smtpResult: cachedResult,
|
|
1228
1427
|
cached: true,
|
|
1229
1428
|
port: 0,
|
|
1230
1429
|
portCached: false
|
|
1231
1430
|
};
|
|
1232
1431
|
}
|
|
1233
|
-
} catch (
|
|
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 (
|
|
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
|
-
|
|
1458
|
+
const smtpResult = createSmtpResult(result);
|
|
1459
|
+
if (smtpCacheStore) {
|
|
1260
1460
|
try {
|
|
1261
|
-
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`,
|
|
1461
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
|
|
1262
1462
|
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1263
|
-
} catch (
|
|
1463
|
+
} catch (ignoredError) {
|
|
1264
1464
|
}
|
|
1265
1465
|
}
|
|
1266
|
-
return {
|
|
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(
|
|
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
|
-
|
|
1489
|
+
const smtpResult = createSmtpResult(result);
|
|
1490
|
+
if (smtpCacheStore) {
|
|
1290
1491
|
try {
|
|
1291
|
-
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`,
|
|
1492
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
|
|
1292
1493
|
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1293
|
-
} catch (
|
|
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 (
|
|
1502
|
+
} catch (ignoredError) {
|
|
1302
1503
|
}
|
|
1303
1504
|
}
|
|
1304
|
-
return {
|
|
1505
|
+
return { smtpResult, cached: false, port, portCached: false };
|
|
1305
1506
|
}
|
|
1306
1507
|
}
|
|
1307
1508
|
}
|
|
1308
1509
|
log("All ports failed");
|
|
1309
|
-
return {
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
1581
|
+
case exports.SMTPStep.ehlo:
|
|
1376
1582
|
sendCommand(`EHLO ${hostname}`);
|
|
1377
1583
|
break;
|
|
1378
|
-
case exports.SMTPStep.
|
|
1584
|
+
case exports.SMTPStep.helo:
|
|
1379
1585
|
sendCommand(`HELO ${domain}`);
|
|
1380
1586
|
break;
|
|
1381
|
-
case exports.SMTPStep.
|
|
1587
|
+
case exports.SMTPStep.greeting:
|
|
1382
1588
|
break;
|
|
1383
|
-
case exports.SMTPStep.
|
|
1589
|
+
case exports.SMTPStep.startTls:
|
|
1384
1590
|
sendCommand("STARTTLS");
|
|
1385
1591
|
break;
|
|
1386
|
-
case exports.SMTPStep.
|
|
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.
|
|
1597
|
+
case exports.SMTPStep.rcptTo:
|
|
1392
1598
|
sendCommand(`RCPT TO:<${local}@${domain}>`);
|
|
1393
1599
|
break;
|
|
1394
|
-
case exports.SMTPStep.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
1656
|
+
case exports.SMTPStep.ehlo:
|
|
1451
1657
|
if (code.startsWith("250")) {
|
|
1452
|
-
const hasSTARTTLS = activeSequence.steps.includes(exports.SMTPStep.
|
|
1658
|
+
const hasSTARTTLS = activeSequence.steps.includes(exports.SMTPStep.startTls);
|
|
1453
1659
|
if (!isTLS && useTLS && supportsSTARTTLS && !implicitTLS && hasSTARTTLS) {
|
|
1454
|
-
currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.
|
|
1455
|
-
executeStep(exports.SMTPStep.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
1506
|
-
currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.
|
|
1507
|
-
executeStep(exports.SMTPStep.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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, () =>
|
|
1568
|
-
|
|
1569
|
-
|
|
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
|
|
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 =
|
|
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 (
|
|
2167
|
-
log(`[whois] failed to get WHOIS data for ${domain}: ${
|
|
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 (
|
|
2205
|
-
log(`[whois] error getting domain age for ${domain}: ${
|
|
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 (
|
|
2277
|
-
log(`[whois] error getting domain registration status for ${domain}: ${
|
|
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.
|
|
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 (
|
|
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
|
|
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,
|
|
2486
|
-
log(`[isDisposableEmail] Cached result for ${emailDomain}: ${
|
|
2487
|
-
} catch (
|
|
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}: ${
|
|
2491
|
-
return
|
|
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 (
|
|
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
|
|
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,
|
|
2519
|
-
log(`[isFreeEmail] Cached result for ${emailDomain}: ${
|
|
2520
|
-
} catch (
|
|
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}: ${
|
|
2524
|
-
return
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 (
|
|
2616
|
-
log("[verifyEmail] Failed to get domain age",
|
|
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 (
|
|
2628
|
-
log("[verifyEmail] Failed to get domain registration status",
|
|
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.
|
|
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.
|
|
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 {
|
|
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.
|
|
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.
|
|
2922
|
+
result.metadata.error = exports.VerificationErrorCode.mailboxNotFound;
|
|
2688
2923
|
} else if (result.validSmtp === null && result.metadata) {
|
|
2689
|
-
result.metadata.error = exports.VerificationErrorCode.
|
|
2924
|
+
result.metadata.error = exports.VerificationErrorCode.smtpConnectionFailed;
|
|
2690
2925
|
}
|
|
2691
2926
|
}
|
|
2692
|
-
} catch (
|
|
2693
|
-
log("[verifyEmail] Failed to resolve MX records",
|
|
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.
|
|
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.
|
|
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.
|
|
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;
|