@emailcheck/email-validator-js 2.14.1 → 3.0.1-beta.0

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.esm.js CHANGED
@@ -3,6 +3,7 @@ import { lru } from 'tiny-lru';
3
3
  import { promises } from 'node:dns';
4
4
  import { stringSimilarity } from 'string-similarity-js';
5
5
  import * as net from 'node:net';
6
+ import * as tls from 'node:tls';
6
7
 
7
8
  class LRUAdapter {
8
9
  constructor(maxSize = 1e3, ttlMs = 36e5) {
@@ -52,6 +53,8 @@ const DEFAULT_CACHE_OPTIONS = {
52
53
  // 24 hours
53
54
  smtp: 18e5,
54
55
  // 30 minutes
56
+ smtpPort: 36e5,
57
+ // 1 hour
55
58
  domainSuggestion: 864e5,
56
59
  // 24 hours
57
60
  whois: 36e5
@@ -63,6 +66,7 @@ const DEFAULT_CACHE_OPTIONS = {
63
66
  free: 1e3,
64
67
  domainValid: 1e3,
65
68
  smtp: 500,
69
+ smtpPort: 500,
66
70
  domainSuggestion: 1e3,
67
71
  whois: 200
68
72
  }
@@ -76,6 +80,7 @@ function getDefaultCache() {
76
80
  free: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.free, DEFAULT_CACHE_OPTIONS.ttl.free),
77
81
  domainValid: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.domainValid, DEFAULT_CACHE_OPTIONS.ttl.domainValid),
78
82
  smtp: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.smtp, DEFAULT_CACHE_OPTIONS.ttl.smtp),
83
+ smtpPort: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.smtpPort, DEFAULT_CACHE_OPTIONS.ttl.smtpPort),
79
84
  domainSuggestion: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.domainSuggestion, DEFAULT_CACHE_OPTIONS.ttl.domainSuggestion),
80
85
  whois: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.whois, DEFAULT_CACHE_OPTIONS.ttl.whois)
81
86
  };
@@ -92,6 +97,7 @@ function clearDefaultCache() {
92
97
  defaultCacheInstance.free.clear();
93
98
  defaultCacheInstance.domainValid.clear();
94
99
  defaultCacheInstance.smtp.clear();
100
+ defaultCacheInstance.smtpPort.clear();
95
101
  defaultCacheInstance.domainSuggestion.clear();
96
102
  defaultCacheInstance.whois.clear();
97
103
  }
@@ -1126,6 +1132,40 @@ function detectName(email) {
1126
1132
  return detectNameFromEmail({ email });
1127
1133
  }
1128
1134
 
1135
+ var VerificationErrorCode;
1136
+ (function(VerificationErrorCode2) {
1137
+ VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
1138
+ VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
1139
+ VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
1140
+ VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
1141
+ VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
1142
+ VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
1143
+ VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
1144
+ VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
1145
+ VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
1146
+ VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
1147
+ })(VerificationErrorCode || (VerificationErrorCode = {}));
1148
+ var SMTPStep;
1149
+ (function(SMTPStep2) {
1150
+ SMTPStep2["GREETING"] = "GREETING";
1151
+ SMTPStep2["EHLO"] = "EHLO";
1152
+ SMTPStep2["HELO"] = "HELO";
1153
+ SMTPStep2["STARTTLS"] = "STARTTLS";
1154
+ SMTPStep2["MAIL_FROM"] = "MAIL_FROM";
1155
+ SMTPStep2["RCPT_TO"] = "RCPT_TO";
1156
+ SMTPStep2["VRFY"] = "VRFY";
1157
+ SMTPStep2["QUIT"] = "QUIT";
1158
+ })(SMTPStep || (SMTPStep = {}));
1159
+
1160
+ function isIPAddress(host) {
1161
+ const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
1162
+ if (ipv4Regex.test(host)) {
1163
+ const octets = host.split(".");
1164
+ return octets.every((octet) => parseInt(octet, 10) <= 255);
1165
+ }
1166
+ const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,7}:$|^(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}$|^(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}$|^(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})$|^::1(?::(?::[0-9a-fA-F]{1,4}){1,7})|$|:(?:(?::[0-9a-fA-F]{1,4}){1,7}:)$/;
1167
+ return ipv6Regex.test(host);
1168
+ }
1129
1169
  function isOverQuota(smtpReply) {
1130
1170
  return Boolean(smtpReply && /(over quota)/gi.test(smtpReply));
1131
1171
  }
