@emailcheck/email-validator-js 2.11.0 → 2.13.1-beta.0
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 +290 -25
- package/dist/adapters/lru-adapter.d.ts +19 -0
- package/dist/adapters/redis-adapter.d.ts +45 -0
- package/dist/cache-factory.d.ts +39 -0
- package/dist/cache-interface.d.ts +124 -0
- package/dist/cache.d.ts +28 -0
- package/dist/dns.d.ts +2 -1
- package/dist/domain-suggester.d.ts +6 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.esm.js +216 -78
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +216 -78
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +4 -2
- package/dist/validator.d.ts +2 -1
- package/package.json +4 -4
|
@@ -5,9 +5,13 @@ import type { DomainSuggestion, ISuggestDomainParams } from './types';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare const COMMON_EMAIL_DOMAINS: string[];
|
|
7
7
|
/**
|
|
8
|
-
* Default domain suggestion method using string similarity
|
|
8
|
+
* Default domain suggestion method using string similarity (sync version)
|
|
9
9
|
*/
|
|
10
10
|
export declare function defaultDomainSuggestionMethod(domain: string, commonDomains?: string[]): DomainSuggestion | null;
|
|
11
|
+
/**
|
|
12
|
+
* Async version of default domain suggestion method
|
|
13
|
+
*/
|
|
14
|
+
export declare function defaultDomainSuggestionMethodAsync(domain: string, commonDomains?: string[]): Promise<DomainSuggestion | null>;
|
|
11
15
|
/**
|
|
12
16
|
* Suggest a corrected domain for a potentially misspelled email domain
|
|
13
17
|
* @param params - Parameters including domain and optional custom method
|
|
@@ -20,7 +24,7 @@ export declare function suggestDomain(params: ISuggestDomainParams): DomainSugge
|
|
|
20
24
|
* @param commonDomains - Optional list of common domains to check against
|
|
21
25
|
* @returns Domain suggestion with confidence score, or null if no suggestion
|
|
22
26
|
*/
|
|
23
|
-
export declare function suggestEmailDomain(email: string, commonDomains?: string[]): DomainSuggestion | null
|
|
27
|
+
export declare function suggestEmailDomain(email: string, commonDomains?: string[]): Promise<DomainSuggestion | null>;
|
|
24
28
|
/**
|
|
25
29
|
* Check if a domain is in the common domains list
|
|
26
30
|
* @param domain - Domain to check
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ICache } from './cache-interface';
|
|
1
2
|
import { type DetailedVerificationResult, type IVerifyEmailParams, type IVerifyEmailResult } from './types';
|
|
2
3
|
export { verifyEmailBatch } from './batch';
|
|
3
4
|
export { clearAllCaches } from './cache';
|
|
@@ -6,8 +7,8 @@ export { defaultNameDetectionMethod, detectName, detectNameFromEmail } from './n
|
|
|
6
7
|
export * from './types';
|
|
7
8
|
export { isValidEmail, isValidEmailDomain } from './validator';
|
|
8
9
|
export { getDomainAge, getDomainRegistrationStatus } from './whois';
|
|
9
|
-
export declare function isDisposableEmail(emailOrDomain: string): boolean
|
|
10
|
-
export declare function isFreeEmail(emailOrDomain: string): boolean
|
|
10
|
+
export declare function isDisposableEmail(emailOrDomain: string, cache?: ICache | null): Promise<boolean>;
|
|
11
|
+
export declare function isFreeEmail(emailOrDomain: string, cache?: ICache | null): Promise<boolean>;
|
|
11
12
|
export declare const domainPorts: Record<string, number>;
|
|
12
13
|
/**
|
|
13
14
|
* Verify email address with basic result format (backward compatible)
|
package/dist/index.esm.js
CHANGED
|
@@ -4,6 +4,68 @@ import { promises } from 'node:dns';
|
|
|
4
4
|
import { stringSimilarity } from 'string-similarity-js';
|
|
5
5
|
import net from 'node:net';
|
|
6
6
|
|
|
7
|
+
class LRUAdapter {
|
|
8
|
+
constructor(maxSize = 1e3, ttlMs = 36e5) {
|
|
9
|
+
this.lru = lru(maxSize, ttlMs);
|
|
10
|
+
}
|
|
11
|
+
get(key) {
|
|
12
|
+
const value = this.lru.get(key);
|
|
13
|
+
return value === void 0 ? null : value;
|
|
14
|
+
}
|
|
15
|
+
async set(key, value, ttlMs) {
|
|
16
|
+
if (ttlMs !== void 0) {
|
|
17
|
+
this.lru.set(key, value);
|
|
18
|
+
} else {
|
|
19
|
+
this.lru.set(key, value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async delete(key) {
|
|
23
|
+
this.lru.delete(key);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
async has(key) {
|
|
27
|
+
return this.lru.has(key);
|
|
28
|
+
}
|
|
29
|
+
async clear() {
|
|
30
|
+
this.lru.clear();
|
|
31
|
+
}
|
|
32
|
+
size() {
|
|
33
|
+
return this.lru.size;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get the underlying LRU instance for advanced operations
|
|
37
|
+
*/
|
|
38
|
+
getLRU() {
|
|
39
|
+
return this.lru;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const DEFAULT_CACHE_TTL = {
|
|
44
|
+
mx: 36e5,
|
|
45
|
+
// 1 hour
|
|
46
|
+
disposable: 864e5,
|
|
47
|
+
// 24 hours
|
|
48
|
+
free: 864e5,
|
|
49
|
+
// 24 hours
|
|
50
|
+
domainValid: 864e5,
|
|
51
|
+
// 24 hours
|
|
52
|
+
smtp: 18e5,
|
|
53
|
+
// 30 minutes
|
|
54
|
+
domainSuggestion: 864e5,
|
|
55
|
+
// 24 hours
|
|
56
|
+
whois: 36e5
|
|
57
|
+
// 1 hour
|
|
58
|
+
};
|
|
59
|
+
const DEFAULT_CACHE_SIZE = {
|
|
60
|
+
mx: 500,
|
|
61
|
+
disposable: 1e3,
|
|
62
|
+
free: 1e3,
|
|
63
|
+
domainValid: 1e3,
|
|
64
|
+
smtp: 500,
|
|
65
|
+
domainSuggestion: 1e3,
|
|
66
|
+
whois: 200
|
|
67
|
+
};
|
|
68
|
+
|
|
7
69
|
const mxCache = lru(500, 36e5);
|
|
8
70
|
const disposableCache = lru(1e3, 864e5);
|
|
9
71
|
const freeCache = lru(1e3, 864e5);
|
|
@@ -11,6 +73,19 @@ const domainValidCache = lru(1e3, 864e5);
|
|
|
11
73
|
const smtpCache = lru(500, 18e5);
|
|
12
74
|
const domainSuggestionCache = lru(1e3, 864e5);
|
|
13
75
|
const whoisCache = lru(200, 36e5);
|
|
76
|
+
function getCacheStore(defaultLru, cacheType, passedCache) {
|
|
77
|
+
if (passedCache && passedCache[cacheType]) {
|
|
78
|
+
return passedCache[cacheType];
|
|
79
|
+
}
|
|
80
|
+
return new LRUAdapter(DEFAULT_CACHE_SIZE[cacheType], DEFAULT_CACHE_TTL[cacheType]);
|
|
81
|
+
}
|
|
82
|
+
const mxCacheStore = (passedCache) => getCacheStore(mxCache, "mx", passedCache);
|
|
83
|
+
const disposableCacheStore = (passedCache) => getCacheStore(disposableCache, "disposable", passedCache);
|
|
84
|
+
const freeCacheStore = (passedCache) => getCacheStore(freeCache, "free", passedCache);
|
|
85
|
+
const domainValidCacheStore = (passedCache) => getCacheStore(domainValidCache, "domainValid", passedCache);
|
|
86
|
+
const smtpCacheStore = (passedCache) => getCacheStore(smtpCache, "smtp", passedCache);
|
|
87
|
+
const domainSuggestionCacheStore = (passedCache) => getCacheStore(domainSuggestionCache, "domainSuggestion", passedCache);
|
|
88
|
+
const whoisCacheStore = (passedCache) => getCacheStore(whoisCache, "whois", passedCache);
|
|
14
89
|
function clearAllCaches() {
|
|
15
90
|
mxCache.clear();
|
|
16
91
|
disposableCache.clear();
|
|
@@ -21,9 +96,10 @@ function clearAllCaches() {
|
|
|
21
96
|
whoisCache.clear();
|
|
22
97
|
}
|
|
23
98
|
|
|
24
|
-
async function resolveMxRecords(domain) {
|
|
25
|
-
const
|
|
26
|
-
|
|
99
|
+
async function resolveMxRecords(domain, cache) {
|
|
100
|
+
const cacheStore = mxCacheStore(cache);
|
|
101
|
+
const cached = await cacheStore.get(domain);
|
|
102
|
+
if (cached !== null && cached !== void 0) {
|
|
27
103
|
return cached;
|
|
28
104
|
}
|
|
29
105
|
try {
|
|
@@ -38,10 +114,10 @@ async function resolveMxRecords(domain) {
|
|
|
38
114
|
return 0;
|
|
39
115
|
});
|
|
40
116
|
const exchanges = records.map((record) => record.exchange);
|
|
41
|
-
|
|
117
|
+
await cacheStore.set(domain, exchanges);
|
|
42
118
|
return exchanges;
|
|
43
119
|
} catch (error) {
|
|
44
|
-
|
|
120
|
+
await cacheStore.set(domain, []);
|
|
45
121
|
throw error;
|
|
46
122
|
}
|
|
47
123
|
}
|
|
@@ -131,18 +207,73 @@ function getSimilarityThreshold(domain) {
|
|
|
131
207
|
return 0.75;
|
|
132
208
|
}
|
|
133
209
|
function defaultDomainSuggestionMethod(domain, commonDomains) {
|
|
210
|
+
const domainsToCheck = commonDomains || COMMON_EMAIL_DOMAINS;
|
|
211
|
+
const lowerDomain = domain.toLowerCase();
|
|
212
|
+
if (domainsToCheck.includes(lowerDomain)) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
for (const [correctDomain, typos] of Object.entries(TYPO_PATTERNS)) {
|
|
216
|
+
if (typos.includes(lowerDomain)) {
|
|
217
|
+
return {
|
|
218
|
+
original: domain,
|
|
219
|
+
suggested: correctDomain,
|
|
220
|
+
confidence: 0.95
|
|
221
|
+
// High confidence for known typo patterns
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
let bestMatch = null;
|
|
226
|
+
const threshold = getSimilarityThreshold(lowerDomain);
|
|
227
|
+
for (const commonDomain of domainsToCheck) {
|
|
228
|
+
const similarity = stringSimilarity(lowerDomain, commonDomain.toLowerCase());
|
|
229
|
+
if (similarity >= threshold) {
|
|
230
|
+
if (!bestMatch || similarity > bestMatch.similarity) {
|
|
231
|
+
bestMatch = { domain: commonDomain, similarity };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!bestMatch) {
|
|
236
|
+
for (const commonDomain of domainsToCheck) {
|
|
237
|
+
if (Math.abs(lowerDomain.length - commonDomain.length) <= 2) {
|
|
238
|
+
const similarity = stringSimilarity(lowerDomain, commonDomain.toLowerCase());
|
|
239
|
+
if (similarity >= 0.7) {
|
|
240
|
+
if (!bestMatch || similarity > bestMatch.similarity) {
|
|
241
|
+
bestMatch = { domain: commonDomain, similarity };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (bestMatch) {
|
|
248
|
+
if (bestMatch.domain.charAt(0) !== lowerDomain.charAt(0) && bestMatch.similarity < 0.9) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
original: domain,
|
|
253
|
+
suggested: bestMatch.domain,
|
|
254
|
+
confidence: bestMatch.similarity
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
async function defaultDomainSuggestionMethodAsync(domain, commonDomains) {
|
|
260
|
+
return defaultDomainSuggestionMethodImpl(domain, commonDomains);
|
|
261
|
+
}
|
|
262
|
+
async function defaultDomainSuggestionMethodImpl(domain, commonDomains) {
|
|
134
263
|
if (!domain || domain.length < 3) {
|
|
135
264
|
return null;
|
|
136
265
|
}
|
|
137
266
|
const domainsToCheck = commonDomains || COMMON_EMAIL_DOMAINS;
|
|
138
267
|
const lowerDomain = domain.toLowerCase();
|
|
139
268
|
const cacheKey = `${lowerDomain}:${domainsToCheck.length}`;
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
269
|
+
const cache = domainSuggestionCacheStore();
|
|
270
|
+
const cached = cache.get(cacheKey);
|
|
271
|
+
const resolved = cached && typeof cached === "object" && "then" in cached ? await cached : cached;
|
|
272
|
+
if (resolved !== null && resolved !== void 0) {
|
|
273
|
+
return resolved ? { original: domain, suggested: resolved.suggested, confidence: resolved.confidence } : null;
|
|
143
274
|
}
|
|
144
275
|
if (domainsToCheck.includes(lowerDomain)) {
|
|
145
|
-
|
|
276
|
+
await cache.set(cacheKey, null);
|
|
146
277
|
return null;
|
|
147
278
|
}
|
|
148
279
|
for (const [correctDomain, typos] of Object.entries(TYPO_PATTERNS)) {
|
|
@@ -153,7 +284,7 @@ function defaultDomainSuggestionMethod(domain, commonDomains) {
|
|
|
153
284
|
confidence: 0.95
|
|
154
285
|
// High confidence for known typo patterns
|
|
155
286
|
};
|
|
156
|
-
|
|
287
|
+
await cache.set(cacheKey, { suggested: result.suggested, confidence: result.confidence });
|
|
157
288
|
return result;
|
|
158
289
|
}
|
|
159
290
|
}
|
|
@@ -181,7 +312,7 @@ function defaultDomainSuggestionMethod(domain, commonDomains) {
|
|
|
181
312
|
}
|
|
182
313
|
if (bestMatch) {
|
|
183
314
|
if (bestMatch.domain.charAt(0) !== lowerDomain.charAt(0) && bestMatch.similarity < 0.9) {
|
|
184
|
-
|
|
315
|
+
await cache.set(cacheKey, null);
|
|
185
316
|
return null;
|
|
186
317
|
}
|
|
187
318
|
const result = {
|
|
@@ -189,10 +320,10 @@ function defaultDomainSuggestionMethod(domain, commonDomains) {
|
|
|
189
320
|
suggested: bestMatch.domain,
|
|
190
321
|
confidence: bestMatch.similarity
|
|
191
322
|
};
|
|
192
|
-
|
|
323
|
+
await cache.set(cacheKey, { suggested: result.suggested, confidence: result.confidence });
|
|
193
324
|
return result;
|
|
194
325
|
}
|
|
195
|
-
|
|
326
|
+
await cache.set(cacheKey, null);
|
|
196
327
|
return null;
|
|
197
328
|
}
|
|
198
329
|
function suggestDomain(params) {
|
|
@@ -209,7 +340,7 @@ function suggestDomain(params) {
|
|
|
209
340
|
}
|
|
210
341
|
return defaultDomainSuggestionMethod(domain, commonDomains);
|
|
211
342
|
}
|
|
212
|
-
function suggestEmailDomain(email, commonDomains) {
|
|
343
|
+
async function suggestEmailDomain(email, commonDomains) {
|
|
213
344
|
if (!email || !email.includes("@")) {
|
|
214
345
|
return null;
|
|
215
346
|
}
|
|
@@ -217,7 +348,7 @@ function suggestEmailDomain(email, commonDomains) {
|
|
|
217
348
|
if (!domain || !localPart) {
|
|
218
349
|
return null;
|
|
219
350
|
}
|
|
220
|
-
const suggestion =
|
|
351
|
+
const suggestion = await defaultDomainSuggestionMethodAsync(domain, commonDomains);
|
|
221
352
|
if (suggestion) {
|
|
222
353
|
return {
|
|
223
354
|
original: email,
|
|
@@ -788,7 +919,11 @@ function defaultNameDetectionMethod(email) {
|
|
|
788
919
|
const lastNameScore = getLastNameScore(lastParsed.cleaned);
|
|
789
920
|
const reverseScore = getLastNameScore(firstParsed.cleaned) + getFirstNameScore(lastParsed.cleaned);
|
|
790
921
|
const normalScore = firstNameScore + lastNameScore;
|
|
791
|
-
const
|
|
922
|
+
const firstLikely = isLikelyName(first, true, true);
|
|
923
|
+
const lastLikely = isLikelyName(last, true, true);
|
|
924
|
+
const oneIsSingleLetter = first.length === 1 || last.length === 1;
|
|
925
|
+
const otherIsLongEnough = oneIsSingleLetter ? first.length === 1 ? last.length >= 2 : first.length >= 2 : true;
|
|
926
|
+
const bothPartsValid = firstLikely && lastLikely && otherIsLongEnough;
|
|
792
927
|
if (bothPartsValid) {
|
|
793
928
|
const useReversed = reverseScore > normalScore * 1.2;
|
|
794
929
|
if (firstParsed.hasNumbers || lastParsed.hasNumbers) {
|
|
@@ -834,7 +969,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
834
969
|
const lastParsed = parseCompositeNamePart(last);
|
|
835
970
|
const isLastSuffix = COMMON_NAME_SUFFIXES.includes(last.toLowerCase()) || CONTEXTUAL_SUFFIXES.includes(last.toLowerCase()) || isYearLike(last);
|
|
836
971
|
if (isLastSuffix) {
|
|
837
|
-
if (isLikelyName(first, true) && isLikelyName(middle, true)) {
|
|
972
|
+
if (isLikelyName(first, true, true) && isLikelyName(middle, true, true)) {
|
|
838
973
|
const cleanedFirst = firstParsed.hasNumbers ? firstParsed.cleaned : first;
|
|
839
974
|
const cleanedMiddle = middleParsed.hasNumbers ? middleParsed.cleaned : middle;
|
|
840
975
|
if (isLikelyName(cleanedFirst, false, true) && isLikelyName(cleanedMiddle, false, true)) {
|
|
@@ -848,7 +983,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
848
983
|
}
|
|
849
984
|
break;
|
|
850
985
|
}
|
|
851
|
-
} else if (isLikelyName(first, true) && isLikelyName(last, true)) {
|
|
986
|
+
} else if (isLikelyName(first, true, true) && isLikelyName(last, true, true)) {
|
|
852
987
|
const cleanedFirst = firstParsed.hasNumbers ? firstParsed.cleaned : first;
|
|
853
988
|
const cleanedLast = lastParsed.hasNumbers ? lastParsed.cleaned : last;
|
|
854
989
|
if (isLikelyName(cleanedFirst, false, true) && isLikelyName(cleanedLast, false, true)) {
|
|
@@ -868,7 +1003,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
868
1003
|
const isLastPartSuffix = COMMON_NAME_SUFFIXES.includes(lastPartLower) || CONTEXTUAL_SUFFIXES.includes(lastPartLower) || isYearLike(parts[parts.length - 1]);
|
|
869
1004
|
const effectiveLastIndex = isLastPartSuffix ? parts.length - 2 : parts.length - 1;
|
|
870
1005
|
const lastToUse = effectiveLastIndex >= 0 ? parts[effectiveLastIndex] : null;
|
|
871
|
-
if (lastToUse && isLikelyName(firstPart, true) && isLikelyName(lastToUse, true)) {
|
|
1006
|
+
if (lastToUse && isLikelyName(firstPart, true, true) && isLikelyName(lastToUse, true, true)) {
|
|
872
1007
|
const firstParsed = parseCompositeNamePart(firstPart);
|
|
873
1008
|
const lastParsed = parseCompositeNamePart(lastToUse);
|
|
874
1009
|
const cleanedFirst = firstParsed.hasNumbers ? firstParsed.cleaned : firstPart;
|
|
@@ -904,7 +1039,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
904
1039
|
}
|
|
905
1040
|
if (!firstName && !lastName) {
|
|
906
1041
|
const parsed = parseCompositeNamePart(cleanedLocal);
|
|
907
|
-
if (isLikelyName(cleanedLocal, true)) {
|
|
1042
|
+
if (isLikelyName(cleanedLocal, true, false)) {
|
|
908
1043
|
if (/^[a-zA-Z]+$/.test(cleanedLocal)) {
|
|
909
1044
|
const nameScore = Math.max(getFirstNameScore(cleanedLocal), getLastNameScore(cleanedLocal));
|
|
910
1045
|
if (getFirstNameScore(cleanedLocal) >= getLastNameScore(cleanedLocal)) {
|
|
@@ -1085,7 +1220,7 @@ var VerificationErrorCode;
|
|
|
1085
1220
|
VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
|
|
1086
1221
|
})(VerificationErrorCode || (VerificationErrorCode = {}));
|
|
1087
1222
|
|
|
1088
|
-
function isValidEmailDomain(emailOrDomain) {
|
|
1223
|
+
async function isValidEmailDomain(emailOrDomain, cache) {
|
|
1089
1224
|
let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
|
|
1090
1225
|
if (!emailDomain) {
|
|
1091
1226
|
emailDomain = _;
|
|
@@ -1093,16 +1228,17 @@ function isValidEmailDomain(emailOrDomain) {
|
|
|
1093
1228
|
if (!emailDomain) {
|
|
1094
1229
|
return false;
|
|
1095
1230
|
}
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1231
|
+
const cacheStore = domainValidCacheStore(cache);
|
|
1232
|
+
const cached = await cacheStore.get(emailDomain);
|
|
1233
|
+
if (cached !== null && cached !== void 0) {
|
|
1098
1234
|
return cached;
|
|
1099
1235
|
}
|
|
1100
1236
|
try {
|
|
1101
1237
|
const result = isValid(emailDomain) || false;
|
|
1102
|
-
|
|
1238
|
+
await cacheStore.set(emailDomain, result);
|
|
1103
1239
|
return result;
|
|
1104
1240
|
} catch (_e) {
|
|
1105
|
-
|
|
1241
|
+
await cacheStore.set(emailDomain, false);
|
|
1106
1242
|
return false;
|
|
1107
1243
|
}
|
|
1108
1244
|
}
|
|
@@ -1624,8 +1760,9 @@ function queryWhoisServer(domain, server, timeout = 5e3) {
|
|
|
1624
1760
|
async function getWhoisData(domain, timeout = 5e3) {
|
|
1625
1761
|
var _a;
|
|
1626
1762
|
const cacheKey = `whois:${domain}`;
|
|
1627
|
-
const
|
|
1628
|
-
|
|
1763
|
+
const cache = whoisCacheStore();
|
|
1764
|
+
const cached = await cache.get(cacheKey);
|
|
1765
|
+
if (cached !== null && cached !== void 0) {
|
|
1629
1766
|
return cached;
|
|
1630
1767
|
}
|
|
1631
1768
|
try {
|
|
@@ -1642,16 +1779,16 @@ async function getWhoisData(domain, timeout = 5e3) {
|
|
|
1642
1779
|
const referredServer = referMatch[1];
|
|
1643
1780
|
const whoisResponse2 = await queryWhoisServer(domain, referredServer, timeout);
|
|
1644
1781
|
const whoisData3 = parseWhoisData({ rawData: whoisResponse2, domain });
|
|
1645
|
-
|
|
1782
|
+
await cache.set(cacheKey, whoisData3);
|
|
1646
1783
|
return whoisData3;
|
|
1647
1784
|
}
|
|
1648
1785
|
const whoisData2 = parseWhoisData({ rawData: ianaResponse, domain });
|
|
1649
|
-
|
|
1786
|
+
await cache.set(cacheKey, whoisData2);
|
|
1650
1787
|
return whoisData2;
|
|
1651
1788
|
}
|
|
1652
1789
|
const whoisResponse = await queryWhoisServer(domain, whoisServer, timeout);
|
|
1653
1790
|
const whoisData = parseWhoisData({ rawData: whoisResponse, domain });
|
|
1654
|
-
|
|
1791
|
+
await cache.set(cacheKey, whoisData);
|
|
1655
1792
|
return whoisData;
|
|
1656
1793
|
} catch (_error) {
|
|
1657
1794
|
return null;
|
|
@@ -1752,7 +1889,7 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3) {
|
|
|
1752
1889
|
}
|
|
1753
1890
|
|
|
1754
1891
|
async function verifyEmailBatch(params) {
|
|
1755
|
-
const { emailAddresses, concurrency = 5, timeout = 4e3, verifyMx = true, verifySmtp = false, checkDisposable = true, checkFree = true,
|
|
1892
|
+
const { emailAddresses, concurrency = 5, timeout = 4e3, verifyMx = true, verifySmtp = false, checkDisposable = true, checkFree = true, detectName = false, nameDetectionMethod, suggestDomain = false, domainSuggestionMethod, commonDomains, cache } = params;
|
|
1756
1893
|
const startTime = Date.now();
|
|
1757
1894
|
const results = /* @__PURE__ */ new Map();
|
|
1758
1895
|
const batches = [];
|
|
@@ -1765,7 +1902,7 @@ async function verifyEmailBatch(params) {
|
|
|
1765
1902
|
for (const batch of batches) {
|
|
1766
1903
|
const batchPromises = batch.map(async (email) => {
|
|
1767
1904
|
try {
|
|
1768
|
-
const result =
|
|
1905
|
+
const result = await verifyEmailDetailed({
|
|
1769
1906
|
emailAddress: email,
|
|
1770
1907
|
timeout,
|
|
1771
1908
|
verifyMx,
|
|
@@ -1776,39 +1913,20 @@ async function verifyEmailBatch(params) {
|
|
|
1776
1913
|
nameDetectionMethod,
|
|
1777
1914
|
suggestDomain,
|
|
1778
1915
|
domainSuggestionMethod,
|
|
1779
|
-
commonDomains
|
|
1780
|
-
|
|
1781
|
-
emailAddress: email,
|
|
1782
|
-
timeout,
|
|
1783
|
-
verifyMx,
|
|
1784
|
-
verifySmtp,
|
|
1785
|
-
detectName,
|
|
1786
|
-
nameDetectionMethod,
|
|
1787
|
-
suggestDomain,
|
|
1788
|
-
domainSuggestionMethod,
|
|
1789
|
-
commonDomains
|
|
1916
|
+
commonDomains,
|
|
1917
|
+
cache
|
|
1790
1918
|
});
|
|
1791
|
-
if (
|
|
1792
|
-
|
|
1793
|
-
if (detailedResult.valid) {
|
|
1794
|
-
totalValid++;
|
|
1795
|
-
} else {
|
|
1796
|
-
totalInvalid++;
|
|
1797
|
-
}
|
|
1919
|
+
if (result.valid) {
|
|
1920
|
+
totalValid++;
|
|
1798
1921
|
} else {
|
|
1799
|
-
|
|
1800
|
-
if (basicResult.validFormat && basicResult.validMx !== false) {
|
|
1801
|
-
totalValid++;
|
|
1802
|
-
} else {
|
|
1803
|
-
totalInvalid++;
|
|
1804
|
-
}
|
|
1922
|
+
totalInvalid++;
|
|
1805
1923
|
}
|
|
1806
1924
|
return { email, result };
|
|
1807
1925
|
} catch (error) {
|
|
1808
1926
|
totalErrors++;
|
|
1809
1927
|
return {
|
|
1810
1928
|
email,
|
|
1811
|
-
result:
|
|
1929
|
+
result: createErrorDetailedResult(email)
|
|
1812
1930
|
};
|
|
1813
1931
|
}
|
|
1814
1932
|
});
|
|
@@ -1846,38 +1964,56 @@ function createErrorDetailedResult(email, _error) {
|
|
|
1846
1964
|
|
|
1847
1965
|
let disposableEmailProviders;
|
|
1848
1966
|
let freeEmailProviders;
|
|
1849
|
-
function isDisposableEmail(emailOrDomain) {
|
|
1967
|
+
async function isDisposableEmail(emailOrDomain, cache) {
|
|
1850
1968
|
const parts = emailOrDomain.split("@");
|
|
1851
1969
|
const emailDomain = parts.length > 1 ? parts[1] : parts[0];
|
|
1852
1970
|
if (!emailDomain) {
|
|
1853
1971
|
return false;
|
|
1854
1972
|
}
|
|
1855
|
-
const
|
|
1856
|
-
|
|
1973
|
+
const cacheStore = disposableCacheStore(cache);
|
|
1974
|
+
let cached;
|
|
1975
|
+
try {
|
|
1976
|
+
cached = await cacheStore.get(emailDomain);
|
|
1977
|
+
} catch (_error) {
|
|
1978
|
+
cached = null;
|
|
1979
|
+
}
|
|
1980
|
+
if (cached !== null && cached !== void 0) {
|
|
1857
1981
|
return cached;
|
|
1858
1982
|
}
|
|
1859
1983
|
if (!disposableEmailProviders) {
|
|
1860
1984
|
disposableEmailProviders = new Set(require("./disposable-email-providers.json"));
|
|
1861
1985
|
}
|
|
1862
1986
|
const result = disposableEmailProviders.has(emailDomain);
|
|
1863
|
-
|
|
1987
|
+
try {
|
|
1988
|
+
await cacheStore.set(emailDomain, result);
|
|
1989
|
+
} catch (_error) {
|
|
1990
|
+
}
|
|
1864
1991
|
return result;
|
|
1865
1992
|
}
|
|
1866
|
-
function isFreeEmail(emailOrDomain) {
|
|
1993
|
+
async function isFreeEmail(emailOrDomain, cache) {
|
|
1867
1994
|
const parts = emailOrDomain.split("@");
|
|
1868
1995
|
const emailDomain = parts.length > 1 ? parts[1] : parts[0];
|
|
1869
1996
|
if (!emailDomain) {
|
|
1870
1997
|
return false;
|
|
1871
1998
|
}
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1999
|
+
const cacheStore = freeCacheStore(cache);
|
|
2000
|
+
let cached;
|
|
2001
|
+
try {
|
|
2002
|
+
cached = await cacheStore.get(emailDomain);
|
|
2003
|
+
} catch (_error) {
|
|
2004
|
+
cached = null;
|
|
2005
|
+
}
|
|
2006
|
+
if (cached !== null && cached !== void 0) {
|
|
1874
2007
|
return cached;
|
|
1875
2008
|
}
|
|
1876
2009
|
if (!freeEmailProviders) {
|
|
1877
2010
|
freeEmailProviders = new Set(require("./free-email-providers.json"));
|
|
1878
2011
|
}
|
|
1879
2012
|
const result = freeEmailProviders.has(emailDomain);
|
|
1880
|
-
|
|
2013
|
+
try {
|
|
2014
|
+
await cacheStore.set(emailDomain, result);
|
|
2015
|
+
} catch (_error) {
|
|
2016
|
+
}
|
|
1881
2017
|
return result;
|
|
1882
2018
|
}
|
|
1883
2019
|
const domainPorts = {
|
|
@@ -1910,7 +2046,7 @@ async function verifyEmail(params) {
|
|
|
1910
2046
|
if (suggestDomain2) {
|
|
1911
2047
|
const [, emailDomain] = emailAddress.split("@");
|
|
1912
2048
|
if (emailDomain) {
|
|
1913
|
-
result.domainSuggestion = domainSuggestionMethod ? domainSuggestionMethod(emailDomain) : suggestEmailDomain(emailAddress, commonDomains);
|
|
2049
|
+
result.domainSuggestion = domainSuggestionMethod ? domainSuggestionMethod(emailDomain) : await suggestEmailDomain(emailAddress, commonDomains);
|
|
1914
2050
|
}
|
|
1915
2051
|
}
|
|
1916
2052
|
if (checkDomainAge) {
|
|
@@ -1932,7 +2068,7 @@ async function verifyEmail(params) {
|
|
|
1932
2068
|
if (!verifyMx && !verifySmtp)
|
|
1933
2069
|
return result;
|
|
1934
2070
|
try {
|
|
1935
|
-
mxRecords = await resolveMxRecords(domain);
|
|
2071
|
+
mxRecords = await resolveMxRecords(domain, params.cache);
|
|
1936
2072
|
log("[verifyEmail] Found MX records", mxRecords);
|
|
1937
2073
|
} catch (err) {
|
|
1938
2074
|
log("[verifyEmail] Failed to resolve MX records", err);
|
|
@@ -1946,8 +2082,9 @@ async function verifyEmail(params) {
|
|
|
1946
2082
|
}
|
|
1947
2083
|
if (verifySmtp && (mxRecords === null || mxRecords === void 0 ? void 0 : mxRecords.length) > 0) {
|
|
1948
2084
|
const cacheKey = `${emailAddress}:smtp`;
|
|
1949
|
-
const
|
|
1950
|
-
|
|
2085
|
+
const smtpCacheInstance = smtpCacheStore(params.cache);
|
|
2086
|
+
const cachedSmtp = await smtpCacheInstance.get(cacheKey);
|
|
2087
|
+
if (cachedSmtp !== null && cachedSmtp !== void 0) {
|
|
1951
2088
|
result.validSmtp = cachedSmtp;
|
|
1952
2089
|
if (detectName2 && !result.detectedName) {
|
|
1953
2090
|
result.detectedName = detectNameFromEmail({
|
|
@@ -1977,7 +2114,7 @@ async function verifyEmail(params) {
|
|
|
1977
2114
|
port: domainPort,
|
|
1978
2115
|
retryAttempts: params.retryAttempts
|
|
1979
2116
|
});
|
|
1980
|
-
|
|
2117
|
+
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
1981
2118
|
result.validSmtp = smtpResult;
|
|
1982
2119
|
}
|
|
1983
2120
|
return result;
|
|
@@ -2017,7 +2154,7 @@ async function verifyEmailDetailed(params) {
|
|
|
2017
2154
|
if (suggestDomain2) {
|
|
2018
2155
|
const [, emailDomain] = emailAddress.split("@");
|
|
2019
2156
|
if (emailDomain) {
|
|
2020
|
-
result.domainSuggestion = domainSuggestionMethod ? domainSuggestionMethod(emailDomain) : suggestEmailDomain(emailAddress, commonDomains);
|
|
2157
|
+
result.domainSuggestion = domainSuggestionMethod ? domainSuggestionMethod(emailDomain) : await suggestEmailDomain(emailAddress, commonDomains);
|
|
2021
2158
|
}
|
|
2022
2159
|
}
|
|
2023
2160
|
const [local, domain] = emailAddress.split("@");
|
|
@@ -2028,7 +2165,7 @@ async function verifyEmailDetailed(params) {
|
|
|
2028
2165
|
}
|
|
2029
2166
|
return result;
|
|
2030
2167
|
}
|
|
2031
|
-
if (!isValidEmailDomain(domain)) {
|
|
2168
|
+
if (!await isValidEmailDomain(domain, params.cache)) {
|
|
2032
2169
|
result.domain.error = VerificationErrorCode.INVALID_DOMAIN;
|
|
2033
2170
|
if (result.metadata) {
|
|
2034
2171
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
@@ -2036,14 +2173,14 @@ async function verifyEmailDetailed(params) {
|
|
|
2036
2173
|
return result;
|
|
2037
2174
|
}
|
|
2038
2175
|
if (checkDisposable) {
|
|
2039
|
-
result.disposable = isDisposableEmail(emailAddress);
|
|
2176
|
+
result.disposable = await isDisposableEmail(emailAddress, params.cache);
|
|
2040
2177
|
if (result.disposable) {
|
|
2041
2178
|
result.valid = false;
|
|
2042
2179
|
result.domain.error = VerificationErrorCode.DISPOSABLE_EMAIL;
|
|
2043
2180
|
}
|
|
2044
2181
|
}
|
|
2045
2182
|
if (checkFree) {
|
|
2046
|
-
result.freeProvider = isFreeEmail(emailAddress);
|
|
2183
|
+
result.freeProvider = await isFreeEmail(emailAddress, params.cache);
|
|
2047
2184
|
}
|
|
2048
2185
|
if (checkDomainAge) {
|
|
2049
2186
|
try {
|
|
@@ -2063,7 +2200,7 @@ async function verifyEmailDetailed(params) {
|
|
|
2063
2200
|
}
|
|
2064
2201
|
if (verifyMx || verifySmtp) {
|
|
2065
2202
|
try {
|
|
2066
|
-
const mxRecords = await resolveMxRecords(domain);
|
|
2203
|
+
const mxRecords = await resolveMxRecords(domain, params.cache);
|
|
2067
2204
|
result.domain.mxRecords = mxRecords;
|
|
2068
2205
|
result.domain.valid = mxRecords.length > 0;
|
|
2069
2206
|
if (!result.domain.valid) {
|
|
@@ -2071,8 +2208,9 @@ async function verifyEmailDetailed(params) {
|
|
|
2071
2208
|
}
|
|
2072
2209
|
if (verifySmtp && mxRecords.length > 0) {
|
|
2073
2210
|
const cacheKey = `${emailAddress}:smtp`;
|
|
2074
|
-
const
|
|
2075
|
-
|
|
2211
|
+
const smtpCacheInstance = smtpCacheStore(params.cache);
|
|
2212
|
+
const cachedSmtp = await smtpCacheInstance.get(cacheKey);
|
|
2213
|
+
if (cachedSmtp !== null && cachedSmtp !== void 0) {
|
|
2076
2214
|
result.smtp.valid = cachedSmtp;
|
|
2077
2215
|
if (result.metadata) {
|
|
2078
2216
|
result.metadata.cached = true;
|
|
@@ -2100,7 +2238,7 @@ async function verifyEmailDetailed(params) {
|
|
|
2100
2238
|
port: domainPort,
|
|
2101
2239
|
retryAttempts: params.retryAttempts
|
|
2102
2240
|
});
|
|
2103
|
-
|
|
2241
|
+
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2104
2242
|
result.smtp.valid = smtpResult;
|
|
2105
2243
|
}
|
|
2106
2244
|
if (result.smtp.valid === false) {
|