@emailcheck/email-validator-js 3.0.1-beta.0 → 3.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +457 -236
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +460 -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 +128 -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.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isValid, parse } from 'psl';
|
|
2
2
|
import { lru } from 'tiny-lru';
|
|
3
|
-
import { promises } from 'node:dns';
|
|
4
3
|
import { stringSimilarity } from 'string-similarity-js';
|
|
4
|
+
import { promises } from 'node:dns';
|
|
5
5
|
import * as net from 'node:net';
|
|
6
6
|
import * as tls from 'node:tls';
|
|
7
7
|
|
|
@@ -44,31 +44,23 @@ class LRUAdapter {
|
|
|
44
44
|
const DEFAULT_CACHE_OPTIONS = {
|
|
45
45
|
ttl: {
|
|
46
46
|
mx: 36e5,
|
|
47
|
-
// 1 hour
|
|
48
47
|
disposable: 864e5,
|
|
49
|
-
// 24 hours
|
|
50
48
|
free: 864e5,
|
|
51
|
-
// 24 hours
|
|
52
49
|
domainValid: 864e5,
|
|
53
|
-
// 24 hours
|
|
54
50
|
smtp: 18e5,
|
|
55
|
-
|
|
56
|
-
smtpPort: 36e5,
|
|
57
|
-
// 1 hour
|
|
51
|
+
smtpPort: 864e5,
|
|
58
52
|
domainSuggestion: 864e5,
|
|
59
|
-
// 24 hours
|
|
60
53
|
whois: 36e5
|
|
61
|
-
// 1 hour
|
|
62
54
|
},
|
|
63
55
|
maxSize: {
|
|
64
|
-
mx:
|
|
65
|
-
disposable:
|
|
66
|
-
free:
|
|
67
|
-
domainValid:
|
|
68
|
-
smtp:
|
|
69
|
-
smtpPort:
|
|
70
|
-
domainSuggestion:
|
|
71
|
-
whois:
|
|
56
|
+
mx: 1e4,
|
|
57
|
+
disposable: 1e4,
|
|
58
|
+
free: 1e4,
|
|
59
|
+
domainValid: 1e4,
|
|
60
|
+
smtp: 1e4,
|
|
61
|
+
smtpPort: 1e4,
|
|
62
|
+
domainSuggestion: 1e4,
|
|
63
|
+
whois: 1e4
|
|
72
64
|
}
|
|
73
65
|
};
|
|
74
66
|
let defaultCacheInstance = null;
|
|
@@ -106,41 +98,7 @@ function resetDefaultCache() {
|
|
|
106
98
|
defaultCacheInstance = null;
|
|
107
99
|
}
|
|
108
100
|
|
|
109
|
-
|
|
110
|
-
const { domain, cache, logger } = params;
|
|
111
|
-
const log = logger || (() => {
|
|
112
|
-
});
|
|
113
|
-
const cacheStore = getCacheStore(cache, "mx");
|
|
114
|
-
const cached = await cacheStore.get(domain);
|
|
115
|
-
if (cached !== null && cached !== void 0) {
|
|
116
|
-
log(`[resolveMxRecords] Cache hit for ${domain}: ${cached.length} MX records`);
|
|
117
|
-
return cached;
|
|
118
|
-
}
|
|
119
|
-
log(`[resolveMxRecords] Performing DNS MX lookup for ${domain}`);
|
|
120
|
-
try {
|
|
121
|
-
const records = await promises.resolveMx(domain);
|
|
122
|
-
records.sort((a, b) => {
|
|
123
|
-
if (a.priority < b.priority) {
|
|
124
|
-
return -1;
|
|
125
|
-
}
|
|
126
|
-
if (a.priority > b.priority) {
|
|
127
|
-
return 1;
|
|
128
|
-
}
|
|
129
|
-
return 0;
|
|
130
|
-
});
|
|
131
|
-
const exchanges = records.map((record) => record.exchange);
|
|
132
|
-
log(`[resolveMxRecords] Found ${exchanges.length} MX records for ${domain}: [${exchanges.join(", ")}]`);
|
|
133
|
-
await cacheStore.set(domain, exchanges);
|
|
134
|
-
log(`[resolveMxRecords] Cached ${exchanges.length} MX records for ${domain}`);
|
|
135
|
-
return exchanges;
|
|
136
|
-
} catch (error) {
|
|
137
|
-
log(`[resolveMxRecords] MX lookup failed for ${domain}, caching empty result`);
|
|
138
|
-
await cacheStore.set(domain, []);
|
|
139
|
-
throw error;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const COMMON_EMAIL_DOMAINS = [
|
|
101
|
+
const commonEmailDomains = [
|
|
144
102
|
// Popular free email providers
|
|
145
103
|
"gmail.com",
|
|
146
104
|
"yahoo.com",
|
|
@@ -225,7 +183,7 @@ function getSimilarityThreshold(domain) {
|
|
|
225
183
|
return 0.75;
|
|
226
184
|
}
|
|
227
185
|
function defaultDomainSuggestionMethod(domain, commonDomains) {
|
|
228
|
-
const domainsToCheck = commonDomains ||
|
|
186
|
+
const domainsToCheck = commonDomains || commonEmailDomains;
|
|
229
187
|
const lowerDomain = domain.toLowerCase();
|
|
230
188
|
if (domainsToCheck.includes(lowerDomain)) {
|
|
231
189
|
return null;
|
|
@@ -281,7 +239,7 @@ async function defaultDomainSuggestionMethodImpl(domain, commonDomains, cache) {
|
|
|
281
239
|
if (!domain || domain.length < 3) {
|
|
282
240
|
return null;
|
|
283
241
|
}
|
|
284
|
-
const domainsToCheck = commonDomains ||
|
|
242
|
+
const domainsToCheck = commonDomains || commonEmailDomains;
|
|
285
243
|
const lowerDomain = domain.toLowerCase();
|
|
286
244
|
const cacheKey = `${lowerDomain}:${domainsToCheck.length}`;
|
|
287
245
|
const cacheStore = getCacheStore(cache, "domainSuggestion");
|
|
@@ -377,15 +335,107 @@ async function suggestEmailDomain(email, commonDomains, cache) {
|
|
|
377
335
|
return null;
|
|
378
336
|
}
|
|
379
337
|
function isCommonDomain(domain, commonDomains) {
|
|
380
|
-
const domainsToCheck = commonDomains ||
|
|
338
|
+
const domainsToCheck = commonDomains || commonEmailDomains;
|
|
381
339
|
return domainsToCheck.includes(domain.toLowerCase());
|
|
382
340
|
}
|
|
383
341
|
function getDomainSimilarity(domain1, domain2) {
|
|
384
342
|
return stringSimilarity(domain1.toLowerCase(), domain2.toLowerCase());
|
|
385
343
|
}
|
|
386
344
|
|
|
387
|
-
|
|
388
|
-
|
|
345
|
+
async function isValidEmailDomain(emailOrDomain, cache) {
|
|
346
|
+
let [localPart, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
|
|
347
|
+
if (!emailDomain) {
|
|
348
|
+
emailDomain = localPart;
|
|
349
|
+
}
|
|
350
|
+
if (!emailDomain) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
const cacheStore = getCacheStore(cache, "domainValid");
|
|
354
|
+
const cached = await cacheStore.get(emailDomain);
|
|
355
|
+
if (cached !== null && cached !== void 0) {
|
|
356
|
+
return cached.isValid;
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
const isValidResult = isValid(emailDomain) || false;
|
|
360
|
+
const richResult = {
|
|
361
|
+
isValid: isValidResult,
|
|
362
|
+
hasMX: false,
|
|
363
|
+
// MX not checked in this function
|
|
364
|
+
checkedAt: Date.now()
|
|
365
|
+
};
|
|
366
|
+
await cacheStore.set(emailDomain, richResult);
|
|
367
|
+
return isValidResult;
|
|
368
|
+
} catch (validationError) {
|
|
369
|
+
const errorResult = {
|
|
370
|
+
isValid: false,
|
|
371
|
+
hasMX: false,
|
|
372
|
+
checkedAt: Date.now()
|
|
373
|
+
};
|
|
374
|
+
await cacheStore.set(emailDomain, errorResult);
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function isValidEmail(emailAddress) {
|
|
379
|
+
if (!emailAddress || typeof emailAddress !== "string") {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
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,}))$/;
|
|
383
|
+
const emailLower = emailAddress.toLowerCase();
|
|
384
|
+
if (emailLower.indexOf(".+") !== -1)
|
|
385
|
+
return false;
|
|
386
|
+
if (emailLower.indexOf("..") !== -1)
|
|
387
|
+
return false;
|
|
388
|
+
if (emailLower.startsWith(".") || emailLower.endsWith("."))
|
|
389
|
+
return false;
|
|
390
|
+
const parts = emailAddress.split("@");
|
|
391
|
+
if (parts.length !== 2)
|
|
392
|
+
return false;
|
|
393
|
+
const [localPart, domain] = parts;
|
|
394
|
+
if (!localPart || !domain)
|
|
395
|
+
return false;
|
|
396
|
+
if (localPart.length > 64)
|
|
397
|
+
return false;
|
|
398
|
+
if (domain.length > 253)
|
|
399
|
+
return false;
|
|
400
|
+
return re.test(emailLower);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async function resolveMxRecords(params) {
|
|
404
|
+
const { domain, cache, logger } = params;
|
|
405
|
+
const log = logger || (() => {
|
|
406
|
+
});
|
|
407
|
+
const cacheStore = getCacheStore(cache, "mx");
|
|
408
|
+
const cached = await cacheStore.get(domain);
|
|
409
|
+
if (cached !== null && cached !== void 0) {
|
|
410
|
+
log(`[resolveMxRecords] Cache hit for ${domain}: ${cached === null || cached === void 0 ? void 0 : cached.length} MX records`);
|
|
411
|
+
return cached;
|
|
412
|
+
}
|
|
413
|
+
log(`[resolveMxRecords] Performing DNS MX lookup for ${domain}`);
|
|
414
|
+
try {
|
|
415
|
+
const records = await promises.resolveMx(domain);
|
|
416
|
+
records === null || records === void 0 ? void 0 : records.sort((a, b) => {
|
|
417
|
+
if (a.priority < b.priority) {
|
|
418
|
+
return -1;
|
|
419
|
+
}
|
|
420
|
+
if (a.priority > b.priority) {
|
|
421
|
+
return 1;
|
|
422
|
+
}
|
|
423
|
+
return 0;
|
|
424
|
+
});
|
|
425
|
+
const exchanges = records === null || records === void 0 ? void 0 : records.map((record) => record.exchange);
|
|
426
|
+
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(", ")}]`);
|
|
427
|
+
await cacheStore.set(domain, exchanges);
|
|
428
|
+
log(`[resolveMxRecords] Cached ${exchanges === null || exchanges === void 0 ? void 0 : exchanges.length} MX records for ${domain}`);
|
|
429
|
+
return exchanges;
|
|
430
|
+
} catch (error) {
|
|
431
|
+
log(`[resolveMxRecords] MX lookup failed for ${domain}, caching empty result`);
|
|
432
|
+
await cacheStore.set(domain, []);
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const nameSeparator = [".", "_", "-"];
|
|
438
|
+
const commonNameSuffixes = [
|
|
389
439
|
"mail",
|
|
390
440
|
"email",
|
|
391
441
|
"contact",
|
|
@@ -400,7 +450,7 @@ const COMMON_NAME_SUFFIXES = [
|
|
|
400
450
|
"notifications",
|
|
401
451
|
"alerts"
|
|
402
452
|
];
|
|
403
|
-
const
|
|
453
|
+
const contextualSuffixes = [
|
|
404
454
|
"dev",
|
|
405
455
|
"company",
|
|
406
456
|
"team",
|
|
@@ -432,7 +482,7 @@ const COMMON_TITLES = [
|
|
|
432
482
|
"father",
|
|
433
483
|
"sister"
|
|
434
484
|
];
|
|
435
|
-
const
|
|
485
|
+
const commonFirstName = /* @__PURE__ */ new Set([
|
|
436
486
|
// English names
|
|
437
487
|
"james",
|
|
438
488
|
"john",
|
|
@@ -610,7 +660,7 @@ const COMMON_FIRST_NAMES = /* @__PURE__ */ new Set([
|
|
|
610
660
|
"sekou",
|
|
611
661
|
"mariama"
|
|
612
662
|
]);
|
|
613
|
-
const
|
|
663
|
+
const commonLastName = /* @__PURE__ */ new Set([
|
|
614
664
|
// English surnames
|
|
615
665
|
"smith",
|
|
616
666
|
"johnson",
|
|
@@ -730,19 +780,19 @@ function isYearLike(str) {
|
|
|
730
780
|
return /^(19|20)\d{2}$/.test(str);
|
|
731
781
|
}
|
|
732
782
|
function isKnownFirstName(str) {
|
|
733
|
-
return
|
|
783
|
+
return commonFirstName.has(str.toLowerCase());
|
|
734
784
|
}
|
|
735
785
|
function isKnownLastName(str) {
|
|
736
|
-
return
|
|
786
|
+
return commonLastName.has(str.toLowerCase());
|
|
737
787
|
}
|
|
738
788
|
function isTitle(str) {
|
|
739
789
|
return COMMON_TITLES.includes(str.toLowerCase().replace(".", ""));
|
|
740
790
|
}
|
|
741
791
|
function getFirstNameScore(str) {
|
|
742
792
|
const lower = str.toLowerCase();
|
|
743
|
-
if (
|
|
793
|
+
if (commonFirstName.has(lower))
|
|
744
794
|
return 1;
|
|
745
|
-
if (
|
|
795
|
+
if (commonLastName.has(lower))
|
|
746
796
|
return 0.3;
|
|
747
797
|
if (str.length >= 2 && str.length <= 15 && /^[a-zA-Z]+$/.test(str))
|
|
748
798
|
return 0.5;
|
|
@@ -750,9 +800,9 @@ function getFirstNameScore(str) {
|
|
|
750
800
|
}
|
|
751
801
|
function getLastNameScore(str) {
|
|
752
802
|
const lower = str.toLowerCase();
|
|
753
|
-
if (
|
|
803
|
+
if (commonLastName.has(lower))
|
|
754
804
|
return 1;
|
|
755
|
-
if (
|
|
805
|
+
if (commonFirstName.has(lower))
|
|
756
806
|
return 0.3;
|
|
757
807
|
if (str.length >= 2 && str.length <= 20 && /^[a-zA-Z]+$/.test(str))
|
|
758
808
|
return 0.5;
|
|
@@ -861,7 +911,7 @@ function isLikelyName(str, allowNumbers = false, allowSingleLetter = false) {
|
|
|
861
911
|
return false;
|
|
862
912
|
if (str.length === 1 && allowSingleLetter && /^[a-zA-Z]$/.test(str))
|
|
863
913
|
return true;
|
|
864
|
-
if (
|
|
914
|
+
if (commonNameSuffixes.includes(str.toLowerCase()))
|
|
865
915
|
return false;
|
|
866
916
|
if (allowNumbers) {
|
|
867
917
|
if (/^\d+$/.test(str))
|
|
@@ -923,7 +973,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
923
973
|
}
|
|
924
974
|
}
|
|
925
975
|
if (!firstName && !lastName) {
|
|
926
|
-
for (const separator of
|
|
976
|
+
for (const separator of nameSeparator) {
|
|
927
977
|
if (cleanedLocal.includes(separator)) {
|
|
928
978
|
const parts = cleanedLocal.split(separator).filter((p) => p.length > 0);
|
|
929
979
|
if (parts.length === 2) {
|
|
@@ -985,7 +1035,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
985
1035
|
const firstParsed = parseCompositeNamePart(first);
|
|
986
1036
|
const middleParsed = parseCompositeNamePart(middle);
|
|
987
1037
|
const lastParsed = parseCompositeNamePart(last);
|
|
988
|
-
const isLastSuffix =
|
|
1038
|
+
const isLastSuffix = commonNameSuffixes.includes(last.toLowerCase()) || contextualSuffixes.includes(last.toLowerCase()) || isYearLike(last);
|
|
989
1039
|
if (isLastSuffix) {
|
|
990
1040
|
if (isLikelyName(first, true, true) && isLikelyName(middle, true, true)) {
|
|
991
1041
|
const cleanedFirst = firstParsed.hasNumbers ? firstParsed.cleaned : first;
|
|
@@ -1018,7 +1068,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
1018
1068
|
} else if (parts.length > 3) {
|
|
1019
1069
|
const firstPart = parts[0];
|
|
1020
1070
|
const lastPartLower = parts[parts.length - 1].toLowerCase();
|
|
1021
|
-
const isLastPartSuffix =
|
|
1071
|
+
const isLastPartSuffix = commonNameSuffixes.includes(lastPartLower) || contextualSuffixes.includes(lastPartLower) || isYearLike(parts[parts.length - 1]);
|
|
1022
1072
|
const effectiveLastIndex = isLastPartSuffix ? parts.length - 2 : parts.length - 1;
|
|
1023
1073
|
const lastToUse = effectiveLastIndex >= 0 ? parts[effectiveLastIndex] : null;
|
|
1024
1074
|
if (lastToUse && isLikelyName(firstPart, true, true) && isLikelyName(lastToUse, true, true)) {
|
|
@@ -1101,7 +1151,7 @@ function detectNameFromEmail(params) {
|
|
|
1101
1151
|
}
|
|
1102
1152
|
return defaultNameDetectionMethod(email);
|
|
1103
1153
|
}
|
|
1104
|
-
function
|
|
1154
|
+
function cleanNameForAlgorithm(name) {
|
|
1105
1155
|
if (!name)
|
|
1106
1156
|
return "";
|
|
1107
1157
|
let cleaned = name.replace(/[._*]/g, "");
|
|
@@ -1111,13 +1161,13 @@ function cleanNameForAlgrothin(name) {
|
|
|
1111
1161
|
}
|
|
1112
1162
|
return cleaned;
|
|
1113
1163
|
}
|
|
1114
|
-
function
|
|
1164
|
+
function detectNameForAlgorithm(email) {
|
|
1115
1165
|
const detectedName = detectName(email);
|
|
1116
1166
|
if (!detectedName) {
|
|
1117
1167
|
return null;
|
|
1118
1168
|
}
|
|
1119
|
-
const cleanedFirstName = detectedName.firstName ?
|
|
1120
|
-
const cleanedLastName = detectedName.lastName ?
|
|
1169
|
+
const cleanedFirstName = detectedName.firstName ? cleanNameForAlgorithm(detectedName.firstName) : void 0;
|
|
1170
|
+
const cleanedLastName = detectedName.lastName ? cleanNameForAlgorithm(detectedName.lastName) : void 0;
|
|
1121
1171
|
if (!cleanedFirstName && !cleanedLastName) {
|
|
1122
1172
|
return null;
|
|
1123
1173
|
}
|
|
@@ -1134,27 +1184,143 @@ function detectName(email) {
|
|
|
1134
1184
|
|
|
1135
1185
|
var VerificationErrorCode;
|
|
1136
1186
|
(function(VerificationErrorCode2) {
|
|
1137
|
-
VerificationErrorCode2["
|
|
1138
|
-
VerificationErrorCode2["
|
|
1139
|
-
VerificationErrorCode2["
|
|
1140
|
-
VerificationErrorCode2["
|
|
1141
|
-
VerificationErrorCode2["
|
|
1142
|
-
VerificationErrorCode2["
|
|
1143
|
-
VerificationErrorCode2["
|
|
1144
|
-
VerificationErrorCode2["
|
|
1145
|
-
VerificationErrorCode2["
|
|
1146
|
-
VerificationErrorCode2["
|
|
1187
|
+
VerificationErrorCode2["invalidFormat"] = "INVALID_FORMAT";
|
|
1188
|
+
VerificationErrorCode2["invalidDomain"] = "INVALID_DOMAIN";
|
|
1189
|
+
VerificationErrorCode2["noMxRecords"] = "NO_MX_RECORDS";
|
|
1190
|
+
VerificationErrorCode2["smtpConnectionFailed"] = "SMTP_CONNECTION_FAILED";
|
|
1191
|
+
VerificationErrorCode2["smtpTimeout"] = "SMTP_TIMEOUT";
|
|
1192
|
+
VerificationErrorCode2["mailboxNotFound"] = "MAILBOX_NOT_FOUND";
|
|
1193
|
+
VerificationErrorCode2["mailboxFull"] = "MAILBOX_FULL";
|
|
1194
|
+
VerificationErrorCode2["networkError"] = "NETWORK_ERROR";
|
|
1195
|
+
VerificationErrorCode2["disposableEmail"] = "DISPOSABLE_EMAIL";
|
|
1196
|
+
VerificationErrorCode2["freeEmailProvider"] = "FREE_EMAIL_PROVIDER";
|
|
1147
1197
|
})(VerificationErrorCode || (VerificationErrorCode = {}));
|
|
1198
|
+
var EmailProvider;
|
|
1199
|
+
(function(EmailProvider2) {
|
|
1200
|
+
EmailProvider2["gmail"] = "gmail";
|
|
1201
|
+
EmailProvider2["hotmailB2b"] = "hotmail_b2b";
|
|
1202
|
+
EmailProvider2["hotmailB2c"] = "hotmail_b2c";
|
|
1203
|
+
EmailProvider2["proofpoint"] = "proofpoint";
|
|
1204
|
+
EmailProvider2["mimecast"] = "mimecast";
|
|
1205
|
+
EmailProvider2["yahoo"] = "yahoo";
|
|
1206
|
+
EmailProvider2["everythingElse"] = "everything_else";
|
|
1207
|
+
})(EmailProvider || (EmailProvider = {}));
|
|
1208
|
+
function parseSmtpError(errorMessage) {
|
|
1209
|
+
const lowerError = errorMessage.toLowerCase();
|
|
1210
|
+
const networkErrorPatterns = [
|
|
1211
|
+
"etimedout",
|
|
1212
|
+
"econnrefused",
|
|
1213
|
+
"enotfound",
|
|
1214
|
+
"econnreset",
|
|
1215
|
+
"socket hang up",
|
|
1216
|
+
"connection_timeout",
|
|
1217
|
+
"socket_timeout",
|
|
1218
|
+
"connection_error",
|
|
1219
|
+
"connection_closed"
|
|
1220
|
+
];
|
|
1221
|
+
const isNetworkError = networkErrorPatterns.some((pattern) => lowerError.includes(pattern));
|
|
1222
|
+
if (isNetworkError) {
|
|
1223
|
+
return {
|
|
1224
|
+
isDisabled: false,
|
|
1225
|
+
hasFullInbox: false,
|
|
1226
|
+
isInvalid: true,
|
|
1227
|
+
isCatchAll: false
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
const disabledPatterns = [
|
|
1231
|
+
"account disabled",
|
|
1232
|
+
"account is disabled",
|
|
1233
|
+
"user disabled",
|
|
1234
|
+
"user is disabled",
|
|
1235
|
+
"account locked",
|
|
1236
|
+
"account is locked",
|
|
1237
|
+
"user blocked",
|
|
1238
|
+
"user is blocked",
|
|
1239
|
+
"mailbox disabled",
|
|
1240
|
+
"delivery not authorized",
|
|
1241
|
+
"message rejected",
|
|
1242
|
+
"access denied",
|
|
1243
|
+
"permission denied",
|
|
1244
|
+
"recipient unknown",
|
|
1245
|
+
"recipient address rejected",
|
|
1246
|
+
"user unknown",
|
|
1247
|
+
"address unknown",
|
|
1248
|
+
"invalid recipient",
|
|
1249
|
+
"not a valid recipient",
|
|
1250
|
+
"recipient does not exist",
|
|
1251
|
+
"no such user",
|
|
1252
|
+
"user does not exist",
|
|
1253
|
+
"mailbox unavailable",
|
|
1254
|
+
"recipient unavailable",
|
|
1255
|
+
"address rejected",
|
|
1256
|
+
"550",
|
|
1257
|
+
"551",
|
|
1258
|
+
"553",
|
|
1259
|
+
"not_found",
|
|
1260
|
+
"ambiguous"
|
|
1261
|
+
];
|
|
1262
|
+
const fullInboxPatterns = [
|
|
1263
|
+
"mailbox full",
|
|
1264
|
+
"inbox full",
|
|
1265
|
+
"quota exceeded",
|
|
1266
|
+
"over quota",
|
|
1267
|
+
"storage limit exceeded",
|
|
1268
|
+
"message too large",
|
|
1269
|
+
"insufficient storage",
|
|
1270
|
+
"mailbox over quota",
|
|
1271
|
+
"over the quota",
|
|
1272
|
+
"mailbox size limit exceeded",
|
|
1273
|
+
"account over quota",
|
|
1274
|
+
"storage space",
|
|
1275
|
+
"overquota",
|
|
1276
|
+
"452",
|
|
1277
|
+
"552",
|
|
1278
|
+
"over_quota"
|
|
1279
|
+
];
|
|
1280
|
+
const catchAllPatterns = [
|
|
1281
|
+
"accept all mail",
|
|
1282
|
+
"catch-all",
|
|
1283
|
+
"catchall",
|
|
1284
|
+
"wildcard",
|
|
1285
|
+
"accepts any recipient",
|
|
1286
|
+
"recipient address accepted"
|
|
1287
|
+
];
|
|
1288
|
+
const rateLimitPatterns = [
|
|
1289
|
+
"receiving mail at a rate that",
|
|
1290
|
+
"rate limit",
|
|
1291
|
+
"too many messages",
|
|
1292
|
+
"temporarily rejected",
|
|
1293
|
+
"try again later",
|
|
1294
|
+
"greylisted",
|
|
1295
|
+
"greylist",
|
|
1296
|
+
"deferring",
|
|
1297
|
+
"temporarily deferred",
|
|
1298
|
+
"421",
|
|
1299
|
+
"450",
|
|
1300
|
+
"451",
|
|
1301
|
+
"temporary_failure"
|
|
1302
|
+
];
|
|
1303
|
+
const isDisabled = disabledPatterns.some((pattern) => lowerError.includes(pattern)) || lowerError.startsWith("550") || lowerError.startsWith("551") || lowerError.startsWith("553");
|
|
1304
|
+
const hasFullInbox = fullInboxPatterns.some((pattern) => lowerError.includes(pattern)) || lowerError.startsWith("452") || lowerError.startsWith("552");
|
|
1305
|
+
const isCatchAll = catchAllPatterns.some((pattern) => lowerError.includes(pattern));
|
|
1306
|
+
const isInvalid = !isDisabled && !hasFullInbox && !isCatchAll && !rateLimitPatterns.some((pattern) => lowerError.includes(pattern)) && !lowerError.startsWith("421") && !lowerError.startsWith("450") && !lowerError.startsWith("451");
|
|
1307
|
+
return {
|
|
1308
|
+
isDisabled,
|
|
1309
|
+
hasFullInbox,
|
|
1310
|
+
isInvalid,
|
|
1311
|
+
isCatchAll
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1148
1314
|
var SMTPStep;
|
|
1149
1315
|
(function(SMTPStep2) {
|
|
1150
|
-
SMTPStep2["
|
|
1151
|
-
SMTPStep2["
|
|
1152
|
-
SMTPStep2["
|
|
1153
|
-
SMTPStep2["
|
|
1154
|
-
SMTPStep2["
|
|
1155
|
-
SMTPStep2["
|
|
1156
|
-
SMTPStep2["
|
|
1157
|
-
SMTPStep2["
|
|
1316
|
+
SMTPStep2["greeting"] = "GREETING";
|
|
1317
|
+
SMTPStep2["ehlo"] = "EHLO";
|
|
1318
|
+
SMTPStep2["helo"] = "HELO";
|
|
1319
|
+
SMTPStep2["startTls"] = "STARTTLS";
|
|
1320
|
+
SMTPStep2["mailFrom"] = "MAIL_FROM";
|
|
1321
|
+
SMTPStep2["rcptTo"] = "RCPT_TO";
|
|
1322
|
+
SMTPStep2["vrfy"] = "VRFY";
|
|
1323
|
+
SMTPStep2["quit"] = "QUIT";
|
|
1158
1324
|
})(SMTPStep || (SMTPStep = {}));
|
|
1159
1325
|
|
|
1160
1326
|
function isIPAddress(host) {
|
|
@@ -1188,9 +1354,38 @@ async function verifyMailboxSMTP(params) {
|
|
|
1188
1354
|
const { ports = DEFAULT_PORTS, timeout = DEFAULT_TIMEOUT, maxRetries = DEFAULT_MAX_RETRIES, tls: tlsConfig = true, hostname = "localhost", useVRFY = true, cache, debug = false, sequence } = options;
|
|
1189
1355
|
const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
|
|
1190
1356
|
};
|
|
1357
|
+
const createSmtpResult = (result, port, tlsUsed, mxHost2) => {
|
|
1358
|
+
const reason = result === true ? "valid" : result === null ? "ambiguous" : "not_found";
|
|
1359
|
+
const parsedError = parseSmtpError(reason);
|
|
1360
|
+
return {
|
|
1361
|
+
canConnectSmtp: result !== null,
|
|
1362
|
+
hasFullInbox: parsedError.hasFullInbox,
|
|
1363
|
+
isCatchAll: parsedError.isCatchAll,
|
|
1364
|
+
isDeliverable: result === true,
|
|
1365
|
+
isDisabled: result === false && parsedError.isDisabled,
|
|
1366
|
+
error: result === null ? reason : result === false ? reason : void 0,
|
|
1367
|
+
providerUsed: EmailProvider.everythingElse,
|
|
1368
|
+
checkedAt: Date.now()
|
|
1369
|
+
};
|
|
1370
|
+
};
|
|
1371
|
+
const createFailureResult = (error) => ({
|
|
1372
|
+
canConnectSmtp: false,
|
|
1373
|
+
hasFullInbox: false,
|
|
1374
|
+
isCatchAll: false,
|
|
1375
|
+
isDeliverable: false,
|
|
1376
|
+
isDisabled: false,
|
|
1377
|
+
error,
|
|
1378
|
+
providerUsed: EmailProvider.everythingElse,
|
|
1379
|
+
checkedAt: Date.now()
|
|
1380
|
+
});
|
|
1191
1381
|
if (!mxRecords || mxRecords.length === 0) {
|
|
1192
1382
|
log("No MX records found");
|
|
1193
|
-
return {
|
|
1383
|
+
return {
|
|
1384
|
+
smtpResult: createFailureResult("No MX records found"),
|
|
1385
|
+
cached: false,
|
|
1386
|
+
port: 0,
|
|
1387
|
+
portCached: false
|
|
1388
|
+
};
|
|
1194
1389
|
}
|
|
1195
1390
|
const mxHost = mxRecords[0];
|
|
1196
1391
|
log(`Verifying ${local}@${domain} via ${mxHost}`);
|
|
@@ -1199,16 +1394,16 @@ async function verifyMailboxSMTP(params) {
|
|
|
1199
1394
|
let cachedResult;
|
|
1200
1395
|
try {
|
|
1201
1396
|
cachedResult = await smtpCacheStore.get(`${mxHost}:${local}@${domain}`);
|
|
1202
|
-
if (cachedResult !== void 0) {
|
|
1203
|
-
log(`Using cached SMTP result: ${cachedResult}`);
|
|
1397
|
+
if (cachedResult !== void 0 && cachedResult !== null) {
|
|
1398
|
+
log(`Using cached SMTP result: ${cachedResult.isDeliverable}`);
|
|
1204
1399
|
return {
|
|
1205
|
-
|
|
1400
|
+
smtpResult: cachedResult,
|
|
1206
1401
|
cached: true,
|
|
1207
1402
|
port: 0,
|
|
1208
1403
|
portCached: false
|
|
1209
1404
|
};
|
|
1210
1405
|
}
|
|
1211
|
-
} catch (
|
|
1406
|
+
} catch (ignoredError) {
|
|
1212
1407
|
cachedResult = void 0;
|
|
1213
1408
|
}
|
|
1214
1409
|
}
|
|
@@ -1217,7 +1412,7 @@ async function verifyMailboxSMTP(params) {
|
|
|
1217
1412
|
let cachedPort;
|
|
1218
1413
|
try {
|
|
1219
1414
|
cachedPort = await smtpPortCacheStore.get(mxHost);
|
|
1220
|
-
} catch (
|
|
1415
|
+
} catch (ignoredError) {
|
|
1221
1416
|
cachedPort = null;
|
|
1222
1417
|
}
|
|
1223
1418
|
if (cachedPort) {
|
|
@@ -1234,21 +1429,22 @@ async function verifyMailboxSMTP(params) {
|
|
|
1234
1429
|
sequence,
|
|
1235
1430
|
log
|
|
1236
1431
|
});
|
|
1237
|
-
|
|
1432
|
+
const smtpResult = createSmtpResult(result);
|
|
1433
|
+
if (smtpCacheStore) {
|
|
1238
1434
|
try {
|
|
1239
|
-
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`,
|
|
1435
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
|
|
1240
1436
|
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1241
|
-
} catch (
|
|
1437
|
+
} catch (ignoredError) {
|
|
1242
1438
|
}
|
|
1243
1439
|
}
|
|
1244
|
-
return {
|
|
1440
|
+
return { smtpResult, cached: false, port: cachedPort, portCached: true };
|
|
1245
1441
|
}
|
|
1246
1442
|
}
|
|
1247
1443
|
for (const port of ports) {
|
|
1248
1444
|
log(`Testing port ${port}`);
|
|
1249
1445
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1250
1446
|
if (attempt > 0) {
|
|
1251
|
-
const delay = Math.min(
|
|
1447
|
+
const delay = Math.min(200 * 2 ** (attempt - 1), 800);
|
|
1252
1448
|
log(`Retry ${attempt + 1}, waiting ${delay}ms`);
|
|
1253
1449
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1254
1450
|
}
|
|
@@ -1264,11 +1460,12 @@ async function verifyMailboxSMTP(params) {
|
|
|
1264
1460
|
sequence,
|
|
1265
1461
|
log
|
|
1266
1462
|
});
|
|
1267
|
-
|
|
1463
|
+
const smtpResult = createSmtpResult(result);
|
|
1464
|
+
if (smtpCacheStore) {
|
|
1268
1465
|
try {
|
|
1269
|
-
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`,
|
|
1466
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
|
|
1270
1467
|
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1271
|
-
} catch (
|
|
1468
|
+
} catch (ignoredError) {
|
|
1272
1469
|
}
|
|
1273
1470
|
}
|
|
1274
1471
|
if (result !== null) {
|
|
@@ -1276,15 +1473,20 @@ async function verifyMailboxSMTP(params) {
|
|
|
1276
1473
|
try {
|
|
1277
1474
|
await smtpPortCacheStore.set(mxHost, port);
|
|
1278
1475
|
log(`Cached port ${port} for ${mxHost}`);
|
|
1279
|
-
} catch (
|
|
1476
|
+
} catch (ignoredError) {
|
|
1280
1477
|
}
|
|
1281
1478
|
}
|
|
1282
|
-
return {
|
|
1479
|
+
return { smtpResult, cached: false, port, portCached: false };
|
|
1283
1480
|
}
|
|
1284
1481
|
}
|
|
1285
1482
|
}
|
|
1286
1483
|
log("All ports failed");
|
|
1287
|
-
return {
|
|
1484
|
+
return {
|
|
1485
|
+
smtpResult: createFailureResult("All SMTP connection attempts failed"),
|
|
1486
|
+
cached: false,
|
|
1487
|
+
port: 0,
|
|
1488
|
+
portCached: false
|
|
1489
|
+
};
|
|
1288
1490
|
}
|
|
1289
1491
|
async function testSMTPConnection(params) {
|
|
1290
1492
|
const { mxHost, port, local, domain, timeout, tlsConfig, hostname, useVRFY, sequence, log } = params;
|
|
@@ -1292,11 +1494,11 @@ async function testSMTPConnection(params) {
|
|
|
1292
1494
|
const useTLS = tlsConfig !== false && (portConfig.tls || portConfig.starttls);
|
|
1293
1495
|
const implicitTLS = portConfig.tls;
|
|
1294
1496
|
const defaultSequence = {
|
|
1295
|
-
steps: [SMTPStep.
|
|
1497
|
+
steps: [SMTPStep.greeting, SMTPStep.ehlo, SMTPStep.mailFrom, SMTPStep.rcptTo]
|
|
1296
1498
|
};
|
|
1297
1499
|
const activeSequence = sequence || defaultSequence;
|
|
1298
1500
|
if (port === 25) {
|
|
1299
|
-
activeSequence.steps = activeSequence.steps.map((step) => step === SMTPStep.
|
|
1501
|
+
activeSequence.steps = activeSequence.steps.map((step) => step === SMTPStep.ehlo ? SMTPStep.helo : step);
|
|
1300
1502
|
}
|
|
1301
1503
|
const tlsOptions = {
|
|
1302
1504
|
host: mxHost,
|
|
@@ -1314,7 +1516,7 @@ async function testSMTPConnection(params) {
|
|
|
1314
1516
|
let resolved = false;
|
|
1315
1517
|
let supportsSTARTTLS = false;
|
|
1316
1518
|
let supportsVRFY = false;
|
|
1317
|
-
|
|
1519
|
+
let cleanup = () => {
|
|
1318
1520
|
if (resolved)
|
|
1319
1521
|
return;
|
|
1320
1522
|
resolved = true;
|
|
@@ -1350,31 +1552,31 @@ async function testSMTPConnection(params) {
|
|
|
1350
1552
|
if (resolved)
|
|
1351
1553
|
return;
|
|
1352
1554
|
switch (step) {
|
|
1353
|
-
case SMTPStep.
|
|
1555
|
+
case SMTPStep.ehlo:
|
|
1354
1556
|
sendCommand(`EHLO ${hostname}`);
|
|
1355
1557
|
break;
|
|
1356
|
-
case SMTPStep.
|
|
1558
|
+
case SMTPStep.helo:
|
|
1357
1559
|
sendCommand(`HELO ${domain}`);
|
|
1358
1560
|
break;
|
|
1359
|
-
case SMTPStep.
|
|
1561
|
+
case SMTPStep.greeting:
|
|
1360
1562
|
break;
|
|
1361
|
-
case SMTPStep.
|
|
1563
|
+
case SMTPStep.startTls:
|
|
1362
1564
|
sendCommand("STARTTLS");
|
|
1363
1565
|
break;
|
|
1364
|
-
case SMTPStep.
|
|
1566
|
+
case SMTPStep.mailFrom: {
|
|
1365
1567
|
const from = activeSequence.from || "<>";
|
|
1366
1568
|
sendCommand(`MAIL FROM:${from}`);
|
|
1367
1569
|
break;
|
|
1368
1570
|
}
|
|
1369
|
-
case SMTPStep.
|
|
1571
|
+
case SMTPStep.rcptTo:
|
|
1370
1572
|
sendCommand(`RCPT TO:<${local}@${domain}>`);
|
|
1371
1573
|
break;
|
|
1372
|
-
case SMTPStep.
|
|
1574
|
+
case SMTPStep.vrfy: {
|
|
1373
1575
|
const vrfyTarget = activeSequence.vrfyTarget || local;
|
|
1374
1576
|
sendCommand(`VRFY ${vrfyTarget}`);
|
|
1375
1577
|
break;
|
|
1376
1578
|
}
|
|
1377
|
-
case SMTPStep.
|
|
1579
|
+
case SMTPStep.quit:
|
|
1378
1580
|
sendCommand("QUIT");
|
|
1379
1581
|
break;
|
|
1380
1582
|
}
|
|
@@ -1398,14 +1600,14 @@ async function testSMTPConnection(params) {
|
|
|
1398
1600
|
}
|
|
1399
1601
|
if (isMultiline) {
|
|
1400
1602
|
const currentStep2 = activeSequence.steps[currentStepIndex];
|
|
1401
|
-
if (currentStep2 === SMTPStep.
|
|
1603
|
+
if (currentStep2 === SMTPStep.ehlo && code === "250") {
|
|
1402
1604
|
const upper = response.toUpperCase();
|
|
1403
1605
|
if (upper.includes("STARTTLS"))
|
|
1404
1606
|
supportsSTARTTLS = true;
|
|
1405
1607
|
if (upper.includes("VRFY"))
|
|
1406
1608
|
supportsVRFY = true;
|
|
1407
1609
|
}
|
|
1408
|
-
if (currentStep2 === SMTPStep.
|
|
1610
|
+
if (currentStep2 === SMTPStep.helo && code === "250") {
|
|
1409
1611
|
const upper = response.toUpperCase();
|
|
1410
1612
|
if (upper.includes("VRFY"))
|
|
1411
1613
|
supportsVRFY = true;
|
|
@@ -1418,19 +1620,19 @@ async function testSMTPConnection(params) {
|
|
|
1418
1620
|
}
|
|
1419
1621
|
const currentStep = activeSequence.steps[currentStepIndex];
|
|
1420
1622
|
switch (currentStep) {
|
|
1421
|
-
case SMTPStep.
|
|
1623
|
+
case SMTPStep.greeting:
|
|
1422
1624
|
if (code.startsWith("220")) {
|
|
1423
1625
|
nextStep();
|
|
1424
1626
|
} else {
|
|
1425
1627
|
finish(null, "no_greeting");
|
|
1426
1628
|
}
|
|
1427
1629
|
break;
|
|
1428
|
-
case SMTPStep.
|
|
1630
|
+
case SMTPStep.ehlo:
|
|
1429
1631
|
if (code.startsWith("250")) {
|
|
1430
|
-
const hasSTARTTLS = activeSequence.steps.includes(SMTPStep.
|
|
1632
|
+
const hasSTARTTLS = activeSequence.steps.includes(SMTPStep.startTls);
|
|
1431
1633
|
if (!isTLS && useTLS && supportsSTARTTLS && !implicitTLS && hasSTARTTLS) {
|
|
1432
|
-
currentStepIndex = activeSequence.steps.indexOf(SMTPStep.
|
|
1433
|
-
executeStep(SMTPStep.
|
|
1634
|
+
currentStepIndex = activeSequence.steps.indexOf(SMTPStep.startTls);
|
|
1635
|
+
executeStep(SMTPStep.startTls);
|
|
1434
1636
|
} else {
|
|
1435
1637
|
nextStep();
|
|
1436
1638
|
}
|
|
@@ -1438,14 +1640,14 @@ async function testSMTPConnection(params) {
|
|
|
1438
1640
|
finish(null, "ehlo_failed");
|
|
1439
1641
|
}
|
|
1440
1642
|
break;
|
|
1441
|
-
case SMTPStep.
|
|
1643
|
+
case SMTPStep.helo:
|
|
1442
1644
|
if (code.startsWith("250")) {
|
|
1443
1645
|
nextStep();
|
|
1444
1646
|
} else {
|
|
1445
1647
|
finish(null, "helo_failed");
|
|
1446
1648
|
}
|
|
1447
1649
|
break;
|
|
1448
|
-
case SMTPStep.
|
|
1650
|
+
case SMTPStep.startTls:
|
|
1449
1651
|
if (code.startsWith("220")) {
|
|
1450
1652
|
const plainSocket = socket;
|
|
1451
1653
|
socket = tls.connect({
|
|
@@ -1456,7 +1658,7 @@ async function testSMTPConnection(params) {
|
|
|
1456
1658
|
isTLS = true;
|
|
1457
1659
|
log("TLS upgraded");
|
|
1458
1660
|
buffer = "";
|
|
1459
|
-
const starttlsIndex = activeSequence.steps.indexOf(SMTPStep.
|
|
1661
|
+
const starttlsIndex = activeSequence.steps.indexOf(SMTPStep.startTls);
|
|
1460
1662
|
currentStepIndex = starttlsIndex;
|
|
1461
1663
|
nextStep();
|
|
1462
1664
|
});
|
|
@@ -1466,28 +1668,28 @@ async function testSMTPConnection(params) {
|
|
|
1466
1668
|
nextStep();
|
|
1467
1669
|
}
|
|
1468
1670
|
break;
|
|
1469
|
-
case SMTPStep.
|
|
1671
|
+
case SMTPStep.mailFrom:
|
|
1470
1672
|
if (code.startsWith("250")) {
|
|
1471
1673
|
nextStep();
|
|
1472
1674
|
} else {
|
|
1473
1675
|
finish(null, "mail_from_rejected");
|
|
1474
1676
|
}
|
|
1475
1677
|
break;
|
|
1476
|
-
case SMTPStep.
|
|
1678
|
+
case SMTPStep.rcptTo:
|
|
1477
1679
|
if (code.startsWith("250") || code.startsWith("251")) {
|
|
1478
1680
|
finish(true, "valid");
|
|
1479
1681
|
} else if (code.startsWith("552") || code.startsWith("452")) {
|
|
1480
1682
|
finish(false, "over_quota");
|
|
1481
1683
|
} else if (code.startsWith("4")) {
|
|
1482
1684
|
finish(null, "temporary_failure");
|
|
1483
|
-
} else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(SMTPStep.
|
|
1484
|
-
currentStepIndex = activeSequence.steps.indexOf(SMTPStep.
|
|
1485
|
-
executeStep(SMTPStep.
|
|
1685
|
+
} else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(SMTPStep.vrfy)) {
|
|
1686
|
+
currentStepIndex = activeSequence.steps.indexOf(SMTPStep.vrfy);
|
|
1687
|
+
executeStep(SMTPStep.vrfy);
|
|
1486
1688
|
} else {
|
|
1487
1689
|
finish(null, "ambiguous");
|
|
1488
1690
|
}
|
|
1489
1691
|
break;
|
|
1490
|
-
case SMTPStep.
|
|
1692
|
+
case SMTPStep.vrfy:
|
|
1491
1693
|
if (code.startsWith("250") || code.startsWith("252")) {
|
|
1492
1694
|
finish(true, "vrfy_valid");
|
|
1493
1695
|
} else if (code.startsWith("550")) {
|
|
@@ -1496,14 +1698,14 @@ async function testSMTPConnection(params) {
|
|
|
1496
1698
|
finish(null, "vrfy_failed");
|
|
1497
1699
|
}
|
|
1498
1700
|
break;
|
|
1499
|
-
case SMTPStep.
|
|
1701
|
+
case SMTPStep.quit:
|
|
1500
1702
|
if (code.startsWith("221")) {
|
|
1501
1703
|
finish(null, "quit_received");
|
|
1502
1704
|
}
|
|
1503
1705
|
break;
|
|
1504
1706
|
}
|
|
1505
1707
|
};
|
|
1506
|
-
|
|
1708
|
+
let handleData = (data) => {
|
|
1507
1709
|
if (resolved)
|
|
1508
1710
|
return;
|
|
1509
1711
|
buffer += data.toString();
|
|
@@ -1539,65 +1741,69 @@ async function testSMTPConnection(params) {
|
|
|
1539
1741
|
return;
|
|
1540
1742
|
}
|
|
1541
1743
|
const firstStep = activeSequence.steps[0];
|
|
1542
|
-
|
|
1744
|
+
let connectionTimeout;
|
|
1745
|
+
let stepTimeout;
|
|
1746
|
+
const resetActivityTimeout = () => {
|
|
1747
|
+
if (stepTimeout) {
|
|
1748
|
+
clearTimeout(stepTimeout);
|
|
1749
|
+
}
|
|
1750
|
+
stepTimeout = setTimeout(() => {
|
|
1751
|
+
if (!resolved) {
|
|
1752
|
+
log(`Step timeout after ${timeout}ms of inactivity`);
|
|
1753
|
+
finish(null, "step_timeout");
|
|
1754
|
+
}
|
|
1755
|
+
}, timeout);
|
|
1756
|
+
};
|
|
1757
|
+
connectionTimeout = setTimeout(() => {
|
|
1758
|
+
if (!resolved) {
|
|
1759
|
+
log(`Connection timeout after ${timeout}ms`);
|
|
1760
|
+
finish(null, "connection_timeout");
|
|
1761
|
+
}
|
|
1762
|
+
}, timeout);
|
|
1763
|
+
if (firstStep !== SMTPStep.greeting) {
|
|
1543
1764
|
executeStep(firstStep);
|
|
1544
1765
|
}
|
|
1545
|
-
socket.setTimeout(timeout, () =>
|
|
1546
|
-
|
|
1547
|
-
|
|
1766
|
+
socket.setTimeout(timeout, () => {
|
|
1767
|
+
if (!resolved) {
|
|
1768
|
+
log(`Socket timeout after ${timeout}ms`);
|
|
1769
|
+
finish(null, "socket_timeout");
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
socket.on("error", (error) => {
|
|
1773
|
+
log(`Socket error: ${error.message}`);
|
|
1548
1774
|
if (!resolved)
|
|
1775
|
+
finish(null, "connection_error");
|
|
1776
|
+
});
|
|
1777
|
+
socket.on("close", () => {
|
|
1778
|
+
if (!resolved) {
|
|
1779
|
+
log("Socket closed unexpectedly");
|
|
1549
1780
|
finish(null, "connection_closed");
|
|
1781
|
+
}
|
|
1550
1782
|
});
|
|
1783
|
+
const originalHandleData = handleData;
|
|
1784
|
+
handleData = (data) => {
|
|
1785
|
+
resetActivityTimeout();
|
|
1786
|
+
originalHandleData(data);
|
|
1787
|
+
};
|
|
1788
|
+
const enhancedCleanup = () => {
|
|
1789
|
+
if (resolved)
|
|
1790
|
+
return;
|
|
1791
|
+
resolved = true;
|
|
1792
|
+
if (connectionTimeout)
|
|
1793
|
+
clearTimeout(connectionTimeout);
|
|
1794
|
+
if (stepTimeout)
|
|
1795
|
+
clearTimeout(stepTimeout);
|
|
1796
|
+
socket.setTimeout(0);
|
|
1797
|
+
try {
|
|
1798
|
+
socket === null || socket === void 0 ? void 0 : socket.write("QUIT\r\n");
|
|
1799
|
+
} catch {
|
|
1800
|
+
}
|
|
1801
|
+
setTimeout(() => socket === null || socket === void 0 ? void 0 : socket.destroy(), 100);
|
|
1802
|
+
};
|
|
1803
|
+
cleanup = enhancedCleanup;
|
|
1551
1804
|
});
|
|
1552
1805
|
}
|
|
1553
1806
|
|
|
1554
|
-
async function isValidEmailDomain(emailOrDomain, cache) {
|
|
1555
|
-
let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
|
|
1556
|
-
if (!emailDomain) {
|
|
1557
|
-
emailDomain = _;
|
|
1558
|
-
}
|
|
1559
|
-
if (!emailDomain) {
|
|
1560
|
-
return false;
|
|
1561
|
-
}
|
|
1562
|
-
const cacheStore = getCacheStore(cache, "domainValid");
|
|
1563
|
-
const cached = await cacheStore.get(emailDomain);
|
|
1564
|
-
if (cached !== null && cached !== void 0) {
|
|
1565
|
-
return cached;
|
|
1566
|
-
}
|
|
1567
|
-
try {
|
|
1568
|
-
const result = isValid(emailDomain) || false;
|
|
1569
|
-
await cacheStore.set(emailDomain, result);
|
|
1570
|
-
return result;
|
|
1571
|
-
} catch (_e) {
|
|
1572
|
-
await cacheStore.set(emailDomain, false);
|
|
1573
|
-
return false;
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
function isValidEmail(emailAddress) {
|
|
1577
|
-
if (!emailAddress || typeof emailAddress !== "string") {
|
|
1578
|
-
return false;
|
|
1579
|
-
}
|
|
1580
|
-
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,}))$/;
|
|
1581
|
-
const emailLower = emailAddress.toLowerCase();
|
|
1582
|
-
if (emailLower.indexOf(".+") !== -1)
|
|
1583
|
-
return false;
|
|
1584
|
-
if (emailLower.indexOf("..") !== -1)
|
|
1585
|
-
return false;
|
|
1586
|
-
if (emailLower.startsWith(".") || emailLower.endsWith("."))
|
|
1587
|
-
return false;
|
|
1588
|
-
const parts = emailAddress.split("@");
|
|
1589
|
-
if (parts.length !== 2)
|
|
1590
|
-
return false;
|
|
1591
|
-
const [localPart, domain] = parts;
|
|
1592
|
-
if (!localPart || !domain)
|
|
1593
|
-
return false;
|
|
1594
|
-
if (localPart.length > 64)
|
|
1595
|
-
return false;
|
|
1596
|
-
if (domain.length > 253)
|
|
1597
|
-
return false;
|
|
1598
|
-
return re.test(emailLower);
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
1807
|
const defaultRegex = {
|
|
1602
1808
|
domainName: "Domain Name: *([^\\s]+)",
|
|
1603
1809
|
registrar: "Registrar: *(.+)",
|
|
@@ -2015,7 +2221,7 @@ function parseWhoisData({ rawData, domain }) {
|
|
|
2015
2221
|
return result;
|
|
2016
2222
|
}
|
|
2017
2223
|
|
|
2018
|
-
const
|
|
2224
|
+
const whoisServers = {
|
|
2019
2225
|
com: "whois.verisign-grs.com",
|
|
2020
2226
|
net: "whois.verisign-grs.com",
|
|
2021
2227
|
org: "whois.pir.org",
|
|
@@ -2115,7 +2321,7 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
|
|
|
2115
2321
|
throw new Error("Invalid domain");
|
|
2116
2322
|
}
|
|
2117
2323
|
log(`[whois] extracted TLD: ${tld} for domain: ${domain}`);
|
|
2118
|
-
const whoisServer =
|
|
2324
|
+
const whoisServer = whoisServers[tld];
|
|
2119
2325
|
if (!whoisServer) {
|
|
2120
2326
|
log(`[whois] no specific server for TLD ${tld}, trying IANA`);
|
|
2121
2327
|
const defaultServer = "whois.iana.org";
|
|
@@ -2141,8 +2347,8 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
|
|
|
2141
2347
|
await cacheStore.set(cacheKey, whoisData);
|
|
2142
2348
|
log(`[whois] successfully retrieved and cached WHOIS data for ${domain}`);
|
|
2143
2349
|
return whoisData;
|
|
2144
|
-
} catch (
|
|
2145
|
-
log(`[whois] failed to get WHOIS data for ${domain}: ${
|
|
2350
|
+
} catch (ignoredError) {
|
|
2351
|
+
log(`[whois] failed to get WHOIS data for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
|
|
2146
2352
|
return null;
|
|
2147
2353
|
}
|
|
2148
2354
|
}
|
|
@@ -2179,8 +2385,8 @@ async function getDomainAge(domain, timeout = 5e3, debug = false, cache) {
|
|
|
2179
2385
|
expirationDate: whoisData.expirationDate ? new Date(whoisData.expirationDate) : null,
|
|
2180
2386
|
updatedDate: whoisData.updatedDate ? new Date(whoisData.updatedDate) : null
|
|
2181
2387
|
};
|
|
2182
|
-
} catch (
|
|
2183
|
-
log(`[whois] error getting domain age for ${domain}: ${
|
|
2388
|
+
} catch (ignoredError) {
|
|
2389
|
+
log(`[whois] error getting domain age for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
|
|
2184
2390
|
return null;
|
|
2185
2391
|
}
|
|
2186
2392
|
}
|
|
@@ -2251,8 +2457,8 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3, debug = false,
|
|
|
2251
2457
|
isPendingDelete,
|
|
2252
2458
|
isLocked
|
|
2253
2459
|
};
|
|
2254
|
-
} catch (
|
|
2255
|
-
log(`[whois] error getting domain registration status for ${domain}: ${
|
|
2460
|
+
} catch (ignoredError) {
|
|
2461
|
+
log(`[whois] error getting domain registration status for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
|
|
2256
2462
|
return null;
|
|
2257
2463
|
}
|
|
2258
2464
|
}
|
|
@@ -2428,7 +2634,7 @@ function createErrorResult(email, _error) {
|
|
|
2428
2634
|
metadata: {
|
|
2429
2635
|
verificationTime: 0,
|
|
2430
2636
|
cached: false,
|
|
2431
|
-
error: VerificationErrorCode.
|
|
2637
|
+
error: VerificationErrorCode.smtpConnectionFailed
|
|
2432
2638
|
}
|
|
2433
2639
|
};
|
|
2434
2640
|
}
|
|
@@ -2448,25 +2654,31 @@ async function isDisposableEmail(params) {
|
|
|
2448
2654
|
let cached;
|
|
2449
2655
|
try {
|
|
2450
2656
|
cached = await cacheStore.get(emailDomain);
|
|
2451
|
-
} catch (
|
|
2657
|
+
} catch (ignoredError) {
|
|
2452
2658
|
cached = null;
|
|
2453
2659
|
}
|
|
2454
2660
|
if (cached !== null && cached !== void 0) {
|
|
2455
|
-
log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached}`);
|
|
2456
|
-
return cached;
|
|
2661
|
+
log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached.isDisposable}`);
|
|
2662
|
+
return cached.isDisposable;
|
|
2457
2663
|
}
|
|
2458
2664
|
if (!disposableEmailProviders) {
|
|
2459
2665
|
disposableEmailProviders = new Set(require("./disposable-email-providers.json"));
|
|
2460
2666
|
}
|
|
2461
|
-
const
|
|
2667
|
+
const isDisposable = disposableEmailProviders.has(emailDomain);
|
|
2668
|
+
const richResult = {
|
|
2669
|
+
isDisposable,
|
|
2670
|
+
source: "disposable-email-providers.json",
|
|
2671
|
+
category: isDisposable ? "disposable" : void 0,
|
|
2672
|
+
checkedAt: Date.now()
|
|
2673
|
+
};
|
|
2462
2674
|
try {
|
|
2463
|
-
await cacheStore.set(emailDomain,
|
|
2464
|
-
log(`[isDisposableEmail] Cached result for ${emailDomain}: ${
|
|
2465
|
-
} catch (
|
|
2675
|
+
await cacheStore.set(emailDomain, richResult);
|
|
2676
|
+
log(`[isDisposableEmail] Cached result for ${emailDomain}: ${isDisposable}`);
|
|
2677
|
+
} catch (ignoredError) {
|
|
2466
2678
|
log(`[isDisposableEmail] Cache write error for ${emailDomain}`);
|
|
2467
2679
|
}
|
|
2468
|
-
log(`[isDisposableEmail] Check result for ${emailDomain}: ${
|
|
2469
|
-
return
|
|
2680
|
+
log(`[isDisposableEmail] Check result for ${emailDomain}: ${isDisposable}`);
|
|
2681
|
+
return isDisposable;
|
|
2470
2682
|
}
|
|
2471
2683
|
async function isFreeEmail(params) {
|
|
2472
2684
|
const { emailOrDomain, cache, logger } = params;
|
|
@@ -2481,25 +2693,30 @@ async function isFreeEmail(params) {
|
|
|
2481
2693
|
let cached;
|
|
2482
2694
|
try {
|
|
2483
2695
|
cached = await cacheStore.get(emailDomain);
|
|
2484
|
-
} catch (
|
|
2696
|
+
} catch (ignoredError) {
|
|
2485
2697
|
cached = null;
|
|
2486
2698
|
}
|
|
2487
2699
|
if (cached !== null && cached !== void 0) {
|
|
2488
|
-
log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached}`);
|
|
2489
|
-
return cached;
|
|
2700
|
+
log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached.isFree}`);
|
|
2701
|
+
return cached.isFree;
|
|
2490
2702
|
}
|
|
2491
2703
|
if (!freeEmailProviders) {
|
|
2492
2704
|
freeEmailProviders = new Set(require("./free-email-providers.json"));
|
|
2493
2705
|
}
|
|
2494
|
-
const
|
|
2706
|
+
const isFree = freeEmailProviders.has(emailDomain);
|
|
2707
|
+
const richResult = {
|
|
2708
|
+
isFree,
|
|
2709
|
+
provider: isFree ? emailDomain : void 0,
|
|
2710
|
+
checkedAt: Date.now()
|
|
2711
|
+
};
|
|
2495
2712
|
try {
|
|
2496
|
-
await cacheStore.set(emailDomain,
|
|
2497
|
-
log(`[isFreeEmail] Cached result for ${emailDomain}: ${
|
|
2498
|
-
} catch (
|
|
2713
|
+
await cacheStore.set(emailDomain, richResult);
|
|
2714
|
+
log(`[isFreeEmail] Cached result for ${emailDomain}: ${isFree}`);
|
|
2715
|
+
} catch (ignoredError) {
|
|
2499
2716
|
log(`[isFreeEmail] Cache write error for ${emailDomain}`);
|
|
2500
2717
|
}
|
|
2501
|
-
log(`[isFreeEmail] Check result for ${emailDomain}: ${
|
|
2502
|
-
return
|
|
2718
|
+
log(`[isFreeEmail] Check result for ${emailDomain}: ${isFree}`);
|
|
2719
|
+
return isFree;
|
|
2503
2720
|
}
|
|
2504
2721
|
const domainPorts = {
|
|
2505
2722
|
// 465 or 587
|
|
@@ -2507,7 +2724,7 @@ const domainPorts = {
|
|
|
2507
2724
|
"ovh.net": 465
|
|
2508
2725
|
};
|
|
2509
2726
|
async function verifyEmail(params) {
|
|
2510
|
-
var _a;
|
|
2727
|
+
var _a, _b;
|
|
2511
2728
|
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;
|
|
2512
2729
|
const startTime = Date.now();
|
|
2513
2730
|
const log = debug ? console.debug : (..._args) => {
|
|
@@ -2527,7 +2744,7 @@ async function verifyEmail(params) {
|
|
|
2527
2744
|
if (!isValidEmail(emailAddress)) {
|
|
2528
2745
|
if (result.metadata) {
|
|
2529
2746
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2530
|
-
result.metadata.error = VerificationErrorCode.
|
|
2747
|
+
result.metadata.error = VerificationErrorCode.invalidFormat;
|
|
2531
2748
|
}
|
|
2532
2749
|
return result;
|
|
2533
2750
|
}
|
|
@@ -2553,14 +2770,14 @@ async function verifyEmail(params) {
|
|
|
2553
2770
|
if (!domain || !local) {
|
|
2554
2771
|
if (result.metadata) {
|
|
2555
2772
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2556
|
-
result.metadata.error = VerificationErrorCode.
|
|
2773
|
+
result.metadata.error = VerificationErrorCode.invalidFormat;
|
|
2557
2774
|
}
|
|
2558
2775
|
return result;
|
|
2559
2776
|
}
|
|
2560
2777
|
if (!await isValidEmailDomain(domain, params.cache)) {
|
|
2561
2778
|
if (result.metadata) {
|
|
2562
2779
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2563
|
-
result.metadata.error = VerificationErrorCode.
|
|
2780
|
+
result.metadata.error = VerificationErrorCode.invalidDomain;
|
|
2564
2781
|
}
|
|
2565
2782
|
return result;
|
|
2566
2783
|
}
|
|
@@ -2569,7 +2786,7 @@ async function verifyEmail(params) {
|
|
|
2569
2786
|
result.isDisposable = await isDisposableEmail({ emailOrDomain: emailAddress, cache: params.cache, logger: log });
|
|
2570
2787
|
log(`[verifyEmail] Disposable check result: ${result.isDisposable}`);
|
|
2571
2788
|
if (result.isDisposable && result.metadata) {
|
|
2572
|
-
result.metadata.error = VerificationErrorCode.
|
|
2789
|
+
result.metadata.error = VerificationErrorCode.disposableEmail;
|
|
2573
2790
|
}
|
|
2574
2791
|
}
|
|
2575
2792
|
if (checkFree) {
|
|
@@ -2590,8 +2807,8 @@ async function verifyEmail(params) {
|
|
|
2590
2807
|
try {
|
|
2591
2808
|
result.domainAge = await getDomainAge(domain, whoisTimeout, debug, params.cache);
|
|
2592
2809
|
log(`[verifyEmail] Domain age result:`, result.domainAge ? `${result.domainAge.ageInDays} days` : "null");
|
|
2593
|
-
} catch (
|
|
2594
|
-
log("[verifyEmail] Failed to get domain age",
|
|
2810
|
+
} catch (error) {
|
|
2811
|
+
log("[verifyEmail] Failed to get domain age", error);
|
|
2595
2812
|
result.domainAge = null;
|
|
2596
2813
|
}
|
|
2597
2814
|
} else if (checkDomainAge && shouldSkipDomainWhois) {
|
|
@@ -2602,8 +2819,8 @@ async function verifyEmail(params) {
|
|
|
2602
2819
|
try {
|
|
2603
2820
|
result.domainRegistration = await getDomainRegistrationStatus(domain, whoisTimeout, debug, params.cache);
|
|
2604
2821
|
log(`[verifyEmail] Domain registration result:`, ((_a = result.domainRegistration) === null || _a === void 0 ? void 0 : _a.isRegistered) ? "registered" : "not registered");
|
|
2605
|
-
} catch (
|
|
2606
|
-
log("[verifyEmail] Failed to get domain registration status",
|
|
2822
|
+
} catch (error) {
|
|
2823
|
+
log("[verifyEmail] Failed to get domain registration status", error);
|
|
2607
2824
|
result.domainRegistration = null;
|
|
2608
2825
|
}
|
|
2609
2826
|
} else if (checkDomainRegistration && shouldSkipDomainWhois) {
|
|
@@ -2616,14 +2833,14 @@ async function verifyEmail(params) {
|
|
|
2616
2833
|
result.validMx = mxRecords.length > 0;
|
|
2617
2834
|
log(`[verifyEmail] MX records found: ${mxRecords.length}, valid: ${result.validMx}`);
|
|
2618
2835
|
if (!result.validMx && result.metadata) {
|
|
2619
|
-
result.metadata.error = VerificationErrorCode.
|
|
2836
|
+
result.metadata.error = VerificationErrorCode.noMxRecords;
|
|
2620
2837
|
}
|
|
2621
2838
|
if (verifySmtp && mxRecords.length > 0) {
|
|
2622
2839
|
const cacheKey = `${emailAddress}:smtp`;
|
|
2623
2840
|
const smtpCacheInstance = getCacheStore(params.cache, "smtp");
|
|
2624
2841
|
const cachedSmtp = await smtpCacheInstance.get(cacheKey);
|
|
2625
2842
|
if (cachedSmtp !== null && cachedSmtp !== void 0) {
|
|
2626
|
-
result.validSmtp = cachedSmtp;
|
|
2843
|
+
result.validSmtp = (_b = cachedSmtp.isDeliverable) !== null && _b !== void 0 ? _b : null;
|
|
2627
2844
|
log(`[verifyEmail] SMTP result from cache: ${result.validSmtp} for ${emailAddress}`);
|
|
2628
2845
|
if (result.metadata) {
|
|
2629
2846
|
result.metadata.cached = true;
|
|
@@ -2643,7 +2860,7 @@ async function verifyEmail(params) {
|
|
|
2643
2860
|
domainPort = domainPorts[mxDomain.domain];
|
|
2644
2861
|
}
|
|
2645
2862
|
}
|
|
2646
|
-
const {
|
|
2863
|
+
const { smtpResult, cached, port } = await verifyMailboxSMTP({
|
|
2647
2864
|
local,
|
|
2648
2865
|
domain,
|
|
2649
2866
|
mxRecords,
|
|
@@ -2656,22 +2873,26 @@ async function verifyEmail(params) {
|
|
|
2656
2873
|
}
|
|
2657
2874
|
});
|
|
2658
2875
|
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2659
|
-
|
|
2876
|
+
if (!smtpResult.canConnectSmtp) {
|
|
2877
|
+
result.validSmtp = null;
|
|
2878
|
+
} else {
|
|
2879
|
+
result.validSmtp = smtpResult.isDeliverable;
|
|
2880
|
+
}
|
|
2660
2881
|
if (result.metadata)
|
|
2661
2882
|
result.metadata.cached = cached;
|
|
2662
2883
|
log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
|
|
2663
2884
|
}
|
|
2664
2885
|
if (result.validSmtp === false && result.metadata) {
|
|
2665
|
-
result.metadata.error = VerificationErrorCode.
|
|
2886
|
+
result.metadata.error = VerificationErrorCode.mailboxNotFound;
|
|
2666
2887
|
} else if (result.validSmtp === null && result.metadata) {
|
|
2667
|
-
result.metadata.error = VerificationErrorCode.
|
|
2888
|
+
result.metadata.error = VerificationErrorCode.smtpConnectionFailed;
|
|
2668
2889
|
}
|
|
2669
2890
|
}
|
|
2670
|
-
} catch (
|
|
2671
|
-
log("[verifyEmail] Failed to resolve MX records",
|
|
2891
|
+
} catch (error) {
|
|
2892
|
+
log("[verifyEmail] Failed to resolve MX records", error);
|
|
2672
2893
|
result.validMx = false;
|
|
2673
2894
|
if (result.metadata) {
|
|
2674
|
-
result.metadata.error = VerificationErrorCode.
|
|
2895
|
+
result.metadata.error = VerificationErrorCode.noMxRecords;
|
|
2675
2896
|
}
|
|
2676
2897
|
}
|
|
2677
2898
|
} else if ((verifyMx || verifySmtp) && shouldSkipMx) {
|
|
@@ -2683,5 +2904,5 @@ async function verifyEmail(params) {
|
|
|
2683
2904
|
return result;
|
|
2684
2905
|
}
|
|
2685
2906
|
|
|
2686
|
-
export {
|
|
2907
|
+
export { DEFAULT_CACHE_OPTIONS, EmailProvider, LRUAdapter, RedisAdapter, SMTPStep, VerificationErrorCode, cleanNameForAlgorithm, clearDefaultCache, commonEmailDomains, defaultDomainSuggestionMethod, defaultNameDetectionMethod, detectName, detectNameForAlgorithm, detectNameFromEmail, domainPorts, getCacheStore, getDefaultCache, getDomainAge, getDomainRegistrationStatus, getDomainSimilarity, isCommonDomain, isDisposableEmail, isFreeEmail, isValidEmail, isValidEmailDomain, parseSmtpError, resetDefaultCache, suggestDomain, suggestEmailDomain, verifyEmail, verifyEmailBatch };
|
|
2687
2908
|
//# sourceMappingURL=index.esm.js.map
|