@emailcheck/email-validator-js 2.13.1-beta.1 → 2.13.1-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 +54 -43
- package/dist/index.d.ts +2 -2
- package/dist/index.esm.js +104 -59
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +104 -59
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +14 -22
- package/dist/whois.d.ts +2 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -129,17 +129,16 @@ const result = await verifyEmail({
|
|
|
129
129
|
timeout: 3000
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
console.log(result.
|
|
133
|
-
console.log(result.
|
|
134
|
-
console.log(result.
|
|
135
|
-
console.log(result.smtp.valid); // true or false
|
|
132
|
+
console.log(result.validFormat); // true
|
|
133
|
+
console.log(result.validMx); // true or false
|
|
134
|
+
console.log(result.validSmtp); // true or false
|
|
136
135
|
```
|
|
137
136
|
|
|
138
137
|
## API Reference
|
|
139
138
|
|
|
140
139
|
### Core Functions
|
|
141
140
|
|
|
142
|
-
#### `verifyEmail(params: IVerifyEmailParams): Promise<
|
|
141
|
+
#### `verifyEmail(params: IVerifyEmailParams): Promise<VerificationResult>`
|
|
143
142
|
|
|
144
143
|
Comprehensive email verification with detailed results and error codes.
|
|
145
144
|
|
|
@@ -161,33 +160,26 @@ Comprehensive email verification with detailed results and error codes.
|
|
|
161
160
|
- `checkDomainAge` (boolean): Check domain age (default: false)
|
|
162
161
|
- `checkDomainRegistration` (boolean): Check domain registration status (default: false)
|
|
163
162
|
- `whoisTimeout` (number): WHOIS lookup timeout (default: 5000)
|
|
163
|
+
- `debug` (boolean): Enable debug logging including WHOIS lookups (default: false)
|
|
164
164
|
- `cache` (ICache): Optional custom cache instance
|
|
165
165
|
|
|
166
166
|
**Returns:**
|
|
167
167
|
```typescript
|
|
168
168
|
{
|
|
169
|
-
valid: boolean;
|
|
170
169
|
email: string;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
valid: boolean | null;
|
|
177
|
-
mxRecords?: string[];
|
|
178
|
-
error?: VerificationErrorCode;
|
|
179
|
-
};
|
|
180
|
-
smtp: {
|
|
181
|
-
valid: boolean | null;
|
|
182
|
-
error?: VerificationErrorCode;
|
|
183
|
-
};
|
|
184
|
-
disposable: boolean;
|
|
185
|
-
freeProvider: boolean;
|
|
170
|
+
validFormat: boolean;
|
|
171
|
+
validMx: boolean | null;
|
|
172
|
+
validSmtp: boolean | null;
|
|
173
|
+
isDisposable: boolean;
|
|
174
|
+
isFree: boolean;
|
|
186
175
|
detectedName?: DetectedName | null;
|
|
176
|
+
domainAge?: DomainAgeInfo | null;
|
|
177
|
+
domainRegistration?: DomainRegistrationInfo | null;
|
|
187
178
|
domainSuggestion?: DomainSuggestion | null;
|
|
188
179
|
metadata?: {
|
|
189
180
|
verificationTime: number;
|
|
190
181
|
cached: boolean;
|
|
182
|
+
error?: VerificationErrorCode;
|
|
191
183
|
};
|
|
192
184
|
}
|
|
193
185
|
```
|
|
@@ -199,7 +191,6 @@ Verify multiple emails in parallel with concurrency control.
|
|
|
199
191
|
**Parameters:**
|
|
200
192
|
- `emailAddresses` (string[], required): Array of emails to verify
|
|
201
193
|
- `concurrency` (number): Parallel processing limit (default: 5)
|
|
202
|
-
- `detailed` (boolean): Return detailed results (default: false)
|
|
203
194
|
- `detectName` (boolean): Detect names from email addresses
|
|
204
195
|
- `suggestDomain` (boolean): Enable domain typo suggestions
|
|
205
196
|
- Other parameters from `verifyEmail`
|
|
@@ -207,7 +198,7 @@ Verify multiple emails in parallel with concurrency control.
|
|
|
207
198
|
**Returns:**
|
|
208
199
|
```typescript
|
|
209
200
|
{
|
|
210
|
-
results: Map<string,
|
|
201
|
+
results: Map<string, VerificationResult>;
|
|
211
202
|
summary: {
|
|
212
203
|
total: number;
|
|
213
204
|
valid: number;
|
|
@@ -395,22 +386,36 @@ const status = await getDomainRegistrationStatus('mydomain.com');
|
|
|
395
386
|
|
|
396
387
|
### Utility Functions
|
|
397
388
|
|
|
398
|
-
#### `isDisposableEmail(emailOrDomain: string): boolean`
|
|
389
|
+
#### `isDisposableEmail(emailOrDomain: string, cache?: ICache, options?: { skipMxCheck?: boolean; skipDomain?: boolean }): boolean`
|
|
399
390
|
Check if email uses a disposable provider.
|
|
400
391
|
|
|
401
392
|
```typescript
|
|
393
|
+
// Basic usage
|
|
402
394
|
isDisposableEmail('user@tempmail.com'); // true
|
|
403
395
|
isDisposableEmail('tempmail.com'); // true
|
|
404
396
|
isDisposableEmail('gmail.com'); // false
|
|
397
|
+
|
|
398
|
+
// With options
|
|
399
|
+
isDisposableEmail('user@tempmail.com', null, {
|
|
400
|
+
skipMxCheck: true, // Skip MX record validation
|
|
401
|
+
skipDomain: true // Skip domain validation
|
|
402
|
+
}); // true
|
|
405
403
|
```
|
|
406
404
|
|
|
407
|
-
#### `isFreeEmail(emailOrDomain: string): boolean`
|
|
405
|
+
#### `isFreeEmail(emailOrDomain: string, cache?: ICache, options?: { skipMxCheck?: boolean; skipDomain?: boolean }): boolean`
|
|
408
406
|
Check if email uses a free provider.
|
|
409
407
|
|
|
410
408
|
```typescript
|
|
409
|
+
// Basic usage
|
|
411
410
|
isFreeEmail('user@gmail.com'); // true
|
|
412
411
|
isFreeEmail('yahoo.com'); // true
|
|
413
412
|
isFreeEmail('corporate.com'); // false
|
|
413
|
+
|
|
414
|
+
// With options
|
|
415
|
+
isFreeEmail('user@gmail.com', null, {
|
|
416
|
+
skipMxCheck: true, // Skip MX record validation
|
|
417
|
+
skipDomain: true // Skip domain validation
|
|
418
|
+
}); // true
|
|
414
419
|
```
|
|
415
420
|
|
|
416
421
|
#### `isValidEmail(emailAddress: string): boolean`
|
|
@@ -583,10 +588,9 @@ const result = await verifyEmail({
|
|
|
583
588
|
verifySmtp: true,
|
|
584
589
|
timeout: 3000
|
|
585
590
|
});
|
|
586
|
-
console.log(result.
|
|
587
|
-
console.log(result.
|
|
588
|
-
console.log(result.
|
|
589
|
-
console.log(result.smtp.valid); // true
|
|
591
|
+
console.log(result.validFormat); // true
|
|
592
|
+
console.log(result.validMx); // true
|
|
593
|
+
console.log(result.validSmtp); // true
|
|
590
594
|
```
|
|
591
595
|
|
|
592
596
|
### Detailed Verification (NEW)
|
|
@@ -600,10 +604,11 @@ const result = await verifyEmail({
|
|
|
600
604
|
checkDisposable: true,
|
|
601
605
|
checkFree: true
|
|
602
606
|
});
|
|
603
|
-
// result.
|
|
604
|
-
// result.
|
|
605
|
-
// result.
|
|
606
|
-
// result.
|
|
607
|
+
// result.validFormat: true
|
|
608
|
+
// result.validMx: true
|
|
609
|
+
// result.validSmtp: true
|
|
610
|
+
// result.isDisposable: false
|
|
611
|
+
// result.isFree: false
|
|
607
612
|
// result.metadata.verificationTime: 125
|
|
608
613
|
```
|
|
609
614
|
|
|
@@ -617,7 +622,8 @@ const result = await verifyEmailBatch({
|
|
|
617
622
|
emailAddresses: emails,
|
|
618
623
|
concurrency: 5,
|
|
619
624
|
verifyMx: true,
|
|
620
|
-
|
|
625
|
+
checkDisposable: true,
|
|
626
|
+
checkFree: true
|
|
621
627
|
});
|
|
622
628
|
// result.summary.valid: 2
|
|
623
629
|
// result.summary.invalid: 1
|
|
@@ -697,16 +703,15 @@ const result = await verifyEmail({
|
|
|
697
703
|
verifyMx: true,
|
|
698
704
|
verifySmtp: true
|
|
699
705
|
});
|
|
700
|
-
// result.
|
|
701
|
-
// result.
|
|
702
|
-
// result.
|
|
703
|
-
// result.smtp.valid: null (couldn't be performed)
|
|
706
|
+
// result.validFormat: true (format is valid)
|
|
707
|
+
// result.validMx: false (no MX records)
|
|
708
|
+
// result.validSmtp: null (couldn't be performed)
|
|
704
709
|
```
|
|
705
710
|
|
|
706
711
|
### Using Detailed Verification for Better Insights
|
|
707
712
|
|
|
708
713
|
```typescript
|
|
709
|
-
const
|
|
714
|
+
const result = await verifyEmail({
|
|
710
715
|
emailAddress: 'user@suspicious-domain.com',
|
|
711
716
|
verifyMx: true,
|
|
712
717
|
verifySmtp: true,
|
|
@@ -714,8 +719,14 @@ const detailed = await verifyEmail({
|
|
|
714
719
|
checkFree: true
|
|
715
720
|
});
|
|
716
721
|
|
|
717
|
-
if (!
|
|
718
|
-
|
|
722
|
+
if (!result.validFormat) {
|
|
723
|
+
console.log('Invalid email format');
|
|
724
|
+
} else if (!result.validMx) {
|
|
725
|
+
console.log('Invalid domain - no MX records');
|
|
726
|
+
} else if (result.isDisposable) {
|
|
727
|
+
console.log('Disposable email detected');
|
|
728
|
+
} else if (result.metadata?.error) {
|
|
729
|
+
switch (result.metadata.error) {
|
|
719
730
|
case VerificationErrorCode.DISPOSABLE_EMAIL:
|
|
720
731
|
console.log('Rejected: Disposable email');
|
|
721
732
|
break;
|
|
@@ -755,7 +766,7 @@ console.log(`Time: ${batch.summary.processingTime}ms`);
|
|
|
755
766
|
// Filter out invalid emails
|
|
756
767
|
const validEmails = [];
|
|
757
768
|
for (const [email, result] of batch.results) {
|
|
758
|
-
if (result.
|
|
769
|
+
if (result.validFormat) {
|
|
759
770
|
validEmails.push(email);
|
|
760
771
|
}
|
|
761
772
|
}
|
|
@@ -1046,7 +1057,7 @@ process.on('SIGTERM', async () => {
|
|
|
1046
1057
|
});
|
|
1047
1058
|
```
|
|
1048
1059
|
|
|
1049
|
-
**Note:** Yahoo, Hotmail, and some providers always return `result.
|
|
1060
|
+
**Note:** Yahoo, Hotmail, and some providers always return `result.validSmtp: true` as they don't allow mailbox verification.
|
|
1050
1061
|
|
|
1051
1062
|
## 🌐 Serverless Deployment
|
|
1052
1063
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ICache } from './cache-interface';
|
|
2
|
-
import { type
|
|
2
|
+
import { type IVerifyEmailParams, type VerificationResult } from './types';
|
|
3
3
|
export { verifyEmailBatch } from './batch';
|
|
4
4
|
export { clearAllCaches } from './cache';
|
|
5
5
|
export { COMMON_EMAIL_DOMAINS, defaultDomainSuggestionMethod, getDomainSimilarity, isCommonDomain, suggestDomain, suggestEmailDomain, } from './domain-suggester';
|
|
@@ -13,4 +13,4 @@ export declare const domainPorts: Record<string, number>;
|
|
|
13
13
|
/**
|
|
14
14
|
* Verify email address
|
|
15
15
|
*/
|
|
16
|
-
export declare function verifyEmail(params: IVerifyEmailParams): Promise<
|
|
16
|
+
export declare function verifyEmail(params: IVerifyEmailParams): Promise<VerificationResult>;
|
package/dist/index.esm.js
CHANGED
|
@@ -1732,37 +1732,50 @@ const WHOIS_SERVERS = {
|
|
|
1732
1732
|
ar: "whois.nic.ar",
|
|
1733
1733
|
cl: "whois.nic.cl"
|
|
1734
1734
|
};
|
|
1735
|
-
function queryWhoisServer(domain, server, timeout = 5e3) {
|
|
1735
|
+
function queryWhoisServer(domain, server, timeout = 5e3, debug = false) {
|
|
1736
|
+
const log = debug ? console.debug : (..._args) => {
|
|
1737
|
+
};
|
|
1736
1738
|
return new Promise((resolve, reject) => {
|
|
1737
1739
|
const client = new net.Socket();
|
|
1738
1740
|
let data = "";
|
|
1741
|
+
log(`[whois] querying ${server} for domain ${domain}`);
|
|
1739
1742
|
const timer = setTimeout(() => {
|
|
1743
|
+
log(`[whois] timeout after ${timeout}ms for ${domain} at ${server}`);
|
|
1740
1744
|
client.destroy();
|
|
1741
1745
|
reject(new Error("WHOIS query timeout"));
|
|
1742
1746
|
}, timeout);
|
|
1743
1747
|
client.connect(43, server, () => {
|
|
1748
|
+
log(`[whois] connected to ${server}, sending query for ${domain}`);
|
|
1744
1749
|
client.write(`${domain}\r
|
|
1745
1750
|
`);
|
|
1746
1751
|
});
|
|
1747
1752
|
client.on("data", (chunk) => {
|
|
1748
|
-
|
|
1753
|
+
const chunkStr = chunk.toString();
|
|
1754
|
+
data += chunkStr;
|
|
1755
|
+
log(`[whois] received ${chunkStr.length} bytes from ${server}`);
|
|
1749
1756
|
});
|
|
1750
1757
|
client.on("close", () => {
|
|
1751
1758
|
clearTimeout(timer);
|
|
1759
|
+
log(`[whois] connection closed, received total ${data.length} bytes from ${server}`);
|
|
1752
1760
|
resolve(data);
|
|
1753
1761
|
});
|
|
1754
1762
|
client.on("error", (err) => {
|
|
1755
1763
|
clearTimeout(timer);
|
|
1764
|
+
log(`[whois] error querying ${server}: ${err.message}`);
|
|
1756
1765
|
reject(err);
|
|
1757
1766
|
});
|
|
1758
1767
|
});
|
|
1759
1768
|
}
|
|
1760
|
-
async function getWhoisData(domain, timeout = 5e3) {
|
|
1769
|
+
async function getWhoisData(domain, timeout = 5e3, debug = false) {
|
|
1761
1770
|
var _a;
|
|
1771
|
+
const log = debug ? console.debug : (..._args) => {
|
|
1772
|
+
};
|
|
1762
1773
|
const cacheKey = `whois:${domain}`;
|
|
1763
1774
|
const cache = whoisCacheStore();
|
|
1775
|
+
log(`[whois] getting WHOIS data for ${domain}`);
|
|
1764
1776
|
const cached = await cache.get(cacheKey);
|
|
1765
1777
|
if (cached !== null && cached !== void 0) {
|
|
1778
|
+
log(`[whois] using cached data for ${domain}`);
|
|
1766
1779
|
return cached;
|
|
1767
1780
|
}
|
|
1768
1781
|
try {
|
|
@@ -1770,41 +1783,55 @@ async function getWhoisData(domain, timeout = 5e3) {
|
|
|
1770
1783
|
if (!tld) {
|
|
1771
1784
|
throw new Error("Invalid domain");
|
|
1772
1785
|
}
|
|
1786
|
+
log(`[whois] extracted TLD: ${tld} for domain: ${domain}`);
|
|
1773
1787
|
const whoisServer = WHOIS_SERVERS[tld];
|
|
1774
1788
|
if (!whoisServer) {
|
|
1789
|
+
log(`[whois] no specific server for TLD ${tld}, trying IANA`);
|
|
1775
1790
|
const defaultServer = "whois.iana.org";
|
|
1776
|
-
const ianaResponse = await queryWhoisServer(domain, defaultServer, timeout);
|
|
1791
|
+
const ianaResponse = await queryWhoisServer(domain, defaultServer, timeout, debug);
|
|
1777
1792
|
const referMatch = ianaResponse.match(/refer:\s+(\S+)/i);
|
|
1778
1793
|
if (referMatch === null || referMatch === void 0 ? void 0 : referMatch[1]) {
|
|
1779
1794
|
const referredServer = referMatch[1];
|
|
1780
|
-
|
|
1795
|
+
log(`[whois] IANA referred to ${referredServer} for ${domain}`);
|
|
1796
|
+
const whoisResponse2 = await queryWhoisServer(domain, referredServer, timeout, debug);
|
|
1781
1797
|
const whoisData3 = parseWhoisData({ rawData: whoisResponse2, domain });
|
|
1782
1798
|
await cache.set(cacheKey, whoisData3);
|
|
1799
|
+
log(`[whois] successfully retrieved and cached WHOIS data from referred server for ${domain}`);
|
|
1783
1800
|
return whoisData3;
|
|
1784
1801
|
}
|
|
1785
1802
|
const whoisData2 = parseWhoisData({ rawData: ianaResponse, domain });
|
|
1786
1803
|
await cache.set(cacheKey, whoisData2);
|
|
1804
|
+
log(`[whois] successfully retrieved and cached WHOIS data from IANA for ${domain}`);
|
|
1787
1805
|
return whoisData2;
|
|
1788
1806
|
}
|
|
1789
|
-
|
|
1807
|
+
log(`[whois] using WHOIS server ${whoisServer} for TLD ${tld}`);
|
|
1808
|
+
const whoisResponse = await queryWhoisServer(domain, whoisServer, timeout, debug);
|
|
1790
1809
|
const whoisData = parseWhoisData({ rawData: whoisResponse, domain });
|
|
1791
1810
|
await cache.set(cacheKey, whoisData);
|
|
1811
|
+
log(`[whois] successfully retrieved and cached WHOIS data for ${domain}`);
|
|
1792
1812
|
return whoisData;
|
|
1793
1813
|
} catch (_error) {
|
|
1814
|
+
log(`[whois] failed to get WHOIS data for ${domain}: ${_error instanceof Error ? _error.message : "Unknown error"}`);
|
|
1794
1815
|
return null;
|
|
1795
1816
|
}
|
|
1796
1817
|
}
|
|
1797
|
-
async function getDomainAge(domain, timeout = 5e3) {
|
|
1818
|
+
async function getDomainAge(domain, timeout = 5e3, debug = false) {
|
|
1819
|
+
const log = debug ? console.debug : (..._args) => {
|
|
1820
|
+
};
|
|
1798
1821
|
try {
|
|
1799
1822
|
const cleanDomain = domain.replace(/^https?:\/\//, "").split("/")[0].split("@").pop();
|
|
1800
1823
|
if (!cleanDomain) {
|
|
1824
|
+
log(`[whois] invalid domain format: ${domain}`);
|
|
1801
1825
|
return null;
|
|
1802
1826
|
}
|
|
1827
|
+
log(`[whois] checking domain age for ${cleanDomain}`);
|
|
1803
1828
|
if (!isValid(cleanDomain)) {
|
|
1829
|
+
log(`[whois] domain validation failed: ${cleanDomain}`);
|
|
1804
1830
|
return null;
|
|
1805
1831
|
}
|
|
1806
|
-
const whoisData = await getWhoisData(cleanDomain, timeout);
|
|
1832
|
+
const whoisData = await getWhoisData(cleanDomain, timeout, debug);
|
|
1807
1833
|
if (!whoisData || !whoisData.creationDate) {
|
|
1834
|
+
log(`[whois] no creation date found for ${cleanDomain}`);
|
|
1808
1835
|
return null;
|
|
1809
1836
|
}
|
|
1810
1837
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -1812,6 +1839,7 @@ async function getDomainAge(domain, timeout = 5e3) {
|
|
|
1812
1839
|
const ageInMilliseconds = now.getTime() - creationDate.getTime();
|
|
1813
1840
|
const ageInDays = Math.floor(ageInMilliseconds / (1e3 * 60 * 60 * 24));
|
|
1814
1841
|
const ageInYears = ageInDays / 365.25;
|
|
1842
|
+
log(`[whois] calculated age for ${cleanDomain}: ${ageInDays} days (${ageInYears.toFixed(2)} years)`);
|
|
1815
1843
|
return {
|
|
1816
1844
|
domain: cleanDomain,
|
|
1817
1845
|
creationDate,
|
|
@@ -1821,20 +1849,27 @@ async function getDomainAge(domain, timeout = 5e3) {
|
|
|
1821
1849
|
updatedDate: whoisData.updatedDate ? new Date(whoisData.updatedDate) : null
|
|
1822
1850
|
};
|
|
1823
1851
|
} catch (_error) {
|
|
1852
|
+
log(`[whois] error getting domain age for ${domain}: ${_error instanceof Error ? _error.message : "Unknown error"}`);
|
|
1824
1853
|
return null;
|
|
1825
1854
|
}
|
|
1826
1855
|
}
|
|
1827
|
-
async function getDomainRegistrationStatus(domain, timeout = 5e3) {
|
|
1856
|
+
async function getDomainRegistrationStatus(domain, timeout = 5e3, debug = false) {
|
|
1857
|
+
const log = debug ? console.debug : (..._args) => {
|
|
1858
|
+
};
|
|
1828
1859
|
try {
|
|
1829
1860
|
const cleanDomain = domain.replace(/^https?:\/\//, "").split("/")[0].split("@").pop();
|
|
1830
1861
|
if (!cleanDomain) {
|
|
1862
|
+
log(`[whois] invalid domain format: ${domain}`);
|
|
1831
1863
|
return null;
|
|
1832
1864
|
}
|
|
1865
|
+
log(`[whois] checking domain registration status for ${cleanDomain}`);
|
|
1833
1866
|
if (!isValid(cleanDomain)) {
|
|
1867
|
+
log(`[whois] domain validation failed: ${cleanDomain}`);
|
|
1834
1868
|
return null;
|
|
1835
1869
|
}
|
|
1836
|
-
const whoisData = await getWhoisData(cleanDomain, timeout);
|
|
1870
|
+
const whoisData = await getWhoisData(cleanDomain, timeout, debug);
|
|
1837
1871
|
if (!whoisData || whoisData.isAvailable) {
|
|
1872
|
+
log(`[whois] domain ${cleanDomain} is available or not registered`);
|
|
1838
1873
|
return {
|
|
1839
1874
|
domain: cleanDomain,
|
|
1840
1875
|
isRegistered: false,
|
|
@@ -1862,6 +1897,7 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3) {
|
|
|
1862
1897
|
if (!isExpired) {
|
|
1863
1898
|
daysUntilExpiration = Math.floor((expirationTime - currentTime) / (1e3 * 60 * 60 * 24));
|
|
1864
1899
|
}
|
|
1900
|
+
log(`[whois] domain ${cleanDomain} expires in ${daysUntilExpiration} days`);
|
|
1865
1901
|
}
|
|
1866
1902
|
const statusList = whoisData.status || [];
|
|
1867
1903
|
const formattedStatusList = statusList.map((s) => {
|
|
@@ -1870,6 +1906,7 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3) {
|
|
|
1870
1906
|
});
|
|
1871
1907
|
const isPendingDelete = formattedStatusList.some((s) => s.toLowerCase().includes("pendingdelete") || s.toLowerCase().includes("redemption"));
|
|
1872
1908
|
const isLocked = formattedStatusList.some((s) => s.toLowerCase().includes("clienttransferprohibited") || s.toLowerCase().includes("servertransferprohibited"));
|
|
1909
|
+
log(`[whois] domain ${cleanDomain} - registered: ${isRegistered}, expired: ${isExpired}, locked: ${isLocked}, pending delete: ${isPendingDelete}`);
|
|
1873
1910
|
return {
|
|
1874
1911
|
domain: cleanDomain,
|
|
1875
1912
|
isRegistered,
|
|
@@ -1884,12 +1921,13 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3) {
|
|
|
1884
1921
|
isLocked
|
|
1885
1922
|
};
|
|
1886
1923
|
} catch (_error) {
|
|
1924
|
+
log(`[whois] error getting domain registration status for ${domain}: ${_error instanceof Error ? _error.message : "Unknown error"}`);
|
|
1887
1925
|
return null;
|
|
1888
1926
|
}
|
|
1889
1927
|
}
|
|
1890
1928
|
|
|
1891
1929
|
async function verifyEmailBatch(params) {
|
|
1892
|
-
const { emailAddresses, concurrency = 5, timeout = 4e3, verifyMx = true, verifySmtp = false, checkDisposable = true, checkFree = true, detectName = false, nameDetectionMethod, suggestDomain = false, domainSuggestionMethod, commonDomains, cache } = params;
|
|
1930
|
+
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;
|
|
1893
1931
|
const startTime = Date.now();
|
|
1894
1932
|
const results = /* @__PURE__ */ new Map();
|
|
1895
1933
|
const batches = [];
|
|
@@ -1914,9 +1952,11 @@ async function verifyEmailBatch(params) {
|
|
|
1914
1952
|
suggestDomain,
|
|
1915
1953
|
domainSuggestionMethod,
|
|
1916
1954
|
commonDomains,
|
|
1955
|
+
skipMxForDisposable,
|
|
1956
|
+
skipDomainWhoisForDisposable,
|
|
1917
1957
|
cache
|
|
1918
1958
|
});
|
|
1919
|
-
if (result.
|
|
1959
|
+
if (result.validFormat) {
|
|
1920
1960
|
totalValid++;
|
|
1921
1961
|
} else {
|
|
1922
1962
|
totalInvalid++;
|
|
@@ -1926,7 +1966,7 @@ async function verifyEmailBatch(params) {
|
|
|
1926
1966
|
totalErrors++;
|
|
1927
1967
|
return {
|
|
1928
1968
|
email,
|
|
1929
|
-
result:
|
|
1969
|
+
result: createErrorResult(email)
|
|
1930
1970
|
};
|
|
1931
1971
|
}
|
|
1932
1972
|
});
|
|
@@ -1946,18 +1986,18 @@ async function verifyEmailBatch(params) {
|
|
|
1946
1986
|
}
|
|
1947
1987
|
};
|
|
1948
1988
|
}
|
|
1949
|
-
function
|
|
1989
|
+
function createErrorResult(email, _error) {
|
|
1950
1990
|
return {
|
|
1951
|
-
valid: false,
|
|
1952
1991
|
email,
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1992
|
+
validFormat: false,
|
|
1993
|
+
validMx: null,
|
|
1994
|
+
validSmtp: null,
|
|
1995
|
+
isDisposable: false,
|
|
1996
|
+
isFree: false,
|
|
1958
1997
|
metadata: {
|
|
1959
1998
|
verificationTime: 0,
|
|
1960
|
-
cached: false
|
|
1999
|
+
cached: false,
|
|
2000
|
+
error: VerificationErrorCode.SMTP_CONNECTION_FAILED
|
|
1961
2001
|
}
|
|
1962
2002
|
};
|
|
1963
2003
|
}
|
|
@@ -2022,31 +2062,30 @@ const domainPorts = {
|
|
|
2022
2062
|
"ovh.net": 465
|
|
2023
2063
|
};
|
|
2024
2064
|
async function verifyEmail(params) {
|
|
2025
|
-
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 } = params;
|
|
2065
|
+
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;
|
|
2026
2066
|
const startTime = Date.now();
|
|
2027
2067
|
const log = debug ? console.debug : (..._args) => {
|
|
2028
2068
|
};
|
|
2029
2069
|
const result = {
|
|
2030
|
-
valid: false,
|
|
2031
2070
|
email: emailAddress,
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2071
|
+
validFormat: false,
|
|
2072
|
+
validMx: null,
|
|
2073
|
+
validSmtp: null,
|
|
2074
|
+
isDisposable: false,
|
|
2075
|
+
isFree: false,
|
|
2037
2076
|
metadata: {
|
|
2038
2077
|
verificationTime: 0,
|
|
2039
2078
|
cached: false
|
|
2040
2079
|
}
|
|
2041
2080
|
};
|
|
2042
2081
|
if (!isValidEmail(emailAddress)) {
|
|
2043
|
-
result.format.error = VerificationErrorCode.INVALID_FORMAT;
|
|
2044
2082
|
if (result.metadata) {
|
|
2045
2083
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2084
|
+
result.metadata.error = VerificationErrorCode.INVALID_FORMAT;
|
|
2046
2085
|
}
|
|
2047
2086
|
return result;
|
|
2048
2087
|
}
|
|
2049
|
-
result.
|
|
2088
|
+
result.validFormat = true;
|
|
2050
2089
|
if (detectName2) {
|
|
2051
2090
|
result.detectedName = detectNameFromEmail({
|
|
2052
2091
|
email: emailAddress,
|
|
@@ -2056,64 +2095,69 @@ async function verifyEmail(params) {
|
|
|
2056
2095
|
if (suggestDomain2) {
|
|
2057
2096
|
const [, emailDomain] = emailAddress.split("@");
|
|
2058
2097
|
if (emailDomain) {
|
|
2059
|
-
|
|
2098
|
+
const suggestion = domainSuggestionMethod ? domainSuggestionMethod(emailDomain) : await suggestEmailDomain(emailAddress, commonDomains);
|
|
2099
|
+
if (suggestion) {
|
|
2100
|
+
result.domainSuggestion = suggestion;
|
|
2101
|
+
} else {
|
|
2102
|
+
result.domainSuggestion = null;
|
|
2103
|
+
}
|
|
2060
2104
|
}
|
|
2061
2105
|
}
|
|
2062
2106
|
const [local, domain] = emailAddress.split("@");
|
|
2063
2107
|
if (!domain || !local) {
|
|
2064
|
-
result.format.error = VerificationErrorCode.INVALID_FORMAT;
|
|
2065
2108
|
if (result.metadata) {
|
|
2066
2109
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2110
|
+
result.metadata.error = VerificationErrorCode.INVALID_FORMAT;
|
|
2067
2111
|
}
|
|
2068
2112
|
return result;
|
|
2069
2113
|
}
|
|
2070
2114
|
if (!await isValidEmailDomain(domain, params.cache)) {
|
|
2071
|
-
result.domain.error = VerificationErrorCode.INVALID_DOMAIN;
|
|
2072
2115
|
if (result.metadata) {
|
|
2073
2116
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2117
|
+
result.metadata.error = VerificationErrorCode.INVALID_DOMAIN;
|
|
2074
2118
|
}
|
|
2075
2119
|
return result;
|
|
2076
2120
|
}
|
|
2077
2121
|
if (checkDisposable) {
|
|
2078
|
-
result.
|
|
2079
|
-
if (result.
|
|
2080
|
-
result.
|
|
2081
|
-
result.domain.error = VerificationErrorCode.DISPOSABLE_EMAIL;
|
|
2122
|
+
result.isDisposable = await isDisposableEmail(emailAddress, params.cache);
|
|
2123
|
+
if (result.isDisposable && result.metadata) {
|
|
2124
|
+
result.metadata.error = VerificationErrorCode.DISPOSABLE_EMAIL;
|
|
2082
2125
|
}
|
|
2083
2126
|
}
|
|
2084
2127
|
if (checkFree) {
|
|
2085
|
-
result.
|
|
2128
|
+
result.isFree = await isFreeEmail(emailAddress, params.cache);
|
|
2086
2129
|
}
|
|
2087
|
-
|
|
2130
|
+
const shouldSkipMx = skipMxForDisposable && result.isDisposable;
|
|
2131
|
+
const shouldSkipDomainWhois = skipDomainWhoisForDisposable && result.isDisposable;
|
|
2132
|
+
if (checkDomainAge && !shouldSkipDomainWhois) {
|
|
2088
2133
|
try {
|
|
2089
|
-
result.domainAge = await getDomainAge(domain, whoisTimeout);
|
|
2134
|
+
result.domainAge = await getDomainAge(domain, whoisTimeout, debug);
|
|
2090
2135
|
} catch (err) {
|
|
2091
|
-
log("[
|
|
2136
|
+
log("[verifyEmail] Failed to get domain age", err);
|
|
2092
2137
|
result.domainAge = null;
|
|
2093
2138
|
}
|
|
2094
2139
|
}
|
|
2095
|
-
if (checkDomainRegistration) {
|
|
2140
|
+
if (checkDomainRegistration && !shouldSkipDomainWhois) {
|
|
2096
2141
|
try {
|
|
2097
|
-
result.domainRegistration = await getDomainRegistrationStatus(domain, whoisTimeout);
|
|
2142
|
+
result.domainRegistration = await getDomainRegistrationStatus(domain, whoisTimeout, debug);
|
|
2098
2143
|
} catch (err) {
|
|
2099
|
-
log("[
|
|
2144
|
+
log("[verifyEmail] Failed to get domain registration status", err);
|
|
2100
2145
|
result.domainRegistration = null;
|
|
2101
2146
|
}
|
|
2102
2147
|
}
|
|
2103
|
-
if (verifyMx || verifySmtp) {
|
|
2148
|
+
if ((verifyMx || verifySmtp) && !shouldSkipMx) {
|
|
2104
2149
|
try {
|
|
2105
2150
|
const mxRecords = await resolveMxRecords(domain, params.cache);
|
|
2106
|
-
result.
|
|
2107
|
-
result.
|
|
2108
|
-
|
|
2109
|
-
result.domain.error = VerificationErrorCode.NO_MX_RECORDS;
|
|
2151
|
+
result.validMx = mxRecords.length > 0;
|
|
2152
|
+
if (!result.validMx && result.metadata) {
|
|
2153
|
+
result.metadata.error = VerificationErrorCode.NO_MX_RECORDS;
|
|
2110
2154
|
}
|
|
2111
2155
|
if (verifySmtp && mxRecords.length > 0) {
|
|
2112
2156
|
const cacheKey = `${emailAddress}:smtp`;
|
|
2113
2157
|
const smtpCacheInstance = smtpCacheStore(params.cache);
|
|
2114
2158
|
const cachedSmtp = await smtpCacheInstance.get(cacheKey);
|
|
2115
2159
|
if (cachedSmtp !== null && cachedSmtp !== void 0) {
|
|
2116
|
-
result.
|
|
2160
|
+
result.validSmtp = cachedSmtp;
|
|
2117
2161
|
if (result.metadata) {
|
|
2118
2162
|
result.metadata.cached = true;
|
|
2119
2163
|
}
|
|
@@ -2141,21 +2185,22 @@ async function verifyEmail(params) {
|
|
|
2141
2185
|
retryAttempts: params.retryAttempts
|
|
2142
2186
|
});
|
|
2143
2187
|
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2144
|
-
result.
|
|
2188
|
+
result.validSmtp = smtpResult;
|
|
2145
2189
|
}
|
|
2146
|
-
if (result.
|
|
2147
|
-
result.
|
|
2148
|
-
} else if (result.
|
|
2149
|
-
result.
|
|
2190
|
+
if (result.validSmtp === false && result.metadata) {
|
|
2191
|
+
result.metadata.error = VerificationErrorCode.MAILBOX_NOT_FOUND;
|
|
2192
|
+
} else if (result.validSmtp === null && result.metadata) {
|
|
2193
|
+
result.metadata.error = VerificationErrorCode.SMTP_CONNECTION_FAILED;
|
|
2150
2194
|
}
|
|
2151
2195
|
}
|
|
2152
2196
|
} catch (err) {
|
|
2153
|
-
log("[
|
|
2154
|
-
result.
|
|
2155
|
-
result.
|
|
2197
|
+
log("[verifyEmail] Failed to resolve MX records", err);
|
|
2198
|
+
result.validMx = false;
|
|
2199
|
+
if (result.metadata) {
|
|
2200
|
+
result.metadata.error = VerificationErrorCode.NO_MX_RECORDS;
|
|
2201
|
+
}
|
|
2156
2202
|
}
|
|
2157
2203
|
}
|
|
2158
|
-
result.valid = result.format.valid && result.domain.valid !== false && result.smtp.valid !== false && !result.disposable;
|
|
2159
2204
|
if (result.metadata) {
|
|
2160
2205
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2161
2206
|
}
|