@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/cli/index.js +60 -20
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.esm.js +70 -24
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +74 -23
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +242 -33
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5,22 +5,29 @@
|
|
|
5
5
|
* (`isDisposableEmail`, `isFreeEmail`) live in their own modules so
|
|
6
6
|
* `batch-verifier.ts` can pull `verifyEmail` directly without going through
|
|
7
7
|
* this file. That broke a `index → batch-verifier → index` Rollup cycle.
|
|
8
|
+
*
|
|
9
|
+
* Anything `export`ed under `src/*.ts` should also be re-exported here so
|
|
10
|
+
* `import { … } from '@emailcheck/email-validator-js'` works for every
|
|
11
|
+
* public symbol. See the audit comment per-line below.
|
|
8
12
|
*/
|
|
9
13
|
export * from './adapters/lru-adapter';
|
|
10
14
|
export * from './adapters/redis-adapter';
|
|
11
15
|
export { verifyEmailBatch } from './batch-verifier';
|
|
12
16
|
export * from './cache';
|
|
13
17
|
export * from './cache-interface';
|
|
14
|
-
export { commonEmailDomains, defaultDomainSuggestionMethod, getDomainSimilarity, isCommonDomain, suggestDomain, suggestEmailDomain, } from './domain-suggester';
|
|
18
|
+
export { commonEmailDomains, defaultDomainSuggestionMethod, defaultDomainSuggestionMethodAsync, getDomainSimilarity, isCommonDomain, suggestDomain, suggestEmailDomain, } from './domain-suggester';
|
|
15
19
|
export { isValidEmail, isValidEmailDomain } from './email-validator';
|
|
16
20
|
export { isDisposableEmail } from './is-disposable-email';
|
|
17
21
|
export { isFreeEmail } from './is-free-email';
|
|
18
22
|
export { isSpamEmail } from './is-spam-email';
|
|
19
23
|
export { isSpamName } from './is-spam-name';
|
|
24
|
+
export { resolveMxRecords } from './mx-resolver';
|
|
20
25
|
export { cleanNameForAlgorithm, defaultNameDetectionMethod, detectName, detectNameForAlgorithm, detectNameFromEmail, } from './name-detector';
|
|
21
26
|
export { refineReasonByEnhancedStatus } from './refine-reason';
|
|
22
27
|
export { type ParsedSmtpError, parseSmtpError } from './smtp-error-parser';
|
|
28
|
+
export { type ParsedDsn, parseDsn, verifyMailboxSMTP } from './smtp-verifier';
|
|
23
29
|
export { ArrayTranscriptCollector, NULL_COLLECTOR, type TranscriptCollector, } from './transcript';
|
|
24
30
|
export * from './types';
|
|
25
31
|
export { domainPorts, verifyEmail } from './verify-email';
|
|
26
32
|
export { getDomainAge, getDomainRegistrationStatus } from './whois';
|
|
33
|
+
export { type ParsedWhoisResult, parseWhoisData } from './whois-parser';
|
package/dist/index.esm.js
CHANGED
|
@@ -992,19 +992,25 @@ function defaultProbeLocal() {
|
|
|
992
992
|
return `${randomBytes(8).toString("hex")}-noexist`;
|
|
993
993
|
}
|
|
994
994
|
async function verifyMailboxSMTP(params) {
|
|
995
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
995
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
996
996
|
const { local, domain, options = {} } = params;
|
|
997
997
|
const mxRecords = (_a = params.mxRecords) !== null && _a !== void 0 ? _a : [];
|
|
998
998
|
const ports = ((_b = options.ports) !== null && _b !== void 0 ? _b : DEFAULT_PORTS).filter((port) => Number.isInteger(port) && port > 0 && port < 65536);
|
|
999
|
-
const
|
|
1000
|
-
const tlsConfig = (_d = options.
|
|
1001
|
-
const
|
|
999
|
+
const perAttemptTimeoutMs = (_c = options.perAttemptTimeoutMs) !== null && _c !== void 0 ? _c : DEFAULT_TIMEOUT_MS;
|
|
1000
|
+
const tlsConfig = (_d = options.tlsConfig) !== null && _d !== void 0 ? _d : true;
|
|
1001
|
+
const heloHostname = (_e = options.heloHostname) !== null && _e !== void 0 ? _e : "localhost";
|
|
1002
1002
|
const debug = (_f = options.debug) !== null && _f !== void 0 ? _f : false;
|
|
1003
1003
|
const captureTranscript = (_g = options.captureTranscript) !== null && _g !== void 0 ? _g : false;
|
|
1004
1004
|
const sequence = options.sequence;
|
|
1005
1005
|
const cache = options.cache;
|
|
1006
1006
|
const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
|
|
1007
1007
|
};
|
|
1008
|
+
const totalDeadlineMs = options.totalDeadlineMs;
|
|
1009
|
+
const maxConsecutiveFailures = options.maxConsecutiveFailures;
|
|
1010
|
+
const maxMxHosts = options.maxMxHosts;
|
|
1011
|
+
const retryAttempts = (_j = (_h = options.retry) === null || _h === void 0 ? void 0 : _h.attempts) !== null && _j !== void 0 ? _j : 0;
|
|
1012
|
+
const retryDelayMs = (_l = (_k = options.retry) === null || _k === void 0 ? void 0 : _k.delayMs) !== null && _l !== void 0 ? _l : 200;
|
|
1013
|
+
const retryBackoff = (_o = (_m = options.retry) === null || _m === void 0 ? void 0 : _m.backoff) !== null && _o !== void 0 ? _o : "exponential";
|
|
1008
1014
|
const startedAtMs = Date.now();
|
|
1009
1015
|
const primaryMx = mxRecords[0];
|
|
1010
1016
|
if (!primaryMx) {
|
|
@@ -1018,14 +1024,14 @@ async function verifyMailboxSMTP(params) {
|
|
|
1018
1024
|
const probeOptions = {
|
|
1019
1025
|
local,
|
|
1020
1026
|
domain,
|
|
1021
|
-
|
|
1027
|
+
perAttemptTimeoutMs,
|
|
1022
1028
|
tlsConfig,
|
|
1023
|
-
|
|
1029
|
+
heloHostname,
|
|
1024
1030
|
sequence,
|
|
1025
1031
|
log,
|
|
1026
1032
|
catchAllProbeLocal: options.catchAllProbeLocal,
|
|
1027
|
-
pipelining: (
|
|
1028
|
-
startTls: (
|
|
1033
|
+
pipelining: (_p = options.pipelining) !== null && _p !== void 0 ? _p : "auto",
|
|
1034
|
+
startTls: (_q = options.startTls) !== null && _q !== void 0 ? _q : "auto"
|
|
1029
1035
|
};
|
|
1030
1036
|
const verdictCache = cache ? getCacheStore(cache, "smtp") : null;
|
|
1031
1037
|
const verdictKey = `${primaryMx}:${local}@${domain}`;
|
|
@@ -1041,17 +1047,42 @@ async function verifyMailboxSMTP(params) {
|
|
|
1041
1047
|
const mxHostsTried = [];
|
|
1042
1048
|
let mxAttempts = 0;
|
|
1043
1049
|
let portAttempts = 0;
|
|
1050
|
+
let consecutiveFailures = 0;
|
|
1044
1051
|
let lastReason = "all_attempts_failed";
|
|
1045
1052
|
let lastEnhancedStatus;
|
|
1046
1053
|
let lastResponseCode;
|
|
1047
|
-
|
|
1054
|
+
let stoppedEarly = null;
|
|
1055
|
+
const isConnectionFailure = (reason) => reason === "connection_error" || reason === "connection_timeout" || reason === "connection_closed" || reason === "socket_timeout";
|
|
1056
|
+
const retryDelayFor = (attemptIndex) => retryBackoff === "exponential" ? retryDelayMs * 2 ** (attemptIndex - 1) : retryDelayMs;
|
|
1057
|
+
const probeWithRetry = async (mxHost, port) => {
|
|
1058
|
+
let lastProbe = await runProbe({ ...probeOptions, mxHost, port });
|
|
1059
|
+
for (let i = 1; i <= retryAttempts; i++) {
|
|
1060
|
+
if (lastProbe.result !== null || !isConnectionFailure(lastProbe.reason))
|
|
1061
|
+
break;
|
|
1062
|
+
const delay = retryDelayFor(i);
|
|
1063
|
+
log(`retry ${i}/${retryAttempts} on ${mxHost}:${port} after ${delay}ms (last: ${lastProbe.reason})`);
|
|
1064
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1065
|
+
portAttempts++;
|
|
1066
|
+
lastProbe = await runProbe({ ...probeOptions, mxHost, port });
|
|
1067
|
+
}
|
|
1068
|
+
return lastProbe;
|
|
1069
|
+
};
|
|
1070
|
+
outer: for (const mxHost of mxRecords) {
|
|
1071
|
+
if (maxMxHosts !== void 0 && mxAttempts >= maxMxHosts) {
|
|
1072
|
+
stoppedEarly = "max_mx_hosts";
|
|
1073
|
+
break;
|
|
1074
|
+
}
|
|
1048
1075
|
mxHostsTried.push(mxHost);
|
|
1049
1076
|
mxAttempts++;
|
|
1050
1077
|
const portsForThisMx = mxHost === primaryMx && cachedPort ? [cachedPort, ...ports.filter((p) => p !== cachedPort)] : ports;
|
|
1051
1078
|
for (const port of portsForThisMx) {
|
|
1079
|
+
if (totalDeadlineMs !== void 0 && Date.now() - startedAtMs >= totalDeadlineMs) {
|
|
1080
|
+
stoppedEarly = "deadline";
|
|
1081
|
+
break outer;
|
|
1082
|
+
}
|
|
1052
1083
|
portAttempts++;
|
|
1053
1084
|
log(`Testing ${mxHost}:${port}`);
|
|
1054
|
-
const probe = await
|
|
1085
|
+
const probe = await probeWithRetry(mxHost, port);
|
|
1055
1086
|
collectTranscript(transcript, commands, probe, mxHost, port);
|
|
1056
1087
|
lastReason = probe.reason;
|
|
1057
1088
|
if (probe.enhancedStatus !== void 0)
|
|
@@ -1070,8 +1101,15 @@ async function verifyMailboxSMTP(params) {
|
|
|
1070
1101
|
await safeCacheSet(portCache, primaryMx, port);
|
|
1071
1102
|
return { smtpResult: smtpResult2, cached: false, port, portCached: cachedPort === port };
|
|
1072
1103
|
}
|
|
1104
|
+
consecutiveFailures = isConnectionFailure(probe.reason) ? consecutiveFailures + 1 : 0;
|
|
1105
|
+
if (maxConsecutiveFailures !== void 0 && consecutiveFailures >= maxConsecutiveFailures) {
|
|
1106
|
+
stoppedEarly = "consecutive_failures";
|
|
1107
|
+
break outer;
|
|
1108
|
+
}
|
|
1073
1109
|
}
|
|
1074
1110
|
}
|
|
1111
|
+
if (stoppedEarly)
|
|
1112
|
+
log(`Stopped early: ${stoppedEarly}`);
|
|
1075
1113
|
log(`All MX\xD7port attempts failed (mx=${mxAttempts}, port=${portAttempts})`);
|
|
1076
1114
|
const metrics = makeMetrics(mxHostsTried, mxAttempts, portAttempts, void 0, startedAtMs);
|
|
1077
1115
|
const smtpResult = {
|
|
@@ -1218,17 +1256,17 @@ class SMTPProbeConnection {
|
|
|
1218
1256
|
} else {
|
|
1219
1257
|
this.socket = net$1.connect({ host: this.p.mxHost, port: this.p.port }, onConnect);
|
|
1220
1258
|
}
|
|
1221
|
-
this.socket.setTimeout(this.p.
|
|
1259
|
+
this.socket.setTimeout(this.p.perAttemptTimeoutMs, () => this.finish(null, "socket_timeout"));
|
|
1222
1260
|
this.socket.on("error", () => this.finish(null, "connection_error"));
|
|
1223
1261
|
this.socket.on("close", () => this.finish(null, "connection_closed"));
|
|
1224
1262
|
}
|
|
1225
1263
|
armConnectionTimer() {
|
|
1226
|
-
this.connectionTimer = setTimeout(() => this.finish(null, "connection_timeout"), this.p.
|
|
1264
|
+
this.connectionTimer = setTimeout(() => this.finish(null, "connection_timeout"), this.p.perAttemptTimeoutMs);
|
|
1227
1265
|
}
|
|
1228
1266
|
resetStepTimer() {
|
|
1229
1267
|
if (this.stepTimer)
|
|
1230
1268
|
clearTimeout(this.stepTimer);
|
|
1231
|
-
this.stepTimer = setTimeout(() => this.finish(null, "step_timeout"), this.p.
|
|
1269
|
+
this.stepTimer = setTimeout(() => this.finish(null, "step_timeout"), this.p.perAttemptTimeoutMs);
|
|
1232
1270
|
}
|
|
1233
1271
|
send(cmd) {
|
|
1234
1272
|
var _a;
|
|
@@ -1257,10 +1295,10 @@ class SMTPProbeConnection {
|
|
|
1257
1295
|
return;
|
|
1258
1296
|
// server-driven; nothing to send
|
|
1259
1297
|
case SMTPStep.ehlo:
|
|
1260
|
-
this.send(`EHLO ${this.p.
|
|
1298
|
+
this.send(`EHLO ${this.p.heloHostname}`);
|
|
1261
1299
|
return;
|
|
1262
1300
|
case SMTPStep.helo:
|
|
1263
|
-
this.send(`HELO ${this.p.
|
|
1301
|
+
this.send(`HELO ${this.p.heloHostname}`);
|
|
1264
1302
|
return;
|
|
1265
1303
|
case SMTPStep.startTls:
|
|
1266
1304
|
this.executeStartTls();
|
|
@@ -1332,7 +1370,7 @@ class SMTPProbeConnection {
|
|
|
1332
1370
|
this.supportsStartTls = false;
|
|
1333
1371
|
this.supportsPipelining = false;
|
|
1334
1372
|
this.postUpgradeReEhlo = true;
|
|
1335
|
-
this.send(`EHLO ${this.p.
|
|
1373
|
+
this.send(`EHLO ${this.p.heloHostname}`);
|
|
1336
1374
|
});
|
|
1337
1375
|
this.socket = tlsSocket;
|
|
1338
1376
|
this.socket.on("data", this.onData);
|
|
@@ -1340,7 +1378,7 @@ class SMTPProbeConnection {
|
|
|
1340
1378
|
this.finish(null, this.tlsUpgrading ? "tls_handshake_failed" : "connection_error");
|
|
1341
1379
|
});
|
|
1342
1380
|
this.socket.on("close", () => this.finish(null, "connection_closed"));
|
|
1343
|
-
this.socket.setTimeout(this.p.
|
|
1381
|
+
this.socket.setTimeout(this.p.perAttemptTimeoutMs, () => this.finish(null, "socket_timeout"));
|
|
1344
1382
|
}
|
|
1345
1383
|
/**
|
|
1346
1384
|
* Send the dual-probe envelope (real RCPT + probe RCPT + RSET). Pipelined
|
|
@@ -2309,11 +2347,11 @@ async function runWhoisChecks(domain, params, result, skipWhois, log, collector)
|
|
|
2309
2347
|
log(`[verifyEmail] WHOIS checks skipped for disposable: ${domain}`);
|
|
2310
2348
|
return;
|
|
2311
2349
|
}
|
|
2312
|
-
const
|
|
2350
|
+
const whoisTimeoutMs = (_a = params.whoisTimeoutMs) !== null && _a !== void 0 ? _a : 5e3;
|
|
2313
2351
|
const debug = (_b = params.debug) !== null && _b !== void 0 ? _b : false;
|
|
2314
2352
|
if (params.checkDomainAge) {
|
|
2315
2353
|
try {
|
|
2316
|
-
result.domainAge = await collector.record("whois-age", () => getDomainAge(domain,
|
|
2354
|
+
result.domainAge = await collector.record("whois-age", () => getDomainAge(domain, whoisTimeoutMs, debug, params.cache), (info) => {
|
|
2317
2355
|
var _a2, _b2, _c2, _d2, _e, _f;
|
|
2318
2356
|
return {
|
|
2319
2357
|
domain,
|
|
@@ -2332,7 +2370,7 @@ async function runWhoisChecks(domain, params, result, skipWhois, log, collector)
|
|
|
2332
2370
|
}
|
|
2333
2371
|
if (params.checkDomainRegistration) {
|
|
2334
2372
|
try {
|
|
2335
|
-
result.domainRegistration = await collector.record("whois-registration", () => getDomainRegistrationStatus(domain,
|
|
2373
|
+
result.domainRegistration = await collector.record("whois-registration", () => getDomainRegistrationStatus(domain, whoisTimeoutMs, debug, params.cache), (info) => {
|
|
2336
2374
|
var _a2, _b2, _c2, _d2, _e, _f;
|
|
2337
2375
|
return {
|
|
2338
2376
|
domain,
|
|
@@ -2403,7 +2441,11 @@ async function runSmtp(local, domain, mxRecords, params, result, log, collector)
|
|
|
2403
2441
|
options: {
|
|
2404
2442
|
cache: params.cache,
|
|
2405
2443
|
ports: resolveSmtpPorts(params.smtpPort, mxRecords[0]),
|
|
2406
|
-
|
|
2444
|
+
perAttemptTimeoutMs: (_a = params.smtpPerAttemptTimeoutMs) !== null && _a !== void 0 ? _a : 4e3,
|
|
2445
|
+
totalDeadlineMs: params.smtpTotalDeadlineMs,
|
|
2446
|
+
maxConsecutiveFailures: params.smtpMaxConsecutiveFailures,
|
|
2447
|
+
maxMxHosts: params.smtpMaxMxHosts,
|
|
2448
|
+
retry: params.smtpRetry,
|
|
2407
2449
|
debug: (_b = params.debug) !== null && _b !== void 0 ? _b : false,
|
|
2408
2450
|
// Forward transcript capture so the SMTP step's details include
|
|
2409
2451
|
// the full per-port transcript when the caller asked for it.
|
|
@@ -2444,7 +2486,7 @@ function smtpVerdictFor(validSmtp) {
|
|
|
2444
2486
|
}
|
|
2445
2487
|
|
|
2446
2488
|
async function verifyEmailBatch(params) {
|
|
2447
|
-
const { emailAddresses, concurrency = 5,
|
|
2489
|
+
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;
|
|
2448
2490
|
const startTime = Date.now();
|
|
2449
2491
|
const results = /* @__PURE__ */ new Map();
|
|
2450
2492
|
const batches = [];
|
|
@@ -2459,7 +2501,11 @@ async function verifyEmailBatch(params) {
|
|
|
2459
2501
|
try {
|
|
2460
2502
|
const result = await verifyEmail({
|
|
2461
2503
|
emailAddress: email,
|
|
2462
|
-
|
|
2504
|
+
smtpPerAttemptTimeoutMs,
|
|
2505
|
+
smtpTotalDeadlineMs,
|
|
2506
|
+
smtpMaxConsecutiveFailures,
|
|
2507
|
+
smtpMaxMxHosts,
|
|
2508
|
+
smtpRetry,
|
|
2463
2509
|
verifyMx,
|
|
2464
2510
|
verifySmtp,
|
|
2465
2511
|
checkDisposable,
|
|
@@ -2750,5 +2796,5 @@ function parseSmtpError(errorMessage) {
|
|
|
2750
2796
|
return { isDisabled, hasFullInbox, isCatchAll, isInvalid };
|
|
2751
2797
|
}
|
|
2752
2798
|
|
|
2753
|
-
export { ArrayTranscriptCollector, DEFAULT_CACHE_OPTIONS, EmailProvider, LRUAdapter, NULL_COLLECTOR, RedisAdapter, SMTPStep, VerificationErrorCode, cleanNameForAlgorithm, clearDefaultCache, commonEmailDomains, defaultDomainSuggestionMethod, defaultNameDetectionMethod, detectName, detectNameForAlgorithm, detectNameFromEmail, domainPorts, getCacheStore, getDefaultCache, getDomainAge, getDomainRegistrationStatus, getDomainSimilarity, isCommonDomain, isDisposableEmail, isFreeEmail, isSpamEmail, isSpamName, isValidEmail, isValidEmailDomain, parseSmtpError, refineReasonByEnhancedStatus, suggestDomain, suggestEmailDomain, verifyEmail, verifyEmailBatch };
|
|
2799
|
+
export { ArrayTranscriptCollector, DEFAULT_CACHE_OPTIONS, EmailProvider, LRUAdapter, NULL_COLLECTOR, RedisAdapter, SMTPStep, VerificationErrorCode, cleanNameForAlgorithm, clearDefaultCache, commonEmailDomains, defaultDomainSuggestionMethod, defaultDomainSuggestionMethodAsync, defaultNameDetectionMethod, detectName, detectNameForAlgorithm, detectNameFromEmail, domainPorts, getCacheStore, getDefaultCache, getDomainAge, getDomainRegistrationStatus, getDomainSimilarity, isCommonDomain, isDisposableEmail, isFreeEmail, isSpamEmail, isSpamName, isValidEmail, isValidEmailDomain, parseDsn, parseSmtpError, parseWhoisData, refineReasonByEnhancedStatus, resolveMxRecords, suggestDomain, suggestEmailDomain, verifyEmail, verifyEmailBatch, verifyMailboxSMTP };
|
|
2754
2800
|
//# sourceMappingURL=index.esm.js.map
|