@emailcheck/email-validator-js 2.13.1-beta.3 → 2.13.1-beta.5
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/dist/dns.d.ts +2 -2
- package/dist/index.d.ts +5 -4
- package/dist/index.esm.js +155 -7
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +156 -6
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +24 -0
- package/package.json +1 -1
package/dist/dns.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function resolveMxRecords(
|
|
1
|
+
import type { IResolveMxParams } from './types';
|
|
2
|
+
export declare function resolveMxRecords(params: IResolveMxParams): Promise<string[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
1
|
+
import { type IDisposableEmailParams, type IFreeEmailParams, type IVerifyEmailParams, type VerificationResult } from './types';
|
|
2
|
+
export * from './adapters/lru-adapter';
|
|
3
|
+
export * from './adapters/redis-adapter';
|
|
3
4
|
export { verifyEmailBatch } from './batch';
|
|
4
5
|
export { clearAllCaches } from './cache';
|
|
5
6
|
export { COMMON_EMAIL_DOMAINS, defaultDomainSuggestionMethod, getDomainSimilarity, isCommonDomain, suggestDomain, suggestEmailDomain, } from './domain-suggester';
|
|
@@ -7,8 +8,8 @@ export { defaultNameDetectionMethod, detectName, detectNameFromEmail } from './n
|
|
|
7
8
|
export * from './types';
|
|
8
9
|
export { isValidEmail, isValidEmailDomain } from './validator';
|
|
9
10
|
export { getDomainAge, getDomainRegistrationStatus } from './whois';
|
|
10
|
-
export declare function isDisposableEmail(
|
|
11
|
-
export declare function isFreeEmail(
|
|
11
|
+
export declare function isDisposableEmail(params: IDisposableEmailParams): Promise<boolean>;
|
|
12
|
+
export declare function isFreeEmail(params: IFreeEmailParams): Promise<boolean>;
|
|
12
13
|
export declare const domainPorts: Record<string, number>;
|
|
13
14
|
/**
|
|
14
15
|
* Verify email address
|
package/dist/index.esm.js
CHANGED
|
@@ -96,12 +96,17 @@ function clearAllCaches() {
|
|
|
96
96
|
whoisCache.clear();
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
async function resolveMxRecords(
|
|
99
|
+
async function resolveMxRecords(params) {
|
|
100
|
+
const { domain, cache, logger } = params;
|
|
101
|
+
const log = logger || (() => {
|
|
102
|
+
});
|
|
100
103
|
const cacheStore = mxCacheStore(cache);
|
|
101
104
|
const cached = await cacheStore.get(domain);
|
|
102
105
|
if (cached !== null && cached !== void 0) {
|
|
106
|
+
log(`[resolveMxRecords] Cache hit for ${domain}: ${cached.length} MX records`);
|
|
103
107
|
return cached;
|
|
104
108
|
}
|
|
109
|
+
log(`[resolveMxRecords] Performing DNS MX lookup for ${domain}`);
|
|
105
110
|
try {
|
|
106
111
|
const records = await promises.resolveMx(domain);
|
|
107
112
|
records.sort((a, b) => {
|
|
@@ -114,9 +119,12 @@ async function resolveMxRecords(domain, cache) {
|
|
|
114
119
|
return 0;
|
|
115
120
|
});
|
|
116
121
|
const exchanges = records.map((record) => record.exchange);
|
|
122
|
+
log(`[resolveMxRecords] Found ${exchanges.length} MX records for ${domain}: [${exchanges.join(", ")}]`);
|
|
117
123
|
await cacheStore.set(domain, exchanges);
|
|
124
|
+
log(`[resolveMxRecords] Cached ${exchanges.length} MX records for ${domain}`);
|
|
118
125
|
return exchanges;
|
|
119
126
|
} catch (error) {
|
|
127
|
+
log(`[resolveMxRecords] MX lookup failed for ${domain}, caching empty result`);
|
|
120
128
|
await cacheStore.set(domain, []);
|
|
121
129
|
throw error;
|
|
122
130
|
}
|
|
@@ -1926,6 +1934,106 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3, debug = false)
|
|
|
1926
1934
|
}
|
|
1927
1935
|
}
|
|
1928
1936
|
|
|
1937
|
+
class RedisAdapter {
|
|
1938
|
+
constructor(redis, options = {}) {
|
|
1939
|
+
this.redis = redis;
|
|
1940
|
+
this.keyPrefix = options.keyPrefix || "email_validator:";
|
|
1941
|
+
this.defaultTtlMs = options.defaultTtlMs || 36e5;
|
|
1942
|
+
this.jsonSerializer = options.jsonSerializer || {
|
|
1943
|
+
stringify: (value) => {
|
|
1944
|
+
const processed = this.processDatesForSerialization(value);
|
|
1945
|
+
return JSON.stringify(processed);
|
|
1946
|
+
},
|
|
1947
|
+
parse: (value) => JSON.parse(value, (key, v) => {
|
|
1948
|
+
if (v && typeof v === "object" && v.__type === "Date") {
|
|
1949
|
+
return new Date(v.value);
|
|
1950
|
+
}
|
|
1951
|
+
return v;
|
|
1952
|
+
})
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
getKey(key) {
|
|
1956
|
+
return `${this.keyPrefix}${key}`;
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* Recursively process an object to convert Date instances to a serializable format
|
|
1960
|
+
*/
|
|
1961
|
+
processDatesForSerialization(obj) {
|
|
1962
|
+
if (obj instanceof Date) {
|
|
1963
|
+
return { __type: "Date", value: obj.toISOString() };
|
|
1964
|
+
}
|
|
1965
|
+
if (obj && typeof obj === "object") {
|
|
1966
|
+
if (Array.isArray(obj)) {
|
|
1967
|
+
return obj.map((item) => this.processDatesForSerialization(item));
|
|
1968
|
+
}
|
|
1969
|
+
const result = {};
|
|
1970
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1971
|
+
result[key] = this.processDatesForSerialization(value);
|
|
1972
|
+
}
|
|
1973
|
+
return result;
|
|
1974
|
+
}
|
|
1975
|
+
return obj;
|
|
1976
|
+
}
|
|
1977
|
+
async get(key) {
|
|
1978
|
+
try {
|
|
1979
|
+
const value = await this.redis.get(this.getKey(key));
|
|
1980
|
+
if (value === null) {
|
|
1981
|
+
return null;
|
|
1982
|
+
}
|
|
1983
|
+
return this.jsonSerializer.parse(value);
|
|
1984
|
+
} catch (error) {
|
|
1985
|
+
console.error("Redis get error:", error);
|
|
1986
|
+
return null;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
async set(key, value, ttlMs) {
|
|
1990
|
+
try {
|
|
1991
|
+
const serializedValue = this.jsonSerializer.stringify(value);
|
|
1992
|
+
const ttl = ttlMs || this.defaultTtlMs;
|
|
1993
|
+
const ttlSeconds = Math.ceil(ttl / 1e3);
|
|
1994
|
+
await this.redis.set(this.getKey(key), serializedValue, "EX", ttlSeconds);
|
|
1995
|
+
} catch (error) {
|
|
1996
|
+
console.error("Redis set error:", error);
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
async delete(key) {
|
|
2000
|
+
try {
|
|
2001
|
+
const result = await this.redis.del(this.getKey(key));
|
|
2002
|
+
return result > 0;
|
|
2003
|
+
} catch (error) {
|
|
2004
|
+
console.error("Redis delete error:", error);
|
|
2005
|
+
return false;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
async has(key) {
|
|
2009
|
+
try {
|
|
2010
|
+
const result = await this.redis.exists(this.getKey(key));
|
|
2011
|
+
return result > 0;
|
|
2012
|
+
} catch (error) {
|
|
2013
|
+
console.error("Redis exists error:", error);
|
|
2014
|
+
return false;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
async clear() {
|
|
2018
|
+
try {
|
|
2019
|
+
await this.redis.flushdb();
|
|
2020
|
+
} catch (error) {
|
|
2021
|
+
console.error("Redis clear error:", error);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
// size() is not applicable for Redis as it's a distributed store
|
|
2025
|
+
size() {
|
|
2026
|
+
return void 0;
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* Helper method to delete only keys with the configured prefix
|
|
2030
|
+
* Requires Redis SCAN command which might not be available in all Redis clients
|
|
2031
|
+
*/
|
|
2032
|
+
async clearPrefixed() {
|
|
2033
|
+
console.warn("clearPrefixed not implemented. Use clear() to flush the entire database.");
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
|
|
1929
2037
|
async function verifyEmailBatch(params) {
|
|
1930
2038
|
const { emailAddresses, concurrency = 5, timeout = 4e3, verifyMx = true, verifySmtp = false, checkDisposable = true, checkFree = true, detectName = false, nameDetectionMethod, suggestDomain = false, domainSuggestionMethod, commonDomains, skipMxForDisposable = false, skipDomainWhoisForDisposable = false, cache } = params;
|
|
1931
2039
|
const startTime = Date.now();
|
|
@@ -2004,7 +2112,10 @@ function createErrorResult(email, _error) {
|
|
|
2004
2112
|
|
|
2005
2113
|
let disposableEmailProviders;
|
|
2006
2114
|
let freeEmailProviders;
|
|
2007
|
-
async function isDisposableEmail(
|
|
2115
|
+
async function isDisposableEmail(params) {
|
|
2116
|
+
const { emailOrDomain, cache, logger } = params;
|
|
2117
|
+
const log = logger || (() => {
|
|
2118
|
+
});
|
|
2008
2119
|
const parts = emailOrDomain.split("@");
|
|
2009
2120
|
const emailDomain = parts.length > 1 ? parts[1] : parts[0];
|
|
2010
2121
|
if (!emailDomain) {
|
|
@@ -2018,6 +2129,7 @@ async function isDisposableEmail(emailOrDomain, cache) {
|
|
|
2018
2129
|
cached = null;
|
|
2019
2130
|
}
|
|
2020
2131
|
if (cached !== null && cached !== void 0) {
|
|
2132
|
+
log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached}`);
|
|
2021
2133
|
return cached;
|
|
2022
2134
|
}
|
|
2023
2135
|
if (!disposableEmailProviders) {
|
|
@@ -2026,11 +2138,17 @@ async function isDisposableEmail(emailOrDomain, cache) {
|
|
|
2026
2138
|
const result = disposableEmailProviders.has(emailDomain);
|
|
2027
2139
|
try {
|
|
2028
2140
|
await cacheStore.set(emailDomain, result);
|
|
2141
|
+
log(`[isDisposableEmail] Cached result for ${emailDomain}: ${result}`);
|
|
2029
2142
|
} catch (_error) {
|
|
2143
|
+
log(`[isDisposableEmail] Cache write error for ${emailDomain}`);
|
|
2030
2144
|
}
|
|
2145
|
+
log(`[isDisposableEmail] Check result for ${emailDomain}: ${result}`);
|
|
2031
2146
|
return result;
|
|
2032
2147
|
}
|
|
2033
|
-
async function isFreeEmail(
|
|
2148
|
+
async function isFreeEmail(params) {
|
|
2149
|
+
const { emailOrDomain, cache, logger } = params;
|
|
2150
|
+
const log = logger || (() => {
|
|
2151
|
+
});
|
|
2034
2152
|
const parts = emailOrDomain.split("@");
|
|
2035
2153
|
const emailDomain = parts.length > 1 ? parts[1] : parts[0];
|
|
2036
2154
|
if (!emailDomain) {
|
|
@@ -2044,6 +2162,7 @@ async function isFreeEmail(emailOrDomain, cache) {
|
|
|
2044
2162
|
cached = null;
|
|
2045
2163
|
}
|
|
2046
2164
|
if (cached !== null && cached !== void 0) {
|
|
2165
|
+
log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached}`);
|
|
2047
2166
|
return cached;
|
|
2048
2167
|
}
|
|
2049
2168
|
if (!freeEmailProviders) {
|
|
@@ -2052,8 +2171,11 @@ async function isFreeEmail(emailOrDomain, cache) {
|
|
|
2052
2171
|
const result = freeEmailProviders.has(emailDomain);
|
|
2053
2172
|
try {
|
|
2054
2173
|
await cacheStore.set(emailDomain, result);
|
|
2174
|
+
log(`[isFreeEmail] Cached result for ${emailDomain}: ${result}`);
|
|
2055
2175
|
} catch (_error) {
|
|
2176
|
+
log(`[isFreeEmail] Cache write error for ${emailDomain}`);
|
|
2056
2177
|
}
|
|
2178
|
+
log(`[isFreeEmail] Check result for ${emailDomain}: ${result}`);
|
|
2057
2179
|
return result;
|
|
2058
2180
|
}
|
|
2059
2181
|
const domainPorts = {
|
|
@@ -2062,6 +2184,7 @@ const domainPorts = {
|
|
|
2062
2184
|
"ovh.net": 465
|
|
2063
2185
|
};
|
|
2064
2186
|
async function verifyEmail(params) {
|
|
2187
|
+
var _a;
|
|
2065
2188
|
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;
|
|
2066
2189
|
const startTime = Date.now();
|
|
2067
2190
|
const log = debug ? console.debug : (..._args) => {
|
|
@@ -2119,36 +2242,56 @@ async function verifyEmail(params) {
|
|
|
2119
2242
|
return result;
|
|
2120
2243
|
}
|
|
2121
2244
|
if (checkDisposable) {
|
|
2122
|
-
|
|
2245
|
+
log(`[verifyEmail] Checking if ${emailAddress} is disposable email`);
|
|
2246
|
+
result.isDisposable = await isDisposableEmail({ emailOrDomain: emailAddress, cache: params.cache, logger: log });
|
|
2247
|
+
log(`[verifyEmail] Disposable check result: ${result.isDisposable}`);
|
|
2123
2248
|
if (result.isDisposable && result.metadata) {
|
|
2124
2249
|
result.metadata.error = VerificationErrorCode.DISPOSABLE_EMAIL;
|
|
2125
2250
|
}
|
|
2126
2251
|
}
|
|
2127
2252
|
if (checkFree) {
|
|
2128
|
-
|
|
2253
|
+
log(`[verifyEmail] Checking if ${emailAddress} is free email provider`);
|
|
2254
|
+
result.isFree = await isFreeEmail({ emailOrDomain: emailAddress, cache: params.cache, logger: log });
|
|
2255
|
+
log(`[verifyEmail] Free email check result: ${result.isFree}`);
|
|
2129
2256
|
}
|
|
2130
2257
|
const shouldSkipMx = skipMxForDisposable && result.isDisposable;
|
|
2131
2258
|
const shouldSkipDomainWhois = skipDomainWhoisForDisposable && result.isDisposable;
|
|
2259
|
+
if (shouldSkipMx) {
|
|
2260
|
+
log(`[verifyEmail] Skipping MX record check for disposable email: ${emailAddress}`);
|
|
2261
|
+
}
|
|
2262
|
+
if (shouldSkipDomainWhois) {
|
|
2263
|
+
log(`[verifyEmail] Skipping domain WHOIS checks for disposable email: ${emailAddress}`);
|
|
2264
|
+
}
|
|
2132
2265
|
if (checkDomainAge && !shouldSkipDomainWhois) {
|
|
2266
|
+
log(`[verifyEmail] Checking domain age for ${domain}`);
|
|
2133
2267
|
try {
|
|
2134
2268
|
result.domainAge = await getDomainAge(domain, whoisTimeout, debug);
|
|
2269
|
+
log(`[verifyEmail] Domain age result:`, result.domainAge ? `${result.domainAge.ageInDays} days` : "null");
|
|
2135
2270
|
} catch (err) {
|
|
2136
2271
|
log("[verifyEmail] Failed to get domain age", err);
|
|
2137
2272
|
result.domainAge = null;
|
|
2138
2273
|
}
|
|
2274
|
+
} else if (checkDomainAge && shouldSkipDomainWhois) {
|
|
2275
|
+
log(`[verifyEmail] Domain age check skipped due to disposable email and skipDomainWhoisForDisposable=true`);
|
|
2139
2276
|
}
|
|
2140
2277
|
if (checkDomainRegistration && !shouldSkipDomainWhois) {
|
|
2278
|
+
log(`[verifyEmail] Checking domain registration status for ${domain}`);
|
|
2141
2279
|
try {
|
|
2142
2280
|
result.domainRegistration = await getDomainRegistrationStatus(domain, whoisTimeout, debug);
|
|
2281
|
+
log(`[verifyEmail] Domain registration result:`, ((_a = result.domainRegistration) === null || _a === void 0 ? void 0 : _a.isRegistered) ? "registered" : "not registered");
|
|
2143
2282
|
} catch (err) {
|
|
2144
2283
|
log("[verifyEmail] Failed to get domain registration status", err);
|
|
2145
2284
|
result.domainRegistration = null;
|
|
2146
2285
|
}
|
|
2286
|
+
} else if (checkDomainRegistration && shouldSkipDomainWhois) {
|
|
2287
|
+
log(`[verifyEmail] Domain registration check skipped due to disposable email and skipDomainWhoisForDisposable=true`);
|
|
2147
2288
|
}
|
|
2148
2289
|
if ((verifyMx || verifySmtp) && !shouldSkipMx) {
|
|
2290
|
+
log(`[verifyEmail] Checking MX records for ${domain}`);
|
|
2149
2291
|
try {
|
|
2150
|
-
const mxRecords = await resolveMxRecords(domain, params.cache);
|
|
2292
|
+
const mxRecords = await resolveMxRecords({ domain, cache: params.cache, logger: log });
|
|
2151
2293
|
result.validMx = mxRecords.length > 0;
|
|
2294
|
+
log(`[verifyEmail] MX records found: ${mxRecords.length}, valid: ${result.validMx}`);
|
|
2152
2295
|
if (!result.validMx && result.metadata) {
|
|
2153
2296
|
result.metadata.error = VerificationErrorCode.NO_MX_RECORDS;
|
|
2154
2297
|
}
|
|
@@ -2158,6 +2301,7 @@ async function verifyEmail(params) {
|
|
|
2158
2301
|
const cachedSmtp = await smtpCacheInstance.get(cacheKey);
|
|
2159
2302
|
if (cachedSmtp !== null && cachedSmtp !== void 0) {
|
|
2160
2303
|
result.validSmtp = cachedSmtp;
|
|
2304
|
+
log(`[verifyEmail] SMTP result from cache: ${result.validSmtp} for ${emailAddress}`);
|
|
2161
2305
|
if (result.metadata) {
|
|
2162
2306
|
result.metadata.cached = true;
|
|
2163
2307
|
}
|
|
@@ -2168,6 +2312,7 @@ async function verifyEmail(params) {
|
|
|
2168
2312
|
});
|
|
2169
2313
|
}
|
|
2170
2314
|
} else {
|
|
2315
|
+
log(`[verifyEmail] Performing SMTP verification for ${emailAddress}`);
|
|
2171
2316
|
let domainPort = params.smtpPort;
|
|
2172
2317
|
if (!domainPort) {
|
|
2173
2318
|
const mxDomain = parse(mxRecords[0]);
|
|
@@ -2186,6 +2331,7 @@ async function verifyEmail(params) {
|
|
|
2186
2331
|
});
|
|
2187
2332
|
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2188
2333
|
result.validSmtp = smtpResult;
|
|
2334
|
+
log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
|
|
2189
2335
|
}
|
|
2190
2336
|
if (result.validSmtp === false && result.metadata) {
|
|
2191
2337
|
result.metadata.error = VerificationErrorCode.MAILBOX_NOT_FOUND;
|
|
@@ -2200,6 +2346,8 @@ async function verifyEmail(params) {
|
|
|
2200
2346
|
result.metadata.error = VerificationErrorCode.NO_MX_RECORDS;
|
|
2201
2347
|
}
|
|
2202
2348
|
}
|
|
2349
|
+
} else if ((verifyMx || verifySmtp) && shouldSkipMx) {
|
|
2350
|
+
log(`[verifyEmail] MX/SMTP checks skipped due to disposable email and skipMxForDisposable=true`);
|
|
2203
2351
|
}
|
|
2204
2352
|
if (result.metadata) {
|
|
2205
2353
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
@@ -2207,5 +2355,5 @@ async function verifyEmail(params) {
|
|
|
2207
2355
|
return result;
|
|
2208
2356
|
}
|
|
2209
2357
|
|
|
2210
|
-
export { COMMON_EMAIL_DOMAINS, VerificationErrorCode, clearAllCaches, defaultDomainSuggestionMethod, defaultNameDetectionMethod, detectName, detectNameFromEmail, domainPorts, getDomainAge, getDomainRegistrationStatus, getDomainSimilarity, isCommonDomain, isDisposableEmail, isFreeEmail, isValidEmail, isValidEmailDomain, suggestDomain, suggestEmailDomain, verifyEmail, verifyEmailBatch };
|
|
2358
|
+
export { COMMON_EMAIL_DOMAINS, LRUAdapter, RedisAdapter, VerificationErrorCode, clearAllCaches, defaultDomainSuggestionMethod, defaultNameDetectionMethod, detectName, detectNameFromEmail, domainPorts, getDomainAge, getDomainRegistrationStatus, getDomainSimilarity, isCommonDomain, isDisposableEmail, isFreeEmail, isValidEmail, isValidEmailDomain, suggestDomain, suggestEmailDomain, verifyEmail, verifyEmailBatch };
|
|
2211
2359
|
//# sourceMappingURL=index.esm.js.map
|