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