@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.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) {
|
|
@@ -1167,6 +1333,10 @@ function isIPAddress(host) {
|
|
|
1167
1333
|
return ipv6Regex.test(host);
|
|
1168
1334
|
}
|
|
1169
1335
|
function isOverQuota(smtpReply) {
|
|
1336
|
+
var _a;
|
|
1337
|
+
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")) {
|
|
1338
|
+
return true;
|
|
1339
|
+
}
|
|
1170
1340
|
return Boolean(smtpReply && /(over quota)/gi.test(smtpReply));
|
|
1171
1341
|
}
|
|
1172
1342
|
function isInvalidMailboxError(smtpReply) {
|
|
@@ -1188,9 +1358,38 @@ async function verifyMailboxSMTP(params) {
|
|
|
1188
1358
|
const { ports = DEFAULT_PORTS, timeout = DEFAULT_TIMEOUT, maxRetries = DEFAULT_MAX_RETRIES, tls: tlsConfig = true, hostname = "localhost", useVRFY = true, cache, debug = false, sequence } = options;
|
|
1189
1359
|
const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
|
|
1190
1360
|
};
|
|
1361
|
+
const createSmtpResult = (result, port, tlsUsed, mxHost2) => {
|
|
1362
|
+
const reason = result === true ? "valid" : result === null ? "ambiguous" : "not_found";
|
|
1363
|
+
const parsedError = parseSmtpError(reason);
|
|
1364
|
+
return {
|
|
1365
|
+
canConnectSmtp: result !== null,
|
|
1366
|
+
hasFullInbox: parsedError.hasFullInbox,
|
|
1367
|
+
isCatchAll: parsedError.isCatchAll,
|
|
1368
|
+
isDeliverable: result === true,
|
|
1369
|
+
isDisabled: result === false && parsedError.isDisabled,
|
|
1370
|
+
error: result === null ? reason : result === false ? reason : void 0,
|
|
1371
|
+
providerUsed: EmailProvider.everythingElse,
|
|
1372
|
+
checkedAt: Date.now()
|
|
1373
|
+
};
|
|
1374
|
+
};
|
|
1375
|
+
const createFailureResult = (error) => ({
|
|
1376
|
+
canConnectSmtp: false,
|
|
1377
|
+
hasFullInbox: false,
|
|
1378
|
+
isCatchAll: false,
|
|
1379
|
+
isDeliverable: false,
|
|
1380
|
+
isDisabled: false,
|
|
1381
|
+
error,
|
|
1382
|
+
providerUsed: EmailProvider.everythingElse,
|
|
1383
|
+
checkedAt: Date.now()
|
|
1384
|
+
});
|
|
1191
1385
|
if (!mxRecords || mxRecords.length === 0) {
|
|
1192
1386
|
log("No MX records found");
|
|
1193
|
-
return {
|
|
1387
|
+
return {
|
|
1388
|
+
smtpResult: createFailureResult("No MX records found"),
|
|
1389
|
+
cached: false,
|
|
1390
|
+
port: 0,
|
|
1391
|
+
portCached: false
|
|
1392
|
+
};
|
|
1194
1393
|
}
|
|
1195
1394
|
const mxHost = mxRecords[0];
|
|
1196
1395
|
log(`Verifying ${local}@${domain} via ${mxHost}`);
|
|
@@ -1199,16 +1398,16 @@ async function verifyMailboxSMTP(params) {
|
|
|
1199
1398
|
let cachedResult;
|
|
1200
1399
|
try {
|
|
1201
1400
|
cachedResult = await smtpCacheStore.get(`${mxHost}:${local}@${domain}`);
|
|
1202
|
-
if (cachedResult !== void 0) {
|
|
1203
|
-
log(`Using cached SMTP result: ${cachedResult}`);
|
|
1401
|
+
if (cachedResult !== void 0 && cachedResult !== null) {
|
|
1402
|
+
log(`Using cached SMTP result: ${cachedResult.isDeliverable}`);
|
|
1204
1403
|
return {
|
|
1205
|
-
|
|
1404
|
+
smtpResult: cachedResult,
|
|
1206
1405
|
cached: true,
|
|
1207
1406
|
port: 0,
|
|
1208
1407
|
portCached: false
|
|
1209
1408
|
};
|
|
1210
1409
|
}
|
|
1211
|
-
} catch (
|
|
1410
|
+
} catch (ignoredError) {
|
|
1212
1411
|
cachedResult = void 0;
|
|
1213
1412
|
}
|
|
1214
1413
|
}
|
|
@@ -1217,7 +1416,7 @@ async function verifyMailboxSMTP(params) {
|
|
|
1217
1416
|
let cachedPort;
|
|
1218
1417
|
try {
|
|
1219
1418
|
cachedPort = await smtpPortCacheStore.get(mxHost);
|
|
1220
|
-
} catch (
|
|
1419
|
+
} catch (ignoredError) {
|
|
1221
1420
|
cachedPort = null;
|
|
1222
1421
|
}
|
|
1223
1422
|
if (cachedPort) {
|
|
@@ -1234,21 +1433,22 @@ async function verifyMailboxSMTP(params) {
|
|
|
1234
1433
|
sequence,
|
|
1235
1434
|
log
|
|
1236
1435
|
});
|
|
1237
|
-
|
|
1436
|
+
const smtpResult = createSmtpResult(result);
|
|
1437
|
+
if (smtpCacheStore) {
|
|
1238
1438
|
try {
|
|
1239
|
-
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`,
|
|
1439
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
|
|
1240
1440
|
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1241
|
-
} catch (
|
|
1441
|
+
} catch (ignoredError) {
|
|
1242
1442
|
}
|
|
1243
1443
|
}
|
|
1244
|
-
return {
|
|
1444
|
+
return { smtpResult, cached: false, port: cachedPort, portCached: true };
|
|
1245
1445
|
}
|
|
1246
1446
|
}
|
|
1247
1447
|
for (const port of ports) {
|
|
1248
1448
|
log(`Testing port ${port}`);
|
|
1249
1449
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1250
1450
|
if (attempt > 0) {
|
|
1251
|
-
const delay = Math.min(
|
|
1451
|
+
const delay = Math.min(200 * 2 ** (attempt - 1), 800);
|
|
1252
1452
|
log(`Retry ${attempt + 1}, waiting ${delay}ms`);
|
|
1253
1453
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1254
1454
|
}
|
|
@@ -1264,11 +1464,12 @@ async function verifyMailboxSMTP(params) {
|
|
|
1264
1464
|
sequence,
|
|
1265
1465
|
log
|
|
1266
1466
|
});
|
|
1267
|
-
|
|
1467
|
+
const smtpResult = createSmtpResult(result);
|
|
1468
|
+
if (smtpCacheStore) {
|
|
1268
1469
|
try {
|
|
1269
|
-
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`,
|
|
1470
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
|
|
1270
1471
|
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1271
|
-
} catch (
|
|
1472
|
+
} catch (ignoredError) {
|
|
1272
1473
|
}
|
|
1273
1474
|
}
|
|
1274
1475
|
if (result !== null) {
|
|
@@ -1276,15 +1477,20 @@ async function verifyMailboxSMTP(params) {
|
|
|
1276
1477
|
try {
|
|
1277
1478
|
await smtpPortCacheStore.set(mxHost, port);
|
|
1278
1479
|
log(`Cached port ${port} for ${mxHost}`);
|
|
1279
|
-
} catch (
|
|
1480
|
+
} catch (ignoredError) {
|
|
1280
1481
|
}
|
|
1281
1482
|
}
|
|
1282
|
-
return {
|
|
1483
|
+
return { smtpResult, cached: false, port, portCached: false };
|
|
1283
1484
|
}
|
|
1284
1485
|
}
|
|
1285
1486
|
}
|
|
1286
1487
|
log("All ports failed");
|
|
1287
|
-
return {
|
|
1488
|
+
return {
|
|
1489
|
+
smtpResult: createFailureResult("All SMTP connection attempts failed"),
|
|
1490
|
+
cached: false,
|
|
1491
|
+
port: 0,
|
|
1492
|
+
portCached: false
|
|
1493
|
+
};
|
|
1288
1494
|
}
|
|
1289
1495
|
async function testSMTPConnection(params) {
|
|
1290
1496
|
const { mxHost, port, local, domain, timeout, tlsConfig, hostname, useVRFY, sequence, log } = params;
|
|
@@ -1292,11 +1498,11 @@ async function testSMTPConnection(params) {
|
|
|
1292
1498
|
const useTLS = tlsConfig !== false && (portConfig.tls || portConfig.starttls);
|
|
1293
1499
|
const implicitTLS = portConfig.tls;
|
|
1294
1500
|
const defaultSequence = {
|
|
1295
|
-
steps: [SMTPStep.
|
|
1501
|
+
steps: [SMTPStep.greeting, SMTPStep.ehlo, SMTPStep.mailFrom, SMTPStep.rcptTo]
|
|
1296
1502
|
};
|
|
1297
1503
|
const activeSequence = sequence || defaultSequence;
|
|
1298
1504
|
if (port === 25) {
|
|
1299
|
-
activeSequence.steps = activeSequence.steps.map((step) => step === SMTPStep.
|
|
1505
|
+
activeSequence.steps = activeSequence.steps.map((step) => step === SMTPStep.ehlo ? SMTPStep.helo : step);
|
|
1300
1506
|
}
|
|
1301
1507
|
const tlsOptions = {
|
|
1302
1508
|
host: mxHost,
|
|
@@ -1314,7 +1520,7 @@ async function testSMTPConnection(params) {
|
|
|
1314
1520
|
let resolved = false;
|
|
1315
1521
|
let supportsSTARTTLS = false;
|
|
1316
1522
|
let supportsVRFY = false;
|
|
1317
|
-
|
|
1523
|
+
let cleanup = () => {
|
|
1318
1524
|
if (resolved)
|
|
1319
1525
|
return;
|
|
1320
1526
|
resolved = true;
|
|
@@ -1350,31 +1556,31 @@ async function testSMTPConnection(params) {
|
|
|
1350
1556
|
if (resolved)
|
|
1351
1557
|
return;
|
|
1352
1558
|
switch (step) {
|
|
1353
|
-
case SMTPStep.
|
|
1559
|
+
case SMTPStep.ehlo:
|
|
1354
1560
|
sendCommand(`EHLO ${hostname}`);
|
|
1355
1561
|
break;
|
|
1356
|
-
case SMTPStep.
|
|
1562
|
+
case SMTPStep.helo:
|
|
1357
1563
|
sendCommand(`HELO ${domain}`);
|
|
1358
1564
|
break;
|
|
1359
|
-
case SMTPStep.
|
|
1565
|
+
case SMTPStep.greeting:
|
|
1360
1566
|
break;
|
|
1361
|
-
case SMTPStep.
|
|
1567
|
+
case SMTPStep.startTls:
|
|
1362
1568
|
sendCommand("STARTTLS");
|
|
1363
1569
|
break;
|
|
1364
|
-
case SMTPStep.
|
|
1570
|
+
case SMTPStep.mailFrom: {
|
|
1365
1571
|
const from = activeSequence.from || "<>";
|
|
1366
1572
|
sendCommand(`MAIL FROM:${from}`);
|
|
1367
1573
|
break;
|
|
1368
1574
|
}
|
|
1369
|
-
case SMTPStep.
|
|
1575
|
+
case SMTPStep.rcptTo:
|
|
1370
1576
|
sendCommand(`RCPT TO:<${local}@${domain}>`);
|
|
1371
1577
|
break;
|
|
1372
|
-
case SMTPStep.
|
|
1578
|
+
case SMTPStep.vrfy: {
|
|
1373
1579
|
const vrfyTarget = activeSequence.vrfyTarget || local;
|
|
1374
1580
|
sendCommand(`VRFY ${vrfyTarget}`);
|
|
1375
1581
|
break;
|
|
1376
1582
|
}
|
|
1377
|
-
case SMTPStep.
|
|
1583
|
+
case SMTPStep.quit:
|
|
1378
1584
|
sendCommand("QUIT");
|
|
1379
1585
|
break;
|
|
1380
1586
|
}
|
|
@@ -1398,14 +1604,14 @@ async function testSMTPConnection(params) {
|
|
|
1398
1604
|
}
|
|
1399
1605
|
if (isMultiline) {
|
|
1400
1606
|
const currentStep2 = activeSequence.steps[currentStepIndex];
|
|
1401
|
-
if (currentStep2 === SMTPStep.
|
|
1607
|
+
if (currentStep2 === SMTPStep.ehlo && code === "250") {
|
|
1402
1608
|
const upper = response.toUpperCase();
|
|
1403
1609
|
if (upper.includes("STARTTLS"))
|
|
1404
1610
|
supportsSTARTTLS = true;
|
|
1405
1611
|
if (upper.includes("VRFY"))
|
|
1406
1612
|
supportsVRFY = true;
|
|
1407
1613
|
}
|
|
1408
|
-
if (currentStep2 === SMTPStep.
|
|
1614
|
+
if (currentStep2 === SMTPStep.helo && code === "250") {
|
|
1409
1615
|
const upper = response.toUpperCase();
|
|
1410
1616
|
if (upper.includes("VRFY"))
|
|
1411
1617
|
supportsVRFY = true;
|
|
@@ -1418,19 +1624,19 @@ async function testSMTPConnection(params) {
|
|
|
1418
1624
|
}
|
|
1419
1625
|
const currentStep = activeSequence.steps[currentStepIndex];
|
|
1420
1626
|
switch (currentStep) {
|
|
1421
|
-
case SMTPStep.
|
|
1627
|
+
case SMTPStep.greeting:
|
|
1422
1628
|
if (code.startsWith("220")) {
|
|
1423
1629
|
nextStep();
|
|
1424
1630
|
} else {
|
|
1425
1631
|
finish(null, "no_greeting");
|
|
1426
1632
|
}
|
|
1427
1633
|
break;
|
|
1428
|
-
case SMTPStep.
|
|
1634
|
+
case SMTPStep.ehlo:
|
|
1429
1635
|
if (code.startsWith("250")) {
|
|
1430
|
-
const hasSTARTTLS = activeSequence.steps.includes(SMTPStep.
|
|
1636
|
+
const hasSTARTTLS = activeSequence.steps.includes(SMTPStep.startTls);
|
|
1431
1637
|
if (!isTLS && useTLS && supportsSTARTTLS && !implicitTLS && hasSTARTTLS) {
|
|
1432
|
-
currentStepIndex = activeSequence.steps.indexOf(SMTPStep.
|
|
1433
|
-
executeStep(SMTPStep.
|
|
1638
|
+
currentStepIndex = activeSequence.steps.indexOf(SMTPStep.startTls);
|
|
1639
|
+
executeStep(SMTPStep.startTls);
|
|
1434
1640
|
} else {
|
|
1435
1641
|
nextStep();
|
|
1436
1642
|
}
|
|
@@ -1438,14 +1644,14 @@ async function testSMTPConnection(params) {
|
|
|
1438
1644
|
finish(null, "ehlo_failed");
|
|
1439
1645
|
}
|
|
1440
1646
|
break;
|
|
1441
|
-
case SMTPStep.
|
|
1647
|
+
case SMTPStep.helo:
|
|
1442
1648
|
if (code.startsWith("250")) {
|
|
1443
1649
|
nextStep();
|
|
1444
1650
|
} else {
|
|
1445
1651
|
finish(null, "helo_failed");
|
|
1446
1652
|
}
|
|
1447
1653
|
break;
|
|
1448
|
-
case SMTPStep.
|
|
1654
|
+
case SMTPStep.startTls:
|
|
1449
1655
|
if (code.startsWith("220")) {
|
|
1450
1656
|
const plainSocket = socket;
|
|
1451
1657
|
socket = tls.connect({
|
|
@@ -1456,7 +1662,7 @@ async function testSMTPConnection(params) {
|
|
|
1456
1662
|
isTLS = true;
|
|
1457
1663
|
log("TLS upgraded");
|
|
1458
1664
|
buffer = "";
|
|
1459
|
-
const starttlsIndex = activeSequence.steps.indexOf(SMTPStep.
|
|
1665
|
+
const starttlsIndex = activeSequence.steps.indexOf(SMTPStep.startTls);
|
|
1460
1666
|
currentStepIndex = starttlsIndex;
|
|
1461
1667
|
nextStep();
|
|
1462
1668
|
});
|
|
@@ -1466,28 +1672,28 @@ async function testSMTPConnection(params) {
|
|
|
1466
1672
|
nextStep();
|
|
1467
1673
|
}
|
|
1468
1674
|
break;
|
|
1469
|
-
case SMTPStep.
|
|
1675
|
+
case SMTPStep.mailFrom:
|
|
1470
1676
|
if (code.startsWith("250")) {
|
|
1471
1677
|
nextStep();
|
|
1472
1678
|
} else {
|
|
1473
1679
|
finish(null, "mail_from_rejected");
|
|
1474
1680
|
}
|
|
1475
1681
|
break;
|
|
1476
|
-
case SMTPStep.
|
|
1682
|
+
case SMTPStep.rcptTo:
|
|
1477
1683
|
if (code.startsWith("250") || code.startsWith("251")) {
|
|
1478
1684
|
finish(true, "valid");
|
|
1479
1685
|
} else if (code.startsWith("552") || code.startsWith("452")) {
|
|
1480
1686
|
finish(false, "over_quota");
|
|
1481
1687
|
} else if (code.startsWith("4")) {
|
|
1482
1688
|
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.
|
|
1689
|
+
} else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(SMTPStep.vrfy)) {
|
|
1690
|
+
currentStepIndex = activeSequence.steps.indexOf(SMTPStep.vrfy);
|
|
1691
|
+
executeStep(SMTPStep.vrfy);
|
|
1486
1692
|
} else {
|
|
1487
1693
|
finish(null, "ambiguous");
|
|
1488
1694
|
}
|
|
1489
1695
|
break;
|
|
1490
|
-
case SMTPStep.
|
|
1696
|
+
case SMTPStep.vrfy:
|
|
1491
1697
|
if (code.startsWith("250") || code.startsWith("252")) {
|
|
1492
1698
|
finish(true, "vrfy_valid");
|
|
1493
1699
|
} else if (code.startsWith("550")) {
|
|
@@ -1496,14 +1702,14 @@ async function testSMTPConnection(params) {
|
|
|
1496
1702
|
finish(null, "vrfy_failed");
|
|
1497
1703
|
}
|
|
1498
1704
|
break;
|
|
1499
|
-
case SMTPStep.
|
|
1705
|
+
case SMTPStep.quit:
|
|
1500
1706
|
if (code.startsWith("221")) {
|
|
1501
1707
|
finish(null, "quit_received");
|
|
1502
1708
|
}
|
|
1503
1709
|
break;
|
|
1504
1710
|
}
|
|
1505
1711
|
};
|
|
1506
|
-
|
|
1712
|
+
let handleData = (data) => {
|
|
1507
1713
|
if (resolved)
|
|
1508
1714
|
return;
|
|
1509
1715
|
buffer += data.toString();
|
|
@@ -1539,65 +1745,69 @@ async function testSMTPConnection(params) {
|
|
|
1539
1745
|
return;
|
|
1540
1746
|
}
|
|
1541
1747
|
const firstStep = activeSequence.steps[0];
|
|
1542
|
-
|
|
1748
|
+
let connectionTimeout;
|
|
1749
|
+
let stepTimeout;
|
|
1750
|
+
const resetActivityTimeout = () => {
|
|
1751
|
+
if (stepTimeout) {
|
|
1752
|
+
clearTimeout(stepTimeout);
|
|
1753
|
+
}
|
|
1754
|
+
stepTimeout = setTimeout(() => {
|
|
1755
|
+
if (!resolved) {
|
|
1756
|
+
log(`Step timeout after ${timeout}ms of inactivity`);
|
|
1757
|
+
finish(null, "step_timeout");
|
|
1758
|
+
}
|
|
1759
|
+
}, timeout);
|
|
1760
|
+
};
|
|
1761
|
+
connectionTimeout = setTimeout(() => {
|
|
1762
|
+
if (!resolved) {
|
|
1763
|
+
log(`Connection timeout after ${timeout}ms`);
|
|
1764
|
+
finish(null, "connection_timeout");
|
|
1765
|
+
}
|
|
1766
|
+
}, timeout);
|
|
1767
|
+
if (firstStep !== SMTPStep.greeting) {
|
|
1543
1768
|
executeStep(firstStep);
|
|
1544
1769
|
}
|
|
1545
|
-
socket.setTimeout(timeout, () =>
|
|
1546
|
-
|
|
1547
|
-
|
|
1770
|
+
socket.setTimeout(timeout, () => {
|
|
1771
|
+
if (!resolved) {
|
|
1772
|
+
log(`Socket timeout after ${timeout}ms`);
|
|
1773
|
+
finish(null, "socket_timeout");
|
|
1774
|
+
}
|
|
1775
|
+
});
|
|
1776
|
+
socket.on("error", (error) => {
|
|
1777
|
+
log(`Socket error: ${error.message}`);
|
|
1548
1778
|
if (!resolved)
|
|
1779
|
+
finish(null, "connection_error");
|
|
1780
|
+
});
|
|
1781
|
+
socket.on("close", () => {
|
|
1782
|
+
if (!resolved) {
|
|
1783
|
+
log("Socket closed unexpectedly");
|
|
1549
1784
|
finish(null, "connection_closed");
|
|
1785
|
+
}
|
|
1550
1786
|
});
|
|
1787
|
+
const originalHandleData = handleData;
|
|
1788
|
+
handleData = (data) => {
|
|
1789
|
+
resetActivityTimeout();
|
|
1790
|
+
originalHandleData(data);
|
|
1791
|
+
};
|
|
1792
|
+
const enhancedCleanup = () => {
|
|
1793
|
+
if (resolved)
|
|
1794
|
+
return;
|
|
1795
|
+
resolved = true;
|
|
1796
|
+
if (connectionTimeout)
|
|
1797
|
+
clearTimeout(connectionTimeout);
|
|
1798
|
+
if (stepTimeout)
|
|
1799
|
+
clearTimeout(stepTimeout);
|
|
1800
|
+
socket.setTimeout(0);
|
|
1801
|
+
try {
|
|
1802
|
+
socket === null || socket === void 0 ? void 0 : socket.write("QUIT\r\n");
|
|
1803
|
+
} catch {
|
|
1804
|
+
}
|
|
1805
|
+
setTimeout(() => socket === null || socket === void 0 ? void 0 : socket.destroy(), 100);
|
|
1806
|
+
};
|
|
1807
|
+
cleanup = enhancedCleanup;
|
|
1551
1808
|
});
|
|
1552
1809
|
}
|
|
1553
1810
|
|
|
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
1811
|
const defaultRegex = {
|
|
1602
1812
|
domainName: "Domain Name: *([^\\s]+)",
|
|
1603
1813
|
registrar: "Registrar: *(.+)",
|
|
@@ -2015,7 +2225,7 @@ function parseWhoisData({ rawData, domain }) {
|
|
|
2015
2225
|
return result;
|
|
2016
2226
|
}
|
|
2017
2227
|
|
|
2018
|
-
const
|
|
2228
|
+
const whoisServers = {
|
|
2019
2229
|
com: "whois.verisign-grs.com",
|
|
2020
2230
|
net: "whois.verisign-grs.com",
|
|
2021
2231
|
org: "whois.pir.org",
|
|
@@ -2115,7 +2325,7 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
|
|
|
2115
2325
|
throw new Error("Invalid domain");
|
|
2116
2326
|
}
|
|
2117
2327
|
log(`[whois] extracted TLD: ${tld} for domain: ${domain}`);
|
|
2118
|
-
const whoisServer =
|
|
2328
|
+
const whoisServer = whoisServers[tld];
|
|
2119
2329
|
if (!whoisServer) {
|
|
2120
2330
|
log(`[whois] no specific server for TLD ${tld}, trying IANA`);
|
|
2121
2331
|
const defaultServer = "whois.iana.org";
|
|
@@ -2141,8 +2351,8 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
|
|
|
2141
2351
|
await cacheStore.set(cacheKey, whoisData);
|
|
2142
2352
|
log(`[whois] successfully retrieved and cached WHOIS data for ${domain}`);
|
|
2143
2353
|
return whoisData;
|
|
2144
|
-
} catch (
|
|
2145
|
-
log(`[whois] failed to get WHOIS data for ${domain}: ${
|
|
2354
|
+
} catch (ignoredError) {
|
|
2355
|
+
log(`[whois] failed to get WHOIS data for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
|
|
2146
2356
|
return null;
|
|
2147
2357
|
}
|
|
2148
2358
|
}
|
|
@@ -2179,8 +2389,8 @@ async function getDomainAge(domain, timeout = 5e3, debug = false, cache) {
|
|
|
2179
2389
|
expirationDate: whoisData.expirationDate ? new Date(whoisData.expirationDate) : null,
|
|
2180
2390
|
updatedDate: whoisData.updatedDate ? new Date(whoisData.updatedDate) : null
|
|
2181
2391
|
};
|
|
2182
|
-
} catch (
|
|
2183
|
-
log(`[whois] error getting domain age for ${domain}: ${
|
|
2392
|
+
} catch (ignoredError) {
|
|
2393
|
+
log(`[whois] error getting domain age for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
|
|
2184
2394
|
return null;
|
|
2185
2395
|
}
|
|
2186
2396
|
}
|
|
@@ -2251,8 +2461,8 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3, debug = false,
|
|
|
2251
2461
|
isPendingDelete,
|
|
2252
2462
|
isLocked
|
|
2253
2463
|
};
|
|
2254
|
-
} catch (
|
|
2255
|
-
log(`[whois] error getting domain registration status for ${domain}: ${
|
|
2464
|
+
} catch (ignoredError) {
|
|
2465
|
+
log(`[whois] error getting domain registration status for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
|
|
2256
2466
|
return null;
|
|
2257
2467
|
}
|
|
2258
2468
|
}
|
|
@@ -2428,7 +2638,7 @@ function createErrorResult(email, _error) {
|
|
|
2428
2638
|
metadata: {
|
|
2429
2639
|
verificationTime: 0,
|
|
2430
2640
|
cached: false,
|
|
2431
|
-
error: VerificationErrorCode.
|
|
2641
|
+
error: VerificationErrorCode.smtpConnectionFailed
|
|
2432
2642
|
}
|
|
2433
2643
|
};
|
|
2434
2644
|
}
|
|
@@ -2448,25 +2658,31 @@ async function isDisposableEmail(params) {
|
|
|
2448
2658
|
let cached;
|
|
2449
2659
|
try {
|
|
2450
2660
|
cached = await cacheStore.get(emailDomain);
|
|
2451
|
-
} catch (
|
|
2661
|
+
} catch (ignoredError) {
|
|
2452
2662
|
cached = null;
|
|
2453
2663
|
}
|
|
2454
2664
|
if (cached !== null && cached !== void 0) {
|
|
2455
|
-
log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached}`);
|
|
2456
|
-
return cached;
|
|
2665
|
+
log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached.isDisposable}`);
|
|
2666
|
+
return cached.isDisposable;
|
|
2457
2667
|
}
|
|
2458
2668
|
if (!disposableEmailProviders) {
|
|
2459
2669
|
disposableEmailProviders = new Set(require("./disposable-email-providers.json"));
|
|
2460
2670
|
}
|
|
2461
|
-
const
|
|
2671
|
+
const isDisposable = disposableEmailProviders.has(emailDomain);
|
|
2672
|
+
const richResult = {
|
|
2673
|
+
isDisposable,
|
|
2674
|
+
source: "disposable-email-providers.json",
|
|
2675
|
+
category: isDisposable ? "disposable" : void 0,
|
|
2676
|
+
checkedAt: Date.now()
|
|
2677
|
+
};
|
|
2462
2678
|
try {
|
|
2463
|
-
await cacheStore.set(emailDomain,
|
|
2464
|
-
log(`[isDisposableEmail] Cached result for ${emailDomain}: ${
|
|
2465
|
-
} catch (
|
|
2679
|
+
await cacheStore.set(emailDomain, richResult);
|
|
2680
|
+
log(`[isDisposableEmail] Cached result for ${emailDomain}: ${isDisposable}`);
|
|
2681
|
+
} catch (ignoredError) {
|
|
2466
2682
|
log(`[isDisposableEmail] Cache write error for ${emailDomain}`);
|
|
2467
2683
|
}
|
|
2468
|
-
log(`[isDisposableEmail] Check result for ${emailDomain}: ${
|
|
2469
|
-
return
|
|
2684
|
+
log(`[isDisposableEmail] Check result for ${emailDomain}: ${isDisposable}`);
|
|
2685
|
+
return isDisposable;
|
|
2470
2686
|
}
|
|
2471
2687
|
async function isFreeEmail(params) {
|
|
2472
2688
|
const { emailOrDomain, cache, logger } = params;
|
|
@@ -2481,25 +2697,30 @@ async function isFreeEmail(params) {
|
|
|
2481
2697
|
let cached;
|
|
2482
2698
|
try {
|
|
2483
2699
|
cached = await cacheStore.get(emailDomain);
|
|
2484
|
-
} catch (
|
|
2700
|
+
} catch (ignoredError) {
|
|
2485
2701
|
cached = null;
|
|
2486
2702
|
}
|
|
2487
2703
|
if (cached !== null && cached !== void 0) {
|
|
2488
|
-
log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached}`);
|
|
2489
|
-
return cached;
|
|
2704
|
+
log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached.isFree}`);
|
|
2705
|
+
return cached.isFree;
|
|
2490
2706
|
}
|
|
2491
2707
|
if (!freeEmailProviders) {
|
|
2492
2708
|
freeEmailProviders = new Set(require("./free-email-providers.json"));
|
|
2493
2709
|
}
|
|
2494
|
-
const
|
|
2710
|
+
const isFree = freeEmailProviders.has(emailDomain);
|
|
2711
|
+
const richResult = {
|
|
2712
|
+
isFree,
|
|
2713
|
+
provider: isFree ? emailDomain : void 0,
|
|
2714
|
+
checkedAt: Date.now()
|
|
2715
|
+
};
|
|
2495
2716
|
try {
|
|
2496
|
-
await cacheStore.set(emailDomain,
|
|
2497
|
-
log(`[isFreeEmail] Cached result for ${emailDomain}: ${
|
|
2498
|
-
} catch (
|
|
2717
|
+
await cacheStore.set(emailDomain, richResult);
|
|
2718
|
+
log(`[isFreeEmail] Cached result for ${emailDomain}: ${isFree}`);
|
|
2719
|
+
} catch (ignoredError) {
|
|
2499
2720
|
log(`[isFreeEmail] Cache write error for ${emailDomain}`);
|
|
2500
2721
|
}
|
|
2501
|
-
log(`[isFreeEmail] Check result for ${emailDomain}: ${
|
|
2502
|
-
return
|
|
2722
|
+
log(`[isFreeEmail] Check result for ${emailDomain}: ${isFree}`);
|
|
2723
|
+
return isFree;
|
|
2503
2724
|
}
|
|
2504
2725
|
const domainPorts = {
|
|
2505
2726
|
// 465 or 587
|
|
@@ -2507,7 +2728,7 @@ const domainPorts = {
|
|
|
2507
2728
|
"ovh.net": 465
|
|
2508
2729
|
};
|
|
2509
2730
|
async function verifyEmail(params) {
|
|
2510
|
-
var _a;
|
|
2731
|
+
var _a, _b;
|
|
2511
2732
|
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
2733
|
const startTime = Date.now();
|
|
2513
2734
|
const log = debug ? console.debug : (..._args) => {
|
|
@@ -2527,7 +2748,7 @@ async function verifyEmail(params) {
|
|
|
2527
2748
|
if (!isValidEmail(emailAddress)) {
|
|
2528
2749
|
if (result.metadata) {
|
|
2529
2750
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2530
|
-
result.metadata.error = VerificationErrorCode.
|
|
2751
|
+
result.metadata.error = VerificationErrorCode.invalidFormat;
|
|
2531
2752
|
}
|
|
2532
2753
|
return result;
|
|
2533
2754
|
}
|
|
@@ -2553,14 +2774,14 @@ async function verifyEmail(params) {
|
|
|
2553
2774
|
if (!domain || !local) {
|
|
2554
2775
|
if (result.metadata) {
|
|
2555
2776
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2556
|
-
result.metadata.error = VerificationErrorCode.
|
|
2777
|
+
result.metadata.error = VerificationErrorCode.invalidFormat;
|
|
2557
2778
|
}
|
|
2558
2779
|
return result;
|
|
2559
2780
|
}
|
|
2560
2781
|
if (!await isValidEmailDomain(domain, params.cache)) {
|
|
2561
2782
|
if (result.metadata) {
|
|
2562
2783
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2563
|
-
result.metadata.error = VerificationErrorCode.
|
|
2784
|
+
result.metadata.error = VerificationErrorCode.invalidDomain;
|
|
2564
2785
|
}
|
|
2565
2786
|
return result;
|
|
2566
2787
|
}
|
|
@@ -2569,7 +2790,7 @@ async function verifyEmail(params) {
|
|
|
2569
2790
|
result.isDisposable = await isDisposableEmail({ emailOrDomain: emailAddress, cache: params.cache, logger: log });
|
|
2570
2791
|
log(`[verifyEmail] Disposable check result: ${result.isDisposable}`);
|
|
2571
2792
|
if (result.isDisposable && result.metadata) {
|
|
2572
|
-
result.metadata.error = VerificationErrorCode.
|
|
2793
|
+
result.metadata.error = VerificationErrorCode.disposableEmail;
|
|
2573
2794
|
}
|
|
2574
2795
|
}
|
|
2575
2796
|
if (checkFree) {
|
|
@@ -2590,8 +2811,8 @@ async function verifyEmail(params) {
|
|
|
2590
2811
|
try {
|
|
2591
2812
|
result.domainAge = await getDomainAge(domain, whoisTimeout, debug, params.cache);
|
|
2592
2813
|
log(`[verifyEmail] Domain age result:`, result.domainAge ? `${result.domainAge.ageInDays} days` : "null");
|
|
2593
|
-
} catch (
|
|
2594
|
-
log("[verifyEmail] Failed to get domain age",
|
|
2814
|
+
} catch (error) {
|
|
2815
|
+
log("[verifyEmail] Failed to get domain age", error);
|
|
2595
2816
|
result.domainAge = null;
|
|
2596
2817
|
}
|
|
2597
2818
|
} else if (checkDomainAge && shouldSkipDomainWhois) {
|
|
@@ -2602,8 +2823,8 @@ async function verifyEmail(params) {
|
|
|
2602
2823
|
try {
|
|
2603
2824
|
result.domainRegistration = await getDomainRegistrationStatus(domain, whoisTimeout, debug, params.cache);
|
|
2604
2825
|
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",
|
|
2826
|
+
} catch (error) {
|
|
2827
|
+
log("[verifyEmail] Failed to get domain registration status", error);
|
|
2607
2828
|
result.domainRegistration = null;
|
|
2608
2829
|
}
|
|
2609
2830
|
} else if (checkDomainRegistration && shouldSkipDomainWhois) {
|
|
@@ -2616,14 +2837,19 @@ async function verifyEmail(params) {
|
|
|
2616
2837
|
result.validMx = mxRecords.length > 0;
|
|
2617
2838
|
log(`[verifyEmail] MX records found: ${mxRecords.length}, valid: ${result.validMx}`);
|
|
2618
2839
|
if (!result.validMx && result.metadata) {
|
|
2619
|
-
result.metadata.error = VerificationErrorCode.
|
|
2840
|
+
result.metadata.error = VerificationErrorCode.noMxRecords;
|
|
2620
2841
|
}
|
|
2621
2842
|
if (verifySmtp && mxRecords.length > 0) {
|
|
2622
2843
|
const cacheKey = `${emailAddress}:smtp`;
|
|
2623
2844
|
const smtpCacheInstance = getCacheStore(params.cache, "smtp");
|
|
2624
2845
|
const cachedSmtp = await smtpCacheInstance.get(cacheKey);
|
|
2625
2846
|
if (cachedSmtp !== null && cachedSmtp !== void 0) {
|
|
2626
|
-
result.
|
|
2847
|
+
result.canConnectSmtp = cachedSmtp.canConnectSmtp;
|
|
2848
|
+
result.hasFullInbox = cachedSmtp.hasFullInbox;
|
|
2849
|
+
result.isCatchAll = cachedSmtp.isCatchAll;
|
|
2850
|
+
result.isDeliverable = cachedSmtp.isDeliverable;
|
|
2851
|
+
result.isDisabled = cachedSmtp.isDisabled;
|
|
2852
|
+
result.validSmtp = (_b = cachedSmtp.isDeliverable) !== null && _b !== void 0 ? _b : null;
|
|
2627
2853
|
log(`[verifyEmail] SMTP result from cache: ${result.validSmtp} for ${emailAddress}`);
|
|
2628
2854
|
if (result.metadata) {
|
|
2629
2855
|
result.metadata.cached = true;
|
|
@@ -2643,7 +2869,7 @@ async function verifyEmail(params) {
|
|
|
2643
2869
|
domainPort = domainPorts[mxDomain.domain];
|
|
2644
2870
|
}
|
|
2645
2871
|
}
|
|
2646
|
-
const {
|
|
2872
|
+
const { smtpResult, cached, port } = await verifyMailboxSMTP({
|
|
2647
2873
|
local,
|
|
2648
2874
|
domain,
|
|
2649
2875
|
mxRecords,
|
|
@@ -2656,22 +2882,31 @@ async function verifyEmail(params) {
|
|
|
2656
2882
|
}
|
|
2657
2883
|
});
|
|
2658
2884
|
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2659
|
-
result.
|
|
2885
|
+
result.canConnectSmtp = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.canConnectSmtp;
|
|
2886
|
+
result.hasFullInbox = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.hasFullInbox;
|
|
2887
|
+
result.isCatchAll = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.isCatchAll;
|
|
2888
|
+
result.isDeliverable = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.isDeliverable;
|
|
2889
|
+
result.isDisabled = smtpResult === null || smtpResult === void 0 ? void 0 : smtpResult.isDisabled;
|
|
2890
|
+
if (!smtpResult.canConnectSmtp) {
|
|
2891
|
+
result.validSmtp = null;
|
|
2892
|
+
} else {
|
|
2893
|
+
result.validSmtp = smtpResult.isDeliverable;
|
|
2894
|
+
}
|
|
2660
2895
|
if (result.metadata)
|
|
2661
2896
|
result.metadata.cached = cached;
|
|
2662
2897
|
log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
|
|
2663
2898
|
}
|
|
2664
2899
|
if (result.validSmtp === false && result.metadata) {
|
|
2665
|
-
result.metadata.error = VerificationErrorCode.
|
|
2900
|
+
result.metadata.error = VerificationErrorCode.mailboxNotFound;
|
|
2666
2901
|
} else if (result.validSmtp === null && result.metadata) {
|
|
2667
|
-
result.metadata.error = VerificationErrorCode.
|
|
2902
|
+
result.metadata.error = VerificationErrorCode.smtpConnectionFailed;
|
|
2668
2903
|
}
|
|
2669
2904
|
}
|
|
2670
|
-
} catch (
|
|
2671
|
-
log("[verifyEmail] Failed to resolve MX records",
|
|
2905
|
+
} catch (error) {
|
|
2906
|
+
log("[verifyEmail] Failed to resolve MX records", error);
|
|
2672
2907
|
result.validMx = false;
|
|
2673
2908
|
if (result.metadata) {
|
|
2674
|
-
result.metadata.error = VerificationErrorCode.
|
|
2909
|
+
result.metadata.error = VerificationErrorCode.noMxRecords;
|
|
2675
2910
|
}
|
|
2676
2911
|
}
|
|
2677
2912
|
} else if ((verifyMx || verifySmtp) && shouldSkipMx) {
|
|
@@ -2683,5 +2918,5 @@ async function verifyEmail(params) {
|
|
|
2683
2918
|
return result;
|
|
2684
2919
|
}
|
|
2685
2920
|
|
|
2686
|
-
export {
|
|
2921
|
+
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
2922
|
//# sourceMappingURL=index.esm.js.map
|