@@ -1135,129 +1175,382 @@ function isInvalidMailboxError(smtpReply) {
1135
1175
  function isMultilineGreet(smtpReply) {
1136
1176
  return Boolean(smtpReply && /^(250|220)-/.test(smtpReply));
1137
1177
  }
1178
+ const DEFAULT_PORTS = [25, 587, 465];
1179
+ const DEFAULT_TIMEOUT = 3e3;
1180
+ const DEFAULT_MAX_RETRIES = 1;
1181
+ const PORT_CONFIGS = {
1182
+ 25: { tls: false, starttls: true },
1183
+ 587: { tls: false, starttls: true },
1184
+ 465: { tls: true, starttls: false }
1185
+ };
1138
1186
  async function verifyMailboxSMTP(params) {
1139
- const { local, domain, mxRecords = [], timeout, debug, port = 25, retryAttempts = 1 } = params;
1140
- const log = debug ? console.debug : (..._args) => {
1187
+ const { local, domain, mxRecords = [], options = {} } = params;
1188
+ const { ports = DEFAULT_PORTS, timeout = DEFAULT_TIMEOUT, maxRetries = DEFAULT_MAX_RETRIES, tls: tlsConfig = true, hostname = "localhost", useVRFY = true, cache, debug = false, sequence } = options;
1189
+ const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
1141
1190
  };
1142
1191
  if (!mxRecords || mxRecords.length === 0) {
1143
- return false;
1192
+ log("No MX records found");
1193
+ return { result: false, cached: false, port: 0, portCached: false };
1194
+ }
1195
+ const mxHost = mxRecords[0];
1196
+ log(`Verifying ${local}@${domain} via ${mxHost}`);
1197
+ const smtpCacheStore = cache ? getCacheStore(cache, "smtp") : null;
1198
+ if (smtpCacheStore) {
1199
+ let cachedResult;
1200
+ try {
1201
+ cachedResult = await smtpCacheStore.get(`${mxHost}:${local}@${domain}`);
1202
+ if (cachedResult !== void 0) {
1203
+ log(`Using cached SMTP result: ${cachedResult}`);
1204
+ return {
1205
+ result: typeof cachedResult === "boolean" ? cachedResult : null,
1206
+ cached: true,
1207
+ port: 0,
1208
+ portCached: false
1209
+ };
1210
+ }
1211
+ } catch (_error) {
1212
+ cachedResult = void 0;
1213
+ }
1144
1214
  }
1145
- const mxIndex = 0;
1146
- const mxRecord = mxRecords[mxIndex];
1147
- for (let attempt = 0; attempt < retryAttempts; attempt++) {
1148
- const result = await attemptVerification({
1149
- mxRecord,
1150
- local,
1151
- domain,
1152
- port,
1153
- timeout,
1154
- log,
1155
- attempt
1156
- });
1157
- if (result !== null) {
1158
- return result;
1215
+ const smtpPortCacheStore = cache ? getCacheStore(cache, "smtpPort") : null;
1216
+ if (smtpPortCacheStore) {
1217
+ let cachedPort;
1218
+ try {
1219
+ cachedPort = await smtpPortCacheStore.get(mxHost);
1220
+ } catch (_error) {
1221
+ cachedPort = null;
1159
1222
  }
1160
- if (attempt < retryAttempts - 1) {
1161
- await new Promise((resolve) => setTimeout(resolve, Math.min(1e3 * (attempt + 1), 3e3)));
1223
+ if (cachedPort) {
1224
+ log(`Using cached port: ${cachedPort}`);
1225
+ const result = await testSMTPConnection({
1226
+ mxHost,
1227
+ port: cachedPort,
1228
+ local,
1229
+ domain,
1230
+ timeout,
1231
+ tlsConfig,
1232
+ hostname,
1233
+ useVRFY,
1234
+ sequence,
1235
+ log
1236
+ });
1237
+ if (smtpCacheStore && result !== void 0) {
1238
+ try {
1239
+ await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, result);
1240
+ log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
1241
+ } catch (_error) {
1242
+ }
1243
+ }
1244
+ return { result, cached: false, port: cachedPort, portCached: true };
1162
1245
  }
1163
1246
  }
1164
- return null;
1247
+ for (const port of ports) {
1248
+ log(`Testing port ${port}`);
1249
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1250
+ if (attempt > 0) {
1251
+ const delay = Math.min(1e3 * 2 ** (attempt - 1), 5e3);
1252
+ log(`Retry ${attempt + 1}, waiting ${delay}ms`);
1253
+ await new Promise((resolve) => setTimeout(resolve, delay));
1254
+ }
1255
+ const result = await testSMTPConnection({
1256
+ mxHost,
1257
+ port,
1258
+ local,
1259
+ domain,
1260
+ timeout,
1261
+ tlsConfig,
1262
+ hostname,
1263
+ useVRFY,
1264
+ sequence,
1265
+ log
1266
+ });
1267
+ if (smtpCacheStore && result !== void 0) {
1268
+ try {
1269
+ await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, result);
1270
+ log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
1271
+ } catch (_error) {
1272
+ }
1273
+ }
1274
+ if (result !== null) {
1275
+ if (smtpPortCacheStore) {
1276
+ try {
1277
+ await smtpPortCacheStore.set(mxHost, port);
1278
+ log(`Cached port ${port} for ${mxHost}`);
1279
+ } catch (_error) {
1280
+ }
1281
+ }
1282
+ return { result, cached: false, port, portCached: false };
1283
+ }
1284
+ }
1285
+ }
1286
+ log("All ports failed");
1287
+ return { result: null, cached: false, port: 0, portCached: false };
1165
1288
  }
1166
- async function attemptVerification(params) {
1167
- const { mxRecord, local, domain, port, timeout, log, attempt } = params;
1289
+ async function testSMTPConnection(params) {
1290
+ const { mxHost, port, local, domain, timeout, tlsConfig, hostname, useVRFY, sequence, log } = params;
1291
+ const portConfig = PORT_CONFIGS[port] || { tls: false, starttls: false };
1292
+ const useTLS = tlsConfig !== false && (portConfig.tls || portConfig.starttls);
1293
+ const implicitTLS = portConfig.tls;
1294
+ const defaultSequence = {
1295
+ steps: [SMTPStep.GREETING, SMTPStep.EHLO, SMTPStep.MAIL_FROM, SMTPStep.RCPT_TO]
1296
+ };
1297
+ const activeSequence = sequence || defaultSequence;
1298
+ if (port === 25) {
1299
+ activeSequence.steps = activeSequence.steps.map((step) => step === SMTPStep.EHLO ? SMTPStep.HELO : step);
1300
+ }
1301
+ const tlsOptions = {
1302
+ host: mxHost,
1303
+ servername: isIPAddress(mxHost) ? void 0 : mxHost,
1304
+ // Don't set servername for IP addresses
1305
+ rejectUnauthorized: false,
1306
+ minVersion: "TLSv1.2",
1307
+ ...typeof tlsConfig === "object" ? tlsConfig : {}
1308
+ };
1168
1309
  return new Promise((resolve) => {
1169
- log(`[verifyMailboxSMTP] connecting to ${mxRecord}:${port} (attempt ${attempt + 1})`);
1170
- const socket = net.connect({
1171
- host: mxRecord,
1172
- port
1173
- });
1174
- let resTimeout = null;
1310
+ let socket;
1311
+ let buffer = "";
1312
+ let isTLS = implicitTLS;
1313
+ let currentStepIndex = 0;
1175
1314
  let resolved = false;
1176
- let cleaned = false;
1315
+ let supportsSTARTTLS = false;
1316
+ let supportsVRFY = false;
1177
1317
  const cleanup = () => {
1178
- if (cleaned)
1179
- return;
1180
- cleaned = true;
1181
- if (resTimeout) {
1182
- clearTimeout(resTimeout);
1183
- resTimeout = null;
1184
- }
1185
- if (socket && !socket.destroyed) {
1186
- socket.removeAllListeners();
1187
- socket.destroy();
1188
- }
1189
- };
1190
- const ret = (result) => {
1191
1318
  if (resolved)
1192
1319
  return;
1193
1320
  resolved = true;
1194
- if (!(socket === null || socket === void 0 ? void 0 : socket.destroyed)) {
1195
- log("[verifyMailboxSMTP] closing socket");
1321
+ try {
1196
1322
  socket === null || socket === void 0 ? void 0 : socket.write("QUIT\r\n");
1197
- socket === null || socket === void 0 ? void 0 : socket.end();
1323
+ } catch {
1198
1324
  }
1325
+ setTimeout(() => socket === null || socket === void 0 ? void 0 : socket.destroy(), 100);
1326
+ };
1327
+ const finish = (result, reason) => {
1328
+ if (resolved)
1329
+ return;
1330
+ log(`${port}: ${reason || (result ? "valid" : "invalid")}`);
1199
1331
  cleanup();
1200
1332
  resolve(result);
1201
1333
  };
1202
- const messages = [`HELO ${domain}`, `MAIL FROM: <${local}@${domain}>`, `RCPT TO: <${local}@${domain}>`];
1203
- log("[verifyMailboxSMTP] writing messages", messages);
1204
- socket.on("data", (data) => {
1205
- const dataString = String(data);
1206
- log("[verifyMailboxSMTP] got data", dataString);
1207
- if (isInvalidMailboxError(dataString))
1208
- return ret(false);
1209
- if (isOverQuota(dataString))
1210
- return ret(false);
1211
- if (!dataString.includes("220") && !dataString.includes("250"))
1212
- return ret(null);
1213
- if (isMultilineGreet(dataString))
1334
+ const sendCommand = (cmd) => {
1335
+ if (resolved)
1214
1336
  return;
1215
- if (messages.length > 0) {
1216
- const message = messages.shift();
1217
- log("[verifyMailboxSMTP] writing message", message);
1218
- return socket.write(`${message}\r
1337
+ log(`\u2192 ${cmd}`);
1338
+ socket === null || socket === void 0 ? void 0 : socket.write(`${cmd}\r
1219
1339
  `);
1340
+ };
1341
+ const nextStep = () => {
1342
+ currentStepIndex++;
1343
+ if (currentStepIndex >= activeSequence.steps.length) {
1344
+ finish(true, "sequence_complete");
1345
+ return;
1220
1346
  }
1221
- ret(true);
1222
- });
1223
- socket.on("error", (err) => {
1224
- log("[verifyMailboxSMTP] error in socket", err);
1225
- ret(null);
1226
- });
1227
- socket.on("close", (err) => {
1228
- if (!resolved) {
1229
- log("[verifyMailboxSMTP] close socket", err);
1230
- ret(null);
1347
+ executeStep(activeSequence.steps[currentStepIndex]);
1348
+ };
1349
+ const executeStep = (step) => {
1350
+ if (resolved)
1351
+ return;
1352
+ switch (step) {
1353
+ case SMTPStep.EHLO:
1354
+ sendCommand(`EHLO ${hostname}`);
1355
+ break;
1356
+ case SMTPStep.HELO:
1357
+ sendCommand(`HELO ${domain}`);
1358
+ break;
1359
+ case SMTPStep.GREETING:
1360
+ break;
1361
+ case SMTPStep.STARTTLS:
1362
+ sendCommand("STARTTLS");
1363
+ break;
1364
+ case SMTPStep.MAIL_FROM: {
1365
+ const from = activeSequence.from || "<>";
1366
+ sendCommand(`MAIL FROM:${from}`);
1367
+ break;
1368
+ }
1369
+ case SMTPStep.RCPT_TO:
1370
+ sendCommand(`RCPT TO:<${local}@${domain}>`);
1371
+ break;
1372
+ case SMTPStep.VRFY: {
1373
+ const vrfyTarget = activeSequence.vrfyTarget || local;
1374
+ sendCommand(`VRFY ${vrfyTarget}`);
1375
+ break;
1376
+ }
1377
+ case SMTPStep.QUIT:
1378
+ sendCommand("QUIT");
1379
+ break;
1231
1380
  }
1232
- });
1233
- socket.on("timeout", () => {
1234
- log("[verifyMailboxSMTP] timeout socket");
1235
- ret(null);
1236
- });
1237
- resTimeout = setTimeout(() => {
1238
- log(`[verifyMailboxSMTP] timed out (${timeout} ms)`);
1239
- if (!resolved) {
1240
- socket.destroy();
1241
- ret(null);
1381
+ };
1382
+ const processResponse = (response) => {
1383
+ if (resolved)
1384
+ return;
1385
+ const code = response.substring(0, 3);
1386
+ const isMultiline = response.length > 3 && response[3] === "-";
1387
+ log(`\u2190 ${response}`);
1388
+ if (isMultilineGreet(response)) {
1389
+ return;
1242
1390
  }
1243
- }, timeout);
1391
+ if (isOverQuota(response)) {
1392
+ finish(false, "over_quota");
1393
+ return;
1394
+ }
1395
+ if (isInvalidMailboxError(response)) {
1396
+ finish(false, "not_found");
1397
+ return;
1398
+ }
1399
+ if (isMultiline) {
1400
+ const currentStep2 = activeSequence.steps[currentStepIndex];
1401
+ if (currentStep2 === SMTPStep.EHLO && code === "250") {
1402
+ const upper = response.toUpperCase();
1403
+ if (upper.includes("STARTTLS"))
1404
+ supportsSTARTTLS = true;
1405
+ if (upper.includes("VRFY"))
1406
+ supportsVRFY = true;
1407
+ }
1408
+ if (currentStep2 === SMTPStep.HELO && code === "250") {
1409
+ const upper = response.toUpperCase();
1410
+ if (upper.includes("VRFY"))
1411
+ supportsVRFY = true;
1412
+ }
1413
+ return;
1414
+ }
1415
+ if (!response.includes("220") && !response.includes("250") && !response.includes("550") && !response.includes("552")) {
1416
+ finish(null, "unrecognized_response");
1417
+ return;
1418
+ }
1419
+ const currentStep = activeSequence.steps[currentStepIndex];
1420
+ switch (currentStep) {
1421
+ case SMTPStep.GREETING:
1422
+ if (code.startsWith("220")) {
1423
+ nextStep();
1424
+ } else {
1425
+ finish(null, "no_greeting");
1426
+ }
1427
+ break;
1428
+ case SMTPStep.EHLO:
1429
+ if (code.startsWith("250")) {
1430
+ const hasSTARTTLS = activeSequence.steps.includes(SMTPStep.STARTTLS);
1431
+ if (!isTLS && useTLS && supportsSTARTTLS && !implicitTLS && hasSTARTTLS) {
1432
+ currentStepIndex = activeSequence.steps.indexOf(SMTPStep.STARTTLS);
1433
+ executeStep(SMTPStep.STARTTLS);
1434
+ } else {
1435
+ nextStep();
1436
+ }
1437
+ } else {
1438
+ finish(null, "ehlo_failed");
1439
+ }
1440
+ break;
1441
+ case SMTPStep.HELO:
1442
+ if (code.startsWith("250")) {
1443
+ nextStep();
1444
+ } else {
1445
+ finish(null, "helo_failed");
1446
+ }
1447
+ break;
1448
+ case SMTPStep.STARTTLS:
1449
+ if (code.startsWith("220")) {
1450
+ const plainSocket = socket;
1451
+ socket = tls.connect({
1452
+ ...tlsOptions,
1453
+ socket: plainSocket,
1454
+ servername: isIPAddress(mxHost) ? void 0 : mxHost
1455
+ }, () => {
1456
+ isTLS = true;
1457
+ log("TLS upgraded");
1458
+ buffer = "";
1459
+ const starttlsIndex = activeSequence.steps.indexOf(SMTPStep.STARTTLS);
1460
+ currentStepIndex = starttlsIndex;
1461
+ nextStep();
1462
+ });
1463
+ socket.on("data", handleData);
1464
+ socket.on("error", () => finish(null, "tls_error"));
1465
+ } else {
1466
+ nextStep();
1467
+ }
1468
+ break;
1469
+ case SMTPStep.MAIL_FROM:
1470
+ if (code.startsWith("250")) {
1471
+ nextStep();
1472
+ } else {
1473
+ finish(null, "mail_from_rejected");
1474
+ }
1475
+ break;
1476
+ case SMTPStep.RCPT_TO:
1477
+ if (code.startsWith("250") || code.startsWith("251")) {
1478
+ finish(true, "valid");
1479
+ } else if (code.startsWith("552") || code.startsWith("452")) {
1480
+ finish(false, "over_quota");
1481
+ } else if (code.startsWith("4")) {
1482
+ finish(null, "temporary_failure");
1483
+ } else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(SMTPStep.VRFY)) {
1484
+ currentStepIndex = activeSequence.steps.indexOf(SMTPStep.VRFY);
1485
+ executeStep(SMTPStep.VRFY);
1486
+ } else {
1487
+ finish(null, "ambiguous");
1488
+ }
1489
+ break;
1490
+ case SMTPStep.VRFY:
1491
+ if (code.startsWith("250") || code.startsWith("252")) {
1492
+ finish(true, "vrfy_valid");
1493
+ } else if (code.startsWith("550")) {
1494
+ finish(false, "vrfy_invalid");
1495
+ } else {
1496
+ finish(null, "vrfy_failed");
1497
+ }
1498
+ break;
1499
+ case SMTPStep.QUIT:
1500
+ if (code.startsWith("221")) {
1501
+ finish(null, "quit_received");
1502
+ }
1503
+ break;
1504
+ }
1505
+ };
1506
+ const handleData = (data) => {
1507
+ if (resolved)
1508
+ return;
1509
+ buffer += data.toString();
1510
+ let pos;
1511
+ while ((pos = buffer.indexOf("\r\n")) !== -1) {
1512
+ const line = buffer.substring(0, pos);
1513
+ buffer = buffer.substring(pos + 2);
1514
+ processResponse(line.trim());
1515
+ }
1516
+ };
1517
+ if (port < 0 || port > 65535 || !Number.isInteger(port)) {
1518
+ finish(null, "invalid_port");
1519
+ return;
1520
+ }
1521
+ if (implicitTLS) {
1522
+ const connectOptions = {
1523
+ ...tlsOptions,
1524
+ port,
1525
+ servername: isIPAddress(mxHost) ? void 0 : mxHost
1526
+ };
1527
+ socket = tls.connect(connectOptions, () => {
1528
+ log(`Connected to ${mxHost}:${port} with TLS`);
1529
+ socket.on("data", handleData);
1530
+ });
1531
+ } else {
1532
+ socket = net.connect({ host: mxHost, port }, () => {
1533
+ log(`Connected to ${mxHost}:${port}`);
1534
+ socket.on("data", handleData);
1535
+ });
1536
+ }
1537
+ if (activeSequence.steps.length === 0) {
1538
+ finish(true, "sequence_complete");
1539
+ return;
1540
+ }
1541
+ const firstStep = activeSequence.steps[0];
1542
+ if (firstStep !== SMTPStep.GREETING) {
1543
+ executeStep(firstStep);
1544
+ }
1545
+ socket.setTimeout(timeout, () => finish(null, "timeout"));
1546
+ socket.on("error", () => finish(null, "connection_error"));
1547
+ socket.on("close", () => {
1548
+ if (!resolved)
1549
+ finish(null, "connection_closed");
1550
+ });
1244
1551
  });
1245
1552
  }
1246
1553
 
1247
- var VerificationErrorCode;
1248
- (function(VerificationErrorCode2) {
1249
- VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
1250
- VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
1251
- VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
1252
- VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
1253
- VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
1254
- VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
1255
- VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
1256
- VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
1257
- VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
1258
- VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
1259
- })(VerificationErrorCode || (VerificationErrorCode = {}));
1260
-
1261
1554
  async function isValidEmailDomain(emailOrDomain, cache) {
1262
1555
  let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
1263
1556
  if (!emailDomain) {
@@ -2350,17 +2643,22 @@ async function verifyEmail(params) {
2350
2643
  domainPort = domainPorts[mxDomain.domain];
2351
2644
  }
2352
2645
  }
2353
- const smtpResult = await verifyMailboxSMTP({
2646
+ const { result: smtpResult, cached, portCached, port } = await verifyMailboxSMTP({
2354
2647
  local,
2355
2648
  domain,
2356
2649
  mxRecords,
2357
- timeout,
2358
- debug,
2359
- port: domainPort,
2360
- retryAttempts: params.retryAttempts
2650
+ options: {
2651
+ cache: params.cache,
2652
+ ports: domainPort ? [domainPort] : void 0,
2653
+ timeout,
2654
+ debug,
2655
+ maxRetries: params.retryAttempts
2656
+ }
2361
2657
  });
2362
2658
  await smtpCacheInstance.set(cacheKey, smtpResult);
2363
2659
  result.validSmtp = smtpResult;
2660
+ if (result.metadata)
2661
+ result.metadata.cached = cached;
2364
2662
  log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
2365
2663
  }
2366
2664
  if (result.validSmtp === false && result.metadata) {
@@ -2385,5 +2683,5 @@ async function verifyEmail(params) {
2385
2683
  return result;
2386
2684
  }
2387
2685
 
2388
- export { COMMON_EMAIL_DOMAINS, DEFAULT_CACHE_OPTIONS, LRUAdapter, RedisAdapter, VerificationErrorCode, cleanNameForAlgrothin, clearDefaultCache, defaultDomainSuggestionMethod, defaultNameDetectionMethod, detectName, detectNameForAlgrothin, detectNameFromEmail, domainPorts, getCacheStore, getDefaultCache, getDomainAge, getDomainRegistrationStatus, getDomainSimilarity, isCommonDomain, isDisposableEmail, isFreeEmail, isValidEmail, isValidEmailDomain, resetDefaultCache, suggestDomain, suggestEmailDomain, verifyEmail, verifyEmailBatch };
2686
+ export { COMMON_EMAIL_DOMAINS, DEFAULT_CACHE_OPTIONS, LRUAdapter, RedisAdapter, SMTPStep, VerificationErrorCode, cleanNameForAlgrothin, clearDefaultCache, defaultDomainSuggestionMethod, defaultNameDetectionMethod, detectName, detectNameForAlgrothin, detectNameFromEmail, domainPorts, getCacheStore, getDefaultCache, getDomainAge, getDomainRegistrationStatus, getDomainSimilarity, isCommonDomain, isDisposableEmail, isFreeEmail, isValidEmail, isValidEmailDomain, resetDefaultCache, suggestDomain, suggestEmailDomain, verifyEmail, verifyEmailBatch };
2389
2687
  //# sourceMappingURL=index.esm.js.map