@emailcheck/email-validator-js 4.0.0-beta.1 → 4.0.0-beta.3
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 +35 -0
- package/dist/cli/index.js +103 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +130 -8
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +130 -7
- package/dist/index.js.map +1 -1
- package/dist/refine-reason.d.ts +1 -0
- package/dist/serverless/adapters/aws-lambda.cjs.js.map +1 -1
- package/dist/serverless/adapters/aws-lambda.esm.js.map +1 -1
- package/dist/serverless/index.cjs.js.map +1 -1
- package/dist/serverless/index.esm.js.map +1 -1
- package/dist/types.d.ts +28 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -177,10 +177,8 @@ exports.VerificationErrorCode = void 0;
|
|
|
177
177
|
VerificationErrorCode2["smtpConnectionFailed"] = "SMTP_CONNECTION_FAILED";
|
|
178
178
|
VerificationErrorCode2["smtpTimeout"] = "SMTP_TIMEOUT";
|
|
179
179
|
VerificationErrorCode2["mailboxNotFound"] = "MAILBOX_NOT_FOUND";
|
|
180
|
-
VerificationErrorCode2["mailboxFull"] = "MAILBOX_FULL";
|
|
181
180
|
VerificationErrorCode2["networkError"] = "NETWORK_ERROR";
|
|
182
181
|
VerificationErrorCode2["disposableEmail"] = "DISPOSABLE_EMAIL";
|
|
183
|
-
VerificationErrorCode2["freeEmailProvider"] = "FREE_EMAIL_PROVIDER";
|
|
184
182
|
})(exports.VerificationErrorCode || (exports.VerificationErrorCode = {}));
|
|
185
183
|
exports.EmailProvider = void 0;
|
|
186
184
|
(function(EmailProvider2) {
|
|
@@ -197,6 +195,7 @@ exports.SMTPStep = void 0;
|
|
|
197
195
|
SMTPStep2["greeting"] = "GREETING";
|
|
198
196
|
SMTPStep2["ehlo"] = "EHLO";
|
|
199
197
|
SMTPStep2["helo"] = "HELO";
|
|
198
|
+
SMTPStep2["startTls"] = "STARTTLS";
|
|
200
199
|
SMTPStep2["mailFrom"] = "MAIL_FROM";
|
|
201
200
|
SMTPStep2["rcptTo"] = "RCPT_TO";
|
|
202
201
|
})(exports.SMTPStep || (exports.SMTPStep = {}));
|
|
@@ -1015,7 +1014,7 @@ function defaultProbeLocal() {
|
|
|
1015
1014
|
return `${node_crypto.randomBytes(8).toString("hex")}-noexist`;
|
|
1016
1015
|
}
|
|
1017
1016
|
async function verifyMailboxSMTP(params) {
|
|
1018
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1017
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
1019
1018
|
const { local, domain, options = {} } = params;
|
|
1020
1019
|
const mxRecords = (_a = params.mxRecords) !== null && _a !== void 0 ? _a : [];
|
|
1021
1020
|
const ports = ((_b = options.ports) !== null && _b !== void 0 ? _b : DEFAULT_PORTS).filter((port) => Number.isInteger(port) && port > 0 && port < 65536);
|
|
@@ -1047,7 +1046,8 @@ async function verifyMailboxSMTP(params) {
|
|
|
1047
1046
|
sequence,
|
|
1048
1047
|
log,
|
|
1049
1048
|
catchAllProbeLocal: options.catchAllProbeLocal,
|
|
1050
|
-
pipelining: (_h = options.pipelining) !== null && _h !== void 0 ? _h : "auto"
|
|
1049
|
+
pipelining: (_h = options.pipelining) !== null && _h !== void 0 ? _h : "auto",
|
|
1050
|
+
startTls: (_j = options.startTls) !== null && _j !== void 0 ? _j : "auto"
|
|
1051
1051
|
};
|
|
1052
1052
|
const verdictCache = cache ? getCacheStore(cache, "smtp") : null;
|
|
1053
1053
|
const verdictKey = `${primaryMx}:${local}@${domain}`;
|
|
@@ -1181,9 +1181,12 @@ class SMTPProbeConnection {
|
|
|
1181
1181
|
this.buffer = "";
|
|
1182
1182
|
this.resolved = false;
|
|
1183
1183
|
this.currentStepIndex = 0;
|
|
1184
|
+
this.tlsUpgrading = false;
|
|
1185
|
+
this.postUpgradeReEhlo = false;
|
|
1184
1186
|
this.transcript = [];
|
|
1185
1187
|
this.commands = [];
|
|
1186
1188
|
this.supportsPipelining = false;
|
|
1189
|
+
this.supportsStartTls = false;
|
|
1187
1190
|
this.dualPhase = "idle";
|
|
1188
1191
|
this.realOutcome = "pending";
|
|
1189
1192
|
this.probeOutcome = "pending";
|
|
@@ -1201,7 +1204,7 @@ class SMTPProbeConnection {
|
|
|
1201
1204
|
this.processLine(line);
|
|
1202
1205
|
}
|
|
1203
1206
|
};
|
|
1204
|
-
const defaultSteps = [exports.SMTPStep.greeting, exports.SMTPStep.ehlo, exports.SMTPStep.mailFrom, exports.SMTPStep.rcptTo];
|
|
1207
|
+
const defaultSteps = [exports.SMTPStep.greeting, exports.SMTPStep.ehlo, exports.SMTPStep.startTls, exports.SMTPStep.mailFrom, exports.SMTPStep.rcptTo];
|
|
1205
1208
|
this.steps = [...(_b = (_a = p.sequence) === null || _a === void 0 ? void 0 : _a.steps) !== null && _b !== void 0 ? _b : defaultSteps];
|
|
1206
1209
|
this.isTLS = PORT_TLS[p.port] === true;
|
|
1207
1210
|
const servername = isIPAddress(p.mxHost) ? void 0 : p.mxHost;
|
|
@@ -1221,7 +1224,8 @@ class SMTPProbeConnection {
|
|
|
1221
1224
|
this.connect();
|
|
1222
1225
|
this.armConnectionTimer();
|
|
1223
1226
|
} catch (error) {
|
|
1224
|
-
this.
|
|
1227
|
+
this.p.log(`connect threw: ${error instanceof Error ? error.message : "unknown"}`);
|
|
1228
|
+
this.finish(null, "connection_error");
|
|
1225
1229
|
}
|
|
1226
1230
|
});
|
|
1227
1231
|
}
|
|
@@ -1280,6 +1284,9 @@ class SMTPProbeConnection {
|
|
|
1280
1284
|
case exports.SMTPStep.helo:
|
|
1281
1285
|
this.send(`HELO ${this.p.hostname}`);
|
|
1282
1286
|
return;
|
|
1287
|
+
case exports.SMTPStep.startTls:
|
|
1288
|
+
this.executeStartTls();
|
|
1289
|
+
return;
|
|
1283
1290
|
case exports.SMTPStep.mailFrom: {
|
|
1284
1291
|
const from = (_b = (_a = this.p.sequence) === null || _a === void 0 ? void 0 : _a.from) !== null && _b !== void 0 ? _b : `<${this.p.local}@${this.p.domain}>`;
|
|
1285
1292
|
this.send(`MAIL FROM:${from}`);
|
|
@@ -1290,6 +1297,73 @@ class SMTPProbeConnection {
|
|
|
1290
1297
|
return;
|
|
1291
1298
|
}
|
|
1292
1299
|
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Conditional STARTTLS upgrade. Skipped (advances to next step) when:
|
|
1302
|
+
* - already TLS (implicit-TLS port like 465 or already-upgraded)
|
|
1303
|
+
* - `startTls === 'never'`
|
|
1304
|
+
* - `startTls === 'auto'` AND the MX didn't advertise STARTTLS in EHLO
|
|
1305
|
+
*
|
|
1306
|
+
* Sends `STARTTLS` and waits for 220 when:
|
|
1307
|
+
* - `startTls === 'force'` (regardless of advertisement)
|
|
1308
|
+
* - `startTls === 'auto'` AND the MX advertised it
|
|
1309
|
+
*
|
|
1310
|
+
* On 220, `tls.connect()` wraps the existing socket. After the handshake
|
|
1311
|
+
* we re-EHLO (mandatory per RFC 3207 §4.2 — pre-TLS state must be
|
|
1312
|
+
* discarded) before continuing to MAIL FROM.
|
|
1313
|
+
*/
|
|
1314
|
+
executeStartTls() {
|
|
1315
|
+
const mode = this.p.startTls;
|
|
1316
|
+
const wantsUpgrade = !this.isTLS && mode !== "never" && (mode === "force" || mode === "auto" && this.supportsStartTls);
|
|
1317
|
+
if (!wantsUpgrade) {
|
|
1318
|
+
this.nextStep();
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
this.send("STARTTLS");
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Wrap the plaintext socket with TLS in place. Called after the server
|
|
1325
|
+
* answers our STARTTLS with 220. Detaches the plaintext-socket listeners
|
|
1326
|
+
* (TLS owns the underlying transport now), re-installs them on the wrapped
|
|
1327
|
+
* socket, resets EHLO-derived capabilities, and re-issues EHLO once the
|
|
1328
|
+
* handshake completes (RFC 3207 §4.2 mandates re-EHLO after upgrade —
|
|
1329
|
+
* pre-TLS state must be discarded).
|
|
1330
|
+
*/
|
|
1331
|
+
upgradeToTls() {
|
|
1332
|
+
const plainSocket = this.socket;
|
|
1333
|
+
if (!plainSocket) {
|
|
1334
|
+
this.finish(null, "tls_upgrade_failed");
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
const detach = plainSocket.removeAllListeners;
|
|
1338
|
+
if (typeof detach === "function") {
|
|
1339
|
+
detach.call(plainSocket, "data");
|
|
1340
|
+
detach.call(plainSocket, "error");
|
|
1341
|
+
detach.call(plainSocket, "close");
|
|
1342
|
+
detach.call(plainSocket, "timeout");
|
|
1343
|
+
}
|
|
1344
|
+
try {
|
|
1345
|
+
plainSocket.setTimeout(0);
|
|
1346
|
+
} catch {
|
|
1347
|
+
}
|
|
1348
|
+
this.tlsUpgrading = true;
|
|
1349
|
+
const servername = isIPAddress(this.p.mxHost) ? void 0 : this.p.mxHost;
|
|
1350
|
+
const tlsSocket = tls__namespace.connect({ ...this.tlsOptions, socket: plainSocket, servername }, () => {
|
|
1351
|
+
this.tlsUpgrading = false;
|
|
1352
|
+
this.isTLS = true;
|
|
1353
|
+
this.buffer = "";
|
|
1354
|
+
this.supportsStartTls = false;
|
|
1355
|
+
this.supportsPipelining = false;
|
|
1356
|
+
this.postUpgradeReEhlo = true;
|
|
1357
|
+
this.send(`EHLO ${this.p.hostname}`);
|
|
1358
|
+
});
|
|
1359
|
+
this.socket = tlsSocket;
|
|
1360
|
+
this.socket.on("data", this.onData);
|
|
1361
|
+
this.socket.on("error", () => {
|
|
1362
|
+
this.finish(null, this.tlsUpgrading ? "tls_handshake_failed" : "connection_error");
|
|
1363
|
+
});
|
|
1364
|
+
this.socket.on("close", () => this.finish(null, "connection_closed"));
|
|
1365
|
+
this.socket.setTimeout(this.p.timeout, () => this.finish(null, "socket_timeout"));
|
|
1366
|
+
}
|
|
1293
1367
|
/**
|
|
1294
1368
|
* Send the dual-probe envelope (real RCPT + probe RCPT + RSET). Pipelined
|
|
1295
1369
|
* when the MX advertised PIPELINING (or `pipelining: 'force'`); sequential
|
|
@@ -1348,10 +1422,13 @@ ${rsetCmd}\r
|
|
|
1348
1422
|
}
|
|
1349
1423
|
if (MULTILINE_RE.test(line)) {
|
|
1350
1424
|
const step = this.steps[this.currentStepIndex];
|
|
1351
|
-
|
|
1425
|
+
const isEhloLike = step === exports.SMTPStep.ehlo || step === exports.SMTPStep.helo || step === exports.SMTPStep.startTls && this.postUpgradeReEhlo;
|
|
1426
|
+
if (isEhloLike && line.startsWith("250-")) {
|
|
1352
1427
|
const upper = line.toUpperCase();
|
|
1353
1428
|
if (upper.includes("PIPELINING"))
|
|
1354
1429
|
this.supportsPipelining = true;
|
|
1430
|
+
if (upper.includes("STARTTLS"))
|
|
1431
|
+
this.supportsStartTls = true;
|
|
1355
1432
|
}
|
|
1356
1433
|
return;
|
|
1357
1434
|
}
|
|
@@ -1386,6 +1463,21 @@ ${rsetCmd}\r
|
|
|
1386
1463
|
else
|
|
1387
1464
|
this.finish(null, "helo_failed");
|
|
1388
1465
|
return;
|
|
1466
|
+
case exports.SMTPStep.startTls:
|
|
1467
|
+
if (this.postUpgradeReEhlo) {
|
|
1468
|
+
this.postUpgradeReEhlo = false;
|
|
1469
|
+
if (code === 250)
|
|
1470
|
+
this.nextStep();
|
|
1471
|
+
else
|
|
1472
|
+
this.finish(null, "ehlo_failed");
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
if (code === 220) {
|
|
1476
|
+
this.upgradeToTls();
|
|
1477
|
+
} else {
|
|
1478
|
+
this.finish(null, "tls_upgrade_failed");
|
|
1479
|
+
}
|
|
1480
|
+
return;
|
|
1389
1481
|
case exports.SMTPStep.mailFrom:
|
|
1390
1482
|
if (code === 250)
|
|
1391
1483
|
this.nextStep();
|
|
@@ -2547,6 +2639,36 @@ function isSpamName(name) {
|
|
|
2547
2639
|
return true;
|
|
2548
2640
|
}
|
|
2549
2641
|
|
|
2642
|
+
const REFINEMENT_TABLE = {
|
|
2643
|
+
// X.1.x — addressing
|
|
2644
|
+
"5.1.1": "mailbox_does_not_exist",
|
|
2645
|
+
"5.1.2": "bad_destination_system",
|
|
2646
|
+
"5.1.3": "bad_destination_address",
|
|
2647
|
+
"5.1.6": "mailbox_moved",
|
|
2648
|
+
"5.1.10": "recipient_address_has_null_mx",
|
|
2649
|
+
// X.2.x — mailbox status
|
|
2650
|
+
"5.2.0": "mailbox_status_other",
|
|
2651
|
+
"5.2.1": "mailbox_disabled",
|
|
2652
|
+
"5.2.2": "mailbox_full",
|
|
2653
|
+
"5.2.3": "message_too_long",
|
|
2654
|
+
"5.2.4": "mailing_list_expansion_problem",
|
|
2655
|
+
// X.4.x — network / routing
|
|
2656
|
+
"4.4.1": "no_answer_from_host",
|
|
2657
|
+
"4.4.2": "bad_connection",
|
|
2658
|
+
// X.7.x — security / policy
|
|
2659
|
+
"5.7.0": "security_other",
|
|
2660
|
+
"5.7.1": "delivery_not_authorized",
|
|
2661
|
+
"5.7.25": "no_reverse_dns",
|
|
2662
|
+
"5.7.26": "multiple_authentication_failures"
|
|
2663
|
+
};
|
|
2664
|
+
function refineReasonByEnhancedStatus(reason, enhancedStatus) {
|
|
2665
|
+
var _a;
|
|
2666
|
+
const base = reason !== null && reason !== void 0 ? reason : "unknown";
|
|
2667
|
+
if (!enhancedStatus)
|
|
2668
|
+
return base;
|
|
2669
|
+
return (_a = REFINEMENT_TABLE[enhancedStatus]) !== null && _a !== void 0 ? _a : base;
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2550
2672
|
const NETWORK_ERROR_PATTERNS = [
|
|
2551
2673
|
"etimedout",
|
|
2552
2674
|
"econnrefused",
|
|
@@ -2677,6 +2799,7 @@ exports.isSpamName = isSpamName;
|
|
|
2677
2799
|
exports.isValidEmail = isValidEmail;
|
|
2678
2800
|
exports.isValidEmailDomain = isValidEmailDomain;
|
|
2679
2801
|
exports.parseSmtpError = parseSmtpError;
|
|
2802
|
+
exports.refineReasonByEnhancedStatus = refineReasonByEnhancedStatus;
|
|
2680
2803
|
exports.suggestDomain = suggestDomain;
|
|
2681
2804
|
exports.suggestEmailDomain = suggestEmailDomain;
|
|
2682
2805
|
exports.verifyEmail = verifyEmail;
|