@emailcheck/email-validator-js 4.0.0 → 5.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1014,19 +1014,25 @@ function defaultProbeLocal() {
1014
1014
  return `${node_crypto.randomBytes(8).toString("hex")}-noexist`;
1015
1015
  }
1016
1016
  async function verifyMailboxSMTP(params) {
1017
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1017
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
1018
1018
  const { local, domain, options = {} } = params;
1019
1019
  const mxRecords = (_a = params.mxRecords) !== null && _a !== void 0 ? _a : [];
1020
1020
  const ports = ((_b = options.ports) !== null && _b !== void 0 ? _b : DEFAULT_PORTS).filter((port) => Number.isInteger(port) && port > 0 && port < 65536);
1021
- const timeout = (_c = options.timeout) !== null && _c !== void 0 ? _c : DEFAULT_TIMEOUT_MS;
1022
- const tlsConfig = (_d = options.tls) !== null && _d !== void 0 ? _d : true;
1023
- const hostname = (_e = options.hostname) !== null && _e !== void 0 ? _e : "localhost";
1021
+ const perAttemptTimeoutMs = (_c = options.perAttemptTimeoutMs) !== null && _c !== void 0 ? _c : DEFAULT_TIMEOUT_MS;
1022
+ const tlsConfig = (_d = options.tlsConfig) !== null && _d !== void 0 ? _d : true;
1023
+ const heloHostname = (_e = options.heloHostname) !== null && _e !== void 0 ? _e : "localhost";
1024
1024
  const debug = (_f = options.debug) !== null && _f !== void 0 ? _f : false;
1025
1025
  const captureTranscript = (_g = options.captureTranscript) !== null && _g !== void 0 ? _g : false;
1026
1026
  const sequence = options.sequence;
1027
1027
  const cache = options.cache;
1028
1028
  const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
1029
1029
  };
1030
+ const totalDeadlineMs = options.totalDeadlineMs;
1031
+ const maxConsecutiveFailures = options.maxConsecutiveFailures;
1032
+ const maxMxHosts = options.maxMxHosts;
1033
+ const retryAttempts = (_j = (_h = options.retry) === null || _h === void 0 ? void 0 : _h.attempts) !== null && _j !== void 0 ? _j : 0;
1034
+ const retryDelayMs = (_l = (_k = options.retry) === null || _k === void 0 ? void 0 : _k.delayMs) !== null && _l !== void 0 ? _l : 200;
1035
+ const retryBackoff = (_o = (_m = options.retry) === null || _m === void 0 ? void 0 : _m.backoff) !== null && _o !== void 0 ? _o : "exponential";
1030
1036
  const startedAtMs = Date.now();
1031
1037
  const primaryMx = mxRecords[0];
1032
1038
  if (!primaryMx) {
@@ -1040,14 +1046,14 @@ async function verifyMailboxSMTP(params) {
1040
1046
  const probeOptions = {
1041
1047
  local,
1042
1048
  domain,
1043
- timeout,
1049
+ perAttemptTimeoutMs,
1044
1050
  tlsConfig,
1045
- hostname,
1051
+ heloHostname,
1046
1052
  sequence,
1047
1053
  log,
1048
1054
  catchAllProbeLocal: options.catchAllProbeLocal,
1049
- pipelining: (_h = options.pipelining) !== null && _h !== void 0 ? _h : "auto",
1050
- startTls: (_j = options.startTls) !== null && _j !== void 0 ? _j : "auto"
1055
+ pipelining: (_p = options.pipelining) !== null && _p !== void 0 ? _p : "auto",
1056
+ startTls: (_q = options.startTls) !== null && _q !== void 0 ? _q : "auto"
1051
1057
  };
1052
1058
  const verdictCache = cache ? getCacheStore(cache, "smtp") : null;
1053
1059
  const verdictKey = `${primaryMx}:${local}@${domain}`;
@@ -1063,17 +1069,42 @@ async function verifyMailboxSMTP(params) {
1063
1069
  const mxHostsTried = [];
1064
1070
  let mxAttempts = 0;
1065
1071
  let portAttempts = 0;
1072
+ let consecutiveFailures = 0;
1066
1073
  let lastReason = "all_attempts_failed";
1067
1074
  let lastEnhancedStatus;
1068
1075
  let lastResponseCode;
1069
- for (const mxHost of mxRecords) {
1076
+ let stoppedEarly = null;
1077
+ const isConnectionFailure = (reason) => reason === "connection_error" || reason === "connection_timeout" || reason === "connection_closed" || reason === "socket_timeout";
1078
+ const retryDelayFor = (attemptIndex) => retryBackoff === "exponential" ? retryDelayMs * 2 ** (attemptIndex - 1) : retryDelayMs;
1079
+ const probeWithRetry = async (mxHost, port) => {
1080
+ let lastProbe = await runProbe({ ...probeOptions, mxHost, port });
1081
+ for (let i = 1; i <= retryAttempts; i++) {
1082
+ if (lastProbe.result !== null || !isConnectionFailure(lastProbe.reason))
1083
+ break;
1084
+ const delay = retryDelayFor(i);
1085
+ log(`retry ${i}/${retryAttempts} on ${mxHost}:${port} after ${delay}ms (last: ${lastProbe.reason})`);
1086
+ await new Promise((r) => setTimeout(r, delay));
1087
+ portAttempts++;
1088
+ lastProbe = await runProbe({ ...probeOptions, mxHost, port });
1089
+ }
1090
+ return lastProbe;
1091
+ };
1092
+ outer: for (const mxHost of mxRecords) {
1093
+ if (maxMxHosts !== void 0 && mxAttempts >= maxMxHosts) {
1094
+ stoppedEarly = "max_mx_hosts";
1095
+ break;
1096
+ }
1070
1097
  mxHostsTried.push(mxHost);
1071
1098
  mxAttempts++;
1072
1099
  const portsForThisMx = mxHost === primaryMx && cachedPort ? [cachedPort, ...ports.filter((p) => p !== cachedPort)] : ports;
1073
1100
  for (const port of portsForThisMx) {
1101
+ if (totalDeadlineMs !== void 0 && Date.now() - startedAtMs >= totalDeadlineMs) {
1102
+ stoppedEarly = "deadline";
1103
+ break outer;
1104
+ }
1074
1105
  portAttempts++;
1075
1106
  log(`Testing ${mxHost}:${port}`);
1076
- const probe = await runProbe({ ...probeOptions, mxHost, port });
1107
+ const probe = await probeWithRetry(mxHost, port);
1077
1108
  collectTranscript(transcript, commands, probe, mxHost, port);
1078
1109
  lastReason = probe.reason;
1079
1110
  if (probe.enhancedStatus !== void 0)
@@ -1092,8 +1123,15 @@ async function verifyMailboxSMTP(params) {
1092
1123
  await safeCacheSet(portCache, primaryMx, port);
1093
1124
  return { smtpResult: smtpResult2, cached: false, port, portCached: cachedPort === port };
1094
1125
  }
1126
+ consecutiveFailures = isConnectionFailure(probe.reason) ? consecutiveFailures + 1 : 0;
1127
+ if (maxConsecutiveFailures !== void 0 && consecutiveFailures >= maxConsecutiveFailures) {
1128
+ stoppedEarly = "consecutive_failures";
1129
+ break outer;
1130
+ }
1095
1131
  }
1096
1132
  }
1133
+ if (stoppedEarly)
1134
+ log(`Stopped early: ${stoppedEarly}`);
1097
1135
  log(`All MX\xD7port attempts failed (mx=${mxAttempts}, port=${portAttempts})`);
1098
1136
  const metrics = makeMetrics(mxHostsTried, mxAttempts, portAttempts, void 0, startedAtMs);
1099
1137
  const smtpResult = {
@@ -1240,17 +1278,17 @@ class SMTPProbeConnection {
1240
1278
  } else {
1241
1279
  this.socket = net__namespace.connect({ host: this.p.mxHost, port: this.p.port }, onConnect);
1242
1280
  }
1243
- this.socket.setTimeout(this.p.timeout, () => this.finish(null, "socket_timeout"));
1281
+ this.socket.setTimeout(this.p.perAttemptTimeoutMs, () => this.finish(null, "socket_timeout"));
1244
1282
  this.socket.on("error", () => this.finish(null, "connection_error"));
1245
1283
  this.socket.on("close", () => this.finish(null, "connection_closed"));
1246
1284
  }
1247
1285
  armConnectionTimer() {
1248
- this.connectionTimer = setTimeout(() => this.finish(null, "connection_timeout"), this.p.timeout);
1286
+ this.connectionTimer = setTimeout(() => this.finish(null, "connection_timeout"), this.p.perAttemptTimeoutMs);
1249
1287
  }
1250
1288
  resetStepTimer() {
1251
1289
  if (this.stepTimer)
1252
1290
  clearTimeout(this.stepTimer);
1253
- this.stepTimer = setTimeout(() => this.finish(null, "step_timeout"), this.p.timeout);
1291
+ this.stepTimer = setTimeout(() => this.finish(null, "step_timeout"), this.p.perAttemptTimeoutMs);
1254
1292
  }
1255
1293
  send(cmd) {
1256
1294
  var _a;
@@ -1279,10 +1317,10 @@ class SMTPProbeConnection {
1279
1317
  return;
1280
1318
  // server-driven; nothing to send
1281
1319
  case exports.SMTPStep.ehlo:
1282
- this.send(`EHLO ${this.p.hostname}`);
1320
+ this.send(`EHLO ${this.p.heloHostname}`);
1283
1321
  return;
1284
1322
  case exports.SMTPStep.helo:
1285
- this.send(`HELO ${this.p.hostname}`);
1323
+ this.send(`HELO ${this.p.heloHostname}`);
1286
1324
  return;
1287
1325
  case exports.SMTPStep.startTls:
1288
1326
  this.executeStartTls();
@@ -1354,7 +1392,7 @@ class SMTPProbeConnection {
1354
1392
  this.supportsStartTls = false;
1355
1393
  this.supportsPipelining = false;
1356
1394
  this.postUpgradeReEhlo = true;
1357
- this.send(`EHLO ${this.p.hostname}`);
1395
+ this.send(`EHLO ${this.p.heloHostname}`);
1358
1396
  });
1359
1397
  this.socket = tlsSocket;
1360
1398
  this.socket.on("data", this.onData);
@@ -1362,7 +1400,7 @@ class SMTPProbeConnection {
1362
1400
  this.finish(null, this.tlsUpgrading ? "tls_handshake_failed" : "connection_error");
1363
1401
  });
1364
1402
  this.socket.on("close", () => this.finish(null, "connection_closed"));
1365
- this.socket.setTimeout(this.p.timeout, () => this.finish(null, "socket_timeout"));
1403
+ this.socket.setTimeout(this.p.perAttemptTimeoutMs, () => this.finish(null, "socket_timeout"));
1366
1404
  }
1367
1405
  /**
1368
1406
  * Send the dual-probe envelope (real RCPT + probe RCPT + RSET). Pipelined
@@ -2331,11 +2369,11 @@ async function runWhoisChecks(domain, params, result, skipWhois, log, collector)
2331
2369
  log(`[verifyEmail] WHOIS checks skipped for disposable: ${domain}`);
2332
2370
  return;
2333
2371
  }
2334
- const whoisTimeout = (_a = params.whoisTimeout) !== null && _a !== void 0 ? _a : 5e3;
2372
+ const whoisTimeoutMs = (_a = params.whoisTimeoutMs) !== null && _a !== void 0 ? _a : 5e3;
2335
2373
  const debug = (_b = params.debug) !== null && _b !== void 0 ? _b : false;
2336
2374
  if (params.checkDomainAge) {
2337
2375
  try {
2338
- result.domainAge = await collector.record("whois-age", () => getDomainAge(domain, whoisTimeout, debug, params.cache), (info) => {
2376
+ result.domainAge = await collector.record("whois-age", () => getDomainAge(domain, whoisTimeoutMs, debug, params.cache), (info) => {
2339
2377
  var _a2, _b2, _c2, _d2, _e, _f;
2340
2378
  return {
2341
2379
  domain,
@@ -2354,7 +2392,7 @@ async function runWhoisChecks(domain, params, result, skipWhois, log, collector)
2354
2392
  }
2355
2393
  if (params.checkDomainRegistration) {
2356
2394
  try {
2357
- result.domainRegistration = await collector.record("whois-registration", () => getDomainRegistrationStatus(domain, whoisTimeout, debug, params.cache), (info) => {
2395
+ result.domainRegistration = await collector.record("whois-registration", () => getDomainRegistrationStatus(domain, whoisTimeoutMs, debug, params.cache), (info) => {
2358
2396
  var _a2, _b2, _c2, _d2, _e, _f;
2359
2397
  return {
2360
2398
  domain,
@@ -2425,7 +2463,11 @@ async function runSmtp(local, domain, mxRecords, params, result, log, collector)
2425
2463
  options: {
2426
2464
  cache: params.cache,
2427
2465
  ports: resolveSmtpPorts(params.smtpPort, mxRecords[0]),
2428
- timeout: (_a = params.timeout) !== null && _a !== void 0 ? _a : 4e3,
2466
+ perAttemptTimeoutMs: (_a = params.smtpPerAttemptTimeoutMs) !== null && _a !== void 0 ? _a : 4e3,
2467
+ totalDeadlineMs: params.smtpTotalDeadlineMs,
2468
+ maxConsecutiveFailures: params.smtpMaxConsecutiveFailures,
2469
+ maxMxHosts: params.smtpMaxMxHosts,
2470
+ retry: params.smtpRetry,
2429
2471
  debug: (_b = params.debug) !== null && _b !== void 0 ? _b : false,
2430
2472
  // Forward transcript capture so the SMTP step's details include
2431
2473
  // the full per-port transcript when the caller asked for it.
@@ -2466,7 +2508,7 @@ function smtpVerdictFor(validSmtp) {
2466
2508
  }
2467
2509
 
2468
2510
  async function verifyEmailBatch(params) {
2469
- 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;
2511
+ const { emailAddresses, concurrency = 5, smtpPerAttemptTimeoutMs = 4e3, smtpTotalDeadlineMs, smtpMaxConsecutiveFailures, smtpMaxMxHosts, smtpRetry, verifyMx = true, verifySmtp = false, checkDisposable = true, checkFree = true, detectName = false, nameDetectionMethod, suggestDomain = false, domainSuggestionMethod, commonDomains, skipMxForDisposable = false, skipDomainWhoisForDisposable = false, cache } = params;
2470
2512
  const startTime = Date.now();
2471
2513
  const results = /* @__PURE__ */ new Map();
2472
2514
  const batches = [];
@@ -2481,7 +2523,11 @@ async function verifyEmailBatch(params) {
2481
2523
  try {
2482
2524
  const result = await verifyEmail({
2483
2525
  emailAddress: email,
2484
- timeout,
2526
+ smtpPerAttemptTimeoutMs,
2527
+ smtpTotalDeadlineMs,
2528
+ smtpMaxConsecutiveFailures,
2529
+ smtpMaxMxHosts,
2530
+ smtpRetry,
2485
2531
  verifyMx,
2486
2532
  verifySmtp,
2487
2533
  checkDisposable,
@@ -2781,6 +2827,7 @@ exports.cleanNameForAlgorithm = cleanNameForAlgorithm;
2781
2827
  exports.clearDefaultCache = clearDefaultCache;
2782
2828
  exports.commonEmailDomains = commonEmailDomains;
2783
2829
  exports.defaultDomainSuggestionMethod = defaultDomainSuggestionMethod;
2830
+ exports.defaultDomainSuggestionMethodAsync = defaultDomainSuggestionMethodAsync;
2784
2831
  exports.defaultNameDetectionMethod = defaultNameDetectionMethod;
2785
2832
  exports.detectName = detectName;
2786
2833
  exports.detectNameForAlgorithm = detectNameForAlgorithm;
@@ -2798,10 +2845,14 @@ exports.isSpamEmail = isSpamEmail;
2798
2845
  exports.isSpamName = isSpamName;
2799
2846
  exports.isValidEmail = isValidEmail;
2800
2847
  exports.isValidEmailDomain = isValidEmailDomain;
2848
+ exports.parseDsn = parseDsn;
2801
2849
  exports.parseSmtpError = parseSmtpError;
2850
+ exports.parseWhoisData = parseWhoisData;
2802
2851
  exports.refineReasonByEnhancedStatus = refineReasonByEnhancedStatus;
2852
+ exports.resolveMxRecords = resolveMxRecords;
2803
2853
  exports.suggestDomain = suggestDomain;
2804
2854
  exports.suggestEmailDomain = suggestEmailDomain;
2805
2855
  exports.verifyEmail = verifyEmail;
2806
2856
  exports.verifyEmailBatch = verifyEmailBatch;
2857
+ exports.verifyMailboxSMTP = verifyMailboxSMTP;
2807
2858
  //# sourceMappingURL=index.js.map