@emailcheck/email-validator-js 2.12.0 → 2.13.1-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/README.md +330 -77
- package/dist/adapters/lru-adapter.d.ts +19 -0
- package/dist/adapters/redis-adapter.d.ts +45 -0
- package/dist/cache-factory.d.ts +39 -0
- package/dist/cache-interface.d.ts +124 -0
- package/dist/cache.d.ts +28 -0
- package/dist/dns.d.ts +2 -1
- package/dist/domain-suggester.d.ts +6 -2
- package/dist/index.d.ts +6 -9
- package/dist/index.esm.js +202 -166
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +201 -166
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +4 -15
- package/dist/validator.d.ts +2 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- [API Reference](#api-reference)
|
|
21
21
|
- [Configuration](#configuration-options)
|
|
22
22
|
- [Examples](#examples)
|
|
23
|
+
- [Custom Cache Injection](#-custom-cache-injection)
|
|
23
24
|
- [Performance & Caching](#-performance--caching)
|
|
24
25
|
- [Email Provider Databases](#️-email-provider-databases)
|
|
25
26
|
- [Testing](#testing)
|
|
@@ -122,59 +123,45 @@ import { verifyEmail } from '@emailcheck/email-validator-js';
|
|
|
122
123
|
|
|
123
124
|
// Basic usage
|
|
124
125
|
const result = await verifyEmail({
|
|
125
|
-
emailAddress: 'user@
|
|
126
|
+
emailAddress: 'user@mydomain.com',
|
|
126
127
|
verifyMx: true,
|
|
127
128
|
verifySmtp: true,
|
|
128
129
|
timeout: 3000
|
|
129
130
|
});
|
|
130
131
|
|
|
131
|
-
console.log(result.
|
|
132
|
-
console.log(result.
|
|
133
|
-
console.log(result.
|
|
132
|
+
console.log(result.valid); // Overall validity
|
|
133
|
+
console.log(result.format.valid); // true
|
|
134
|
+
console.log(result.domain.valid); // true or false
|
|
135
|
+
console.log(result.smtp.valid); // true or false
|
|
134
136
|
```
|
|
135
137
|
|
|
136
138
|
## API Reference
|
|
137
139
|
|
|
138
140
|
### Core Functions
|
|
139
141
|
|
|
140
|
-
#### `verifyEmail(params: IVerifyEmailParams): Promise<
|
|
142
|
+
#### `verifyEmail(params: IVerifyEmailParams): Promise<DetailedVerificationResult>`
|
|
141
143
|
|
|
142
|
-
|
|
144
|
+
Comprehensive email verification with detailed results and error codes.
|
|
143
145
|
|
|
144
146
|
**Parameters:**
|
|
145
147
|
- `emailAddress` (string, required): Email address to verify
|
|
146
148
|
- `timeout` (number): Timeout in milliseconds (default: 4000)
|
|
147
|
-
- `verifyMx` (boolean): Check MX records (default:
|
|
149
|
+
- `verifyMx` (boolean): Check MX records (default: true)
|
|
148
150
|
- `verifySmtp` (boolean): Verify SMTP connection (default: false)
|
|
149
151
|
- `smtpPort` (number): Custom SMTP port
|
|
150
152
|
- `debug` (boolean): Enable debug logging (default: false)
|
|
151
|
-
- `detectName` (boolean): Detect names from email address (default: false)
|
|
152
|
-
- `nameDetectionMethod` (function): Custom name detection method
|
|
153
|
-
- `suggestDomain` (boolean): Enable domain typo suggestions (default: false)
|
|
154
|
-
- `domainSuggestionMethod` (function): Custom domain suggestion method
|
|
155
|
-
- `commonDomains` (string[]): Custom list of domains for suggestions
|
|
156
|
-
|
|
157
|
-
**Returns:**
|
|
158
|
-
```typescript
|
|
159
|
-
{
|
|
160
|
-
validFormat: boolean;
|
|
161
|
-
validMx: boolean | null;
|
|
162
|
-
validSmtp: boolean | null;
|
|
163
|
-
detectedName?: DetectedName | null;
|
|
164
|
-
domainSuggestion?: DomainSuggestion | null;
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
#### `verifyEmailDetailed(params: IVerifyEmailParams): Promise<DetailedVerificationResult>`
|
|
169
|
-
|
|
170
|
-
Advanced verification with detailed results and error codes.
|
|
171
|
-
|
|
172
|
-
**Additional Parameters:**
|
|
173
153
|
- `checkDisposable` (boolean): Check for disposable emails (default: true)
|
|
174
154
|
- `checkFree` (boolean): Check for free email providers (default: true)
|
|
175
155
|
- `retryAttempts` (number): Retry attempts for failures (default: 1)
|
|
176
156
|
- `detectName` (boolean): Detect names from email address (default: false)
|
|
177
|
-
- `
|
|
157
|
+
- `nameDetectionMethod` (function): Custom name detection method
|
|
158
|
+
- `suggestDomain` (boolean): Enable domain typo suggestions (default: true)
|
|
159
|
+
- `domainSuggestionMethod` (function): Custom domain suggestion method
|
|
160
|
+
- `commonDomains` (string[]): Custom list of domains for suggestions
|
|
161
|
+
- `checkDomainAge` (boolean): Check domain age (default: false)
|
|
162
|
+
- `checkDomainRegistration` (boolean): Check domain registration status (default: false)
|
|
163
|
+
- `whoisTimeout` (number): WHOIS lookup timeout (default: 5000)
|
|
164
|
+
- `cache` (ICache): Optional custom cache instance
|
|
178
165
|
|
|
179
166
|
**Returns:**
|
|
180
167
|
```typescript
|
|
@@ -237,7 +224,7 @@ Verify multiple emails in parallel with concurrency control.
|
|
|
237
224
|
Detect first and last name from email address.
|
|
238
225
|
|
|
239
226
|
```typescript
|
|
240
|
-
const name = detectName('john.doe@
|
|
227
|
+
const name = detectName('john.doe@mydomain.com');
|
|
241
228
|
// Returns: { firstName: 'John', lastName: 'Doe', confidence: 0.9 }
|
|
242
229
|
```
|
|
243
230
|
|
|
@@ -270,7 +257,7 @@ const customMethod = (email: string) => {
|
|
|
270
257
|
};
|
|
271
258
|
|
|
272
259
|
const name = detectNameFromEmail({
|
|
273
|
-
email: 'user@
|
|
260
|
+
email: 'user@mydomain.com',
|
|
274
261
|
customMethod: customMethod
|
|
275
262
|
});
|
|
276
263
|
```
|
|
@@ -349,10 +336,10 @@ getDomainSimilarity('gmail.com', 'yahoo.com'); // 0.3
|
|
|
349
336
|
Get domain age information via WHOIS lookup.
|
|
350
337
|
|
|
351
338
|
```typescript
|
|
352
|
-
const ageInfo = await getDomainAge('
|
|
339
|
+
const ageInfo = await getDomainAge('mydomain.com');
|
|
353
340
|
// Returns:
|
|
354
341
|
// {
|
|
355
|
-
// domain: '
|
|
342
|
+
// domain: 'mydomain.com',
|
|
356
343
|
// creationDate: Date,
|
|
357
344
|
// ageInDays: 7890,
|
|
358
345
|
// ageInYears: 21.6,
|
|
@@ -361,8 +348,8 @@ const ageInfo = await getDomainAge('example.com');
|
|
|
361
348
|
// }
|
|
362
349
|
|
|
363
350
|
// Works with email addresses and URLs too
|
|
364
|
-
await getDomainAge('user@
|
|
365
|
-
await getDomainAge('https://
|
|
351
|
+
await getDomainAge('user@mydomain.com');
|
|
352
|
+
await getDomainAge('https://mydomain.com/path');
|
|
366
353
|
```
|
|
367
354
|
|
|
368
355
|
**Parameters:**
|
|
@@ -375,15 +362,15 @@ await getDomainAge('https://example.com/path');
|
|
|
375
362
|
Get detailed domain registration status via WHOIS.
|
|
376
363
|
|
|
377
364
|
```typescript
|
|
378
|
-
const status = await getDomainRegistrationStatus('
|
|
365
|
+
const status = await getDomainRegistrationStatus('mydomain.com');
|
|
379
366
|
// Returns:
|
|
380
367
|
// {
|
|
381
|
-
// domain: '
|
|
368
|
+
// domain: 'mydomain.com',
|
|
382
369
|
// isRegistered: true,
|
|
383
370
|
// isAvailable: false,
|
|
384
371
|
// status: ['clientTransferProhibited'],
|
|
385
372
|
// registrar: 'Example Registrar',
|
|
386
|
-
// nameServers: ['ns1.
|
|
373
|
+
// nameServers: ['ns1.mydomain.com', 'ns2.mydomain.com'],
|
|
387
374
|
// expirationDate: Date,
|
|
388
375
|
// isExpired: false,
|
|
389
376
|
// daysUntilExpiration: 365,
|
|
@@ -430,7 +417,7 @@ isFreeEmail('corporate.com'); // false
|
|
|
430
417
|
Validate email format (RFC 5321 compliant).
|
|
431
418
|
|
|
432
419
|
```typescript
|
|
433
|
-
isValidEmail('user@
|
|
420
|
+
isValidEmail('user@mydomain.com'); // true
|
|
434
421
|
isValidEmail('invalid.email'); // false
|
|
435
422
|
```
|
|
436
423
|
|
|
@@ -446,7 +433,7 @@ isValidEmail('invalid.email'); // false
|
|
|
446
433
|
Validate if a domain has a valid TLD.
|
|
447
434
|
|
|
448
435
|
```typescript
|
|
449
|
-
isValidEmailDomain('
|
|
436
|
+
isValidEmailDomain('mydomain.com'); // true
|
|
450
437
|
isValidEmailDomain('example.invalid'); // false
|
|
451
438
|
```
|
|
452
439
|
|
|
@@ -590,22 +577,23 @@ Number of retry attempts for transient failures. Default: `1`.
|
|
|
590
577
|
```typescript
|
|
591
578
|
import { verifyEmail } from '@emailcheck/email-validator-js';
|
|
592
579
|
|
|
593
|
-
const
|
|
594
|
-
emailAddress: 'foo@email.com',
|
|
595
|
-
verifyMx: true,
|
|
596
|
-
verifySmtp: true,
|
|
597
|
-
timeout: 3000
|
|
580
|
+
const result = await verifyEmail({
|
|
581
|
+
emailAddress: 'foo@email.com',
|
|
582
|
+
verifyMx: true,
|
|
583
|
+
verifySmtp: true,
|
|
584
|
+
timeout: 3000
|
|
598
585
|
});
|
|
599
|
-
//
|
|
600
|
-
//
|
|
601
|
-
//
|
|
586
|
+
console.log(result.valid); // true
|
|
587
|
+
console.log(result.format.valid); // true
|
|
588
|
+
console.log(result.domain.valid); // true
|
|
589
|
+
console.log(result.smtp.valid); // true
|
|
602
590
|
```
|
|
603
591
|
|
|
604
592
|
### Detailed Verification (NEW)
|
|
605
593
|
```typescript
|
|
606
|
-
import {
|
|
594
|
+
import { verifyEmail } from '@emailcheck/email-validator-js';
|
|
607
595
|
|
|
608
|
-
const result = await
|
|
596
|
+
const result = await verifyEmail({
|
|
609
597
|
emailAddress: 'foo@email.com',
|
|
610
598
|
verifyMx: true,
|
|
611
599
|
verifySmtp: true,
|
|
@@ -623,7 +611,7 @@ const result = await verifyEmailDetailed({
|
|
|
623
611
|
```typescript
|
|
624
612
|
import { verifyEmailBatch } from '@emailcheck/email-validator-js';
|
|
625
613
|
|
|
626
|
-
const emails = ['user1@gmail.com', 'user2@
|
|
614
|
+
const emails = ['user1@gmail.com', 'user2@mydomain.com', 'invalid@fake.com'];
|
|
627
615
|
|
|
628
616
|
const result = await verifyEmailBatch({
|
|
629
617
|
emailAddresses: emails,
|
|
@@ -638,26 +626,26 @@ const result = await verifyEmailBatch({
|
|
|
638
626
|
|
|
639
627
|
### Name Detection (ENHANCED)
|
|
640
628
|
```typescript
|
|
641
|
-
import { detectName,
|
|
629
|
+
import { detectName, verifyEmail } from '@emailcheck/email-validator-js';
|
|
642
630
|
|
|
643
631
|
// Standalone name detection - now with composite name support
|
|
644
|
-
const name = detectName('john.doe@
|
|
632
|
+
const name = detectName('john.doe@mydomain.com');
|
|
645
633
|
// name: { firstName: 'John', lastName: 'Doe', confidence: 0.9 }
|
|
646
634
|
|
|
647
635
|
// Handle alphanumeric composite names
|
|
648
|
-
const composite = detectName('mo1.test2@
|
|
636
|
+
const composite = detectName('mo1.test2@mydomain.com');
|
|
649
637
|
// composite: { firstName: 'Mo1', lastName: 'Test2', confidence: 0.6 }
|
|
650
638
|
|
|
651
639
|
// Smart handling of numbers and suffixes
|
|
652
|
-
const withNumbers = detectName('john.doe123@
|
|
640
|
+
const withNumbers = detectName('john.doe123@mydomain.com');
|
|
653
641
|
// withNumbers: { firstName: 'John', lastName: 'Doe', confidence: 0.8 }
|
|
654
642
|
|
|
655
|
-
const withSuffix = detectName('jane.smith.dev@
|
|
643
|
+
const withSuffix = detectName('jane.smith.dev@mydomain.com');
|
|
656
644
|
// withSuffix: { firstName: 'Jane', lastName: 'Smith', confidence: 0.7 }
|
|
657
645
|
|
|
658
646
|
// Integrated with email verification
|
|
659
|
-
const result = await
|
|
660
|
-
emailAddress: 'jane_smith@
|
|
647
|
+
const result = await verifyEmail({
|
|
648
|
+
emailAddress: 'jane_smith@mydomain.com',
|
|
661
649
|
detectName: true
|
|
662
650
|
});
|
|
663
651
|
// result.detectedName: { firstName: 'Jane', lastName: 'Smith', confidence: 0.8 }
|
|
@@ -669,7 +657,7 @@ const customMethod = (email: string) => {
|
|
|
669
657
|
};
|
|
670
658
|
|
|
671
659
|
const resultCustom = await verifyEmail({
|
|
672
|
-
emailAddress: 'user@
|
|
660
|
+
emailAddress: 'user@mydomain.com',
|
|
673
661
|
detectName: true,
|
|
674
662
|
nameDetectionMethod: customMethod
|
|
675
663
|
});
|
|
@@ -677,14 +665,14 @@ const resultCustom = await verifyEmail({
|
|
|
677
665
|
|
|
678
666
|
### Domain Typo Detection (NEW)
|
|
679
667
|
```typescript
|
|
680
|
-
import { suggestEmailDomain,
|
|
668
|
+
import { suggestEmailDomain, verifyEmail } from '@emailcheck/email-validator-js';
|
|
681
669
|
|
|
682
670
|
// Standalone domain suggestion
|
|
683
671
|
const suggestion = suggestEmailDomain('user@gmial.com');
|
|
684
672
|
// suggestion: { original: 'user@gmial.com', suggested: 'user@gmail.com', confidence: 0.95 }
|
|
685
673
|
|
|
686
674
|
// Integrated with email verification (enabled by default in detailed mode)
|
|
687
|
-
const result = await
|
|
675
|
+
const result = await verifyEmail({
|
|
688
676
|
emailAddress: 'john@yaho.com',
|
|
689
677
|
suggestDomain: true // Default: true for detailed verification
|
|
690
678
|
});
|
|
@@ -704,20 +692,21 @@ const resultCustom = await verifyEmail({
|
|
|
704
692
|
|
|
705
693
|
When a domain does not exist or has no MX records:
|
|
706
694
|
```typescript
|
|
707
|
-
const result = await verifyEmail({
|
|
708
|
-
emailAddress: 'foo@bad-domain.com',
|
|
709
|
-
verifyMx: true,
|
|
710
|
-
verifySmtp: true
|
|
695
|
+
const result = await verifyEmail({
|
|
696
|
+
emailAddress: 'foo@bad-domain.com',
|
|
697
|
+
verifyMx: true,
|
|
698
|
+
verifySmtp: true
|
|
711
699
|
});
|
|
712
|
-
//
|
|
713
|
-
//
|
|
714
|
-
//
|
|
700
|
+
// result.valid: false (domain or SMTP failed)
|
|
701
|
+
// result.format.valid: true
|
|
702
|
+
// result.domain.valid: false
|
|
703
|
+
// result.smtp.valid: null (couldn't be performed)
|
|
715
704
|
```
|
|
716
705
|
|
|
717
706
|
### Using Detailed Verification for Better Insights
|
|
718
707
|
|
|
719
708
|
```typescript
|
|
720
|
-
const detailed = await
|
|
709
|
+
const detailed = await verifyEmail({
|
|
721
710
|
emailAddress: 'user@suspicious-domain.com',
|
|
722
711
|
verifyMx: true,
|
|
723
712
|
verifySmtp: true,
|
|
@@ -776,16 +765,16 @@ for (const [email, result] of batch.results) {
|
|
|
776
765
|
|
|
777
766
|
```typescript
|
|
778
767
|
// First verification - hits DNS and SMTP
|
|
779
|
-
const first = await verifyEmail({
|
|
780
|
-
emailAddress: 'cached@
|
|
781
|
-
verifyMx: true
|
|
768
|
+
const first = await verifyEmail({
|
|
769
|
+
emailAddress: 'cached@mydomain.com',
|
|
770
|
+
verifyMx: true
|
|
782
771
|
});
|
|
783
772
|
// Takes ~500ms
|
|
784
773
|
|
|
785
774
|
// Second verification - uses cache
|
|
786
|
-
const second = await verifyEmail({
|
|
787
|
-
emailAddress: 'cached@
|
|
788
|
-
verifyMx: true
|
|
775
|
+
const second = await verifyEmail({
|
|
776
|
+
emailAddress: 'cached@mydomain.com',
|
|
777
|
+
verifyMx: true
|
|
789
778
|
});
|
|
790
779
|
// Takes ~1ms (cached)
|
|
791
780
|
|
|
@@ -793,7 +782,271 @@ const second = await verifyEmail({
|
|
|
793
782
|
clearAllCaches();
|
|
794
783
|
```
|
|
795
784
|
|
|
796
|
-
|
|
785
|
+
## 🚀 Custom Cache Injection
|
|
786
|
+
|
|
787
|
+
The library supports parameter-based cache injection, allowing you to use custom cache backends like Redis, Memcached, or any LRU-compatible cache implementation.
|
|
788
|
+
|
|
789
|
+
### Built-in Cache Adapters
|
|
790
|
+
|
|
791
|
+
#### LRU Cache (Default)
|
|
792
|
+
```typescript
|
|
793
|
+
import { CacheFactory } from '@emailcheck/email-validator-js';
|
|
794
|
+
|
|
795
|
+
// Create LRU cache with custom settings
|
|
796
|
+
const lruCache = CacheFactory.createLRUCache({
|
|
797
|
+
mx: 1800000, // 30 minutes for MX records
|
|
798
|
+
disposable: 86400000, // 24 hours for disposable checks
|
|
799
|
+
free: 86400000, // 24 hours for free email checks
|
|
800
|
+
smtp: 1800000, // 30 minutes for SMTP verification
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
// Use with email verification
|
|
804
|
+
const result = await verifyEmail({
|
|
805
|
+
emailAddress: 'user@mydomain.com',
|
|
806
|
+
verifyMx: true,
|
|
807
|
+
verifySmtp: true,
|
|
808
|
+
cache: lruCache // Pass the cache instance
|
|
809
|
+
});
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
#### Redis Cache
|
|
813
|
+
```typescript
|
|
814
|
+
import { CacheFactory } from '@emailcheck/email-validator-js';
|
|
815
|
+
import Redis from 'ioredis';
|
|
816
|
+
|
|
817
|
+
// Create Redis client
|
|
818
|
+
const redis = new Redis({
|
|
819
|
+
host: 'localhost',
|
|
820
|
+
port: 6379,
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
// Create Redis cache adapter
|
|
824
|
+
const redisCache = CacheFactory.createRedisCache(redis, {
|
|
825
|
+
keyPrefix: 'email_validator:',
|
|
826
|
+
defaultTtlMs: 3600000, // 1 hour default TTL
|
|
827
|
+
jsonSerializer: {
|
|
828
|
+
stringify: (value) => JSON.stringify(value),
|
|
829
|
+
parse: (value) => JSON.parse(value)
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
// Use with batch verification
|
|
834
|
+
const batchResult = await verifyEmailBatch({
|
|
835
|
+
emailAddresses: ['user1@mydomain.com', 'user2@mydomain.com'],
|
|
836
|
+
verifyMx: true,
|
|
837
|
+
verifySmtp: true,
|
|
838
|
+
cache: redisCache,
|
|
839
|
+
concurrency: 10
|
|
840
|
+
});
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### Custom Cache Implementation
|
|
844
|
+
|
|
845
|
+
Create your own cache adapter by implementing the `ICacheStore` interface:
|
|
846
|
+
|
|
847
|
+
```typescript
|
|
848
|
+
import { CacheFactory, type ICacheStore } from '@emailcheck/email-validator-js';
|
|
849
|
+
|
|
850
|
+
class MyCustomCache<T> implements ICacheStore<T> {
|
|
851
|
+
private store = new Map<string, { value: T; expiry: number }>();
|
|
852
|
+
|
|
853
|
+
async get(key: string): Promise<T | null> {
|
|
854
|
+
const item = this.store.get(key);
|
|
855
|
+
if (!item) return null;
|
|
856
|
+
|
|
857
|
+
if (Date.now() > item.expiry) {
|
|
858
|
+
this.store.delete(key);
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
return item.value;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
async set(key: string, value: T, ttlMs?: number): Promise<void> {
|
|
866
|
+
const expiry = Date.now() + (ttlMs || 3600000);
|
|
867
|
+
this.store.set(key, { value, expiry });
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
async delete(key: string): Promise<boolean> {
|
|
871
|
+
return this.store.delete(key);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
async has(key: string): Promise<boolean> {
|
|
875
|
+
return this.store.has(key);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
async clear(): Promise<void> {
|
|
879
|
+
this.store.clear();
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
size(): number {
|
|
883
|
+
return this.store.size;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// Create custom cache
|
|
888
|
+
const customCache = CacheFactory.createCustomCache(
|
|
889
|
+
(cacheType, defaultTtl, defaultSize) => new MyCustomCache(),
|
|
890
|
+
{
|
|
891
|
+
mx: 1800000,
|
|
892
|
+
disposable: 86400000,
|
|
893
|
+
free: 86400000
|
|
894
|
+
}
|
|
895
|
+
);
|
|
896
|
+
|
|
897
|
+
// Use with verification
|
|
898
|
+
const result = await verifyEmail({
|
|
899
|
+
emailAddress: 'user@mydomain.com',
|
|
900
|
+
verifyMx: true,
|
|
901
|
+
verifySmtp: true,
|
|
902
|
+
checkDisposable: true,
|
|
903
|
+
cache: customCache
|
|
904
|
+
});
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
### Mixed Cache Configuration
|
|
908
|
+
|
|
909
|
+
Use different cache backends for different types of data:
|
|
910
|
+
|
|
911
|
+
```typescript
|
|
912
|
+
const mixedCache = CacheFactory.createMixedCache({
|
|
913
|
+
// Use Redis for frequently accessed data
|
|
914
|
+
mx: {
|
|
915
|
+
store: redisStore, // Your Redis store implementation
|
|
916
|
+
ttlMs: 1800000
|
|
917
|
+
},
|
|
918
|
+
smtp: {
|
|
919
|
+
store: redisStore,
|
|
920
|
+
ttlMs: 900000
|
|
921
|
+
},
|
|
922
|
+
|
|
923
|
+
// Use LRU for less critical data
|
|
924
|
+
disposable: {
|
|
925
|
+
ttlMs: 86400000 // 24 hours
|
|
926
|
+
},
|
|
927
|
+
free: {
|
|
928
|
+
ttlMs: 86400000 // 24 hours
|
|
929
|
+
},
|
|
930
|
+
domainValid: {
|
|
931
|
+
ttlMs: 86400000 // 24 hours
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
// Verification will use appropriate cache for each operation
|
|
936
|
+
await verifyEmail({
|
|
937
|
+
emailAddress: 'user@tempmail.com',
|
|
938
|
+
verifyMx: true,
|
|
939
|
+
verifySmtp: true,
|
|
940
|
+
checkDisposable: true,
|
|
941
|
+
checkFree: true,
|
|
942
|
+
cache: mixedCache
|
|
943
|
+
});
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
### Cache Best Practices
|
|
947
|
+
|
|
948
|
+
1. **Choose Appropriate TTL**:
|
|
949
|
+
- MX Records: 30-60 minutes (DNS changes infrequently)
|
|
950
|
+
- SMTP Results: 15-30 minutes (Mailbox status changes)
|
|
951
|
+
- Disposable/Free Lists: 12-24 hours (Provider lists update slowly)
|
|
952
|
+
|
|
953
|
+
2. **Handle Cache Errors Gracefully**:
|
|
954
|
+
```typescript
|
|
955
|
+
const result = await verifyEmail({
|
|
956
|
+
emailAddress: 'user@mydomain.com',
|
|
957
|
+
cache: unreliableCache, // Cache might fail
|
|
958
|
+
verifyMx: true,
|
|
959
|
+
verifySmtp: true
|
|
960
|
+
});
|
|
961
|
+
// Library continues to work even if cache operations fail
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
3. **Monitor Cache Hit Rates**:
|
|
965
|
+
```typescript
|
|
966
|
+
import { CacheFactory } from '@emailcheck/email-validator-js';
|
|
967
|
+
|
|
968
|
+
// Create cache with hit tracking
|
|
969
|
+
const cache = CacheFactory.createLRUCache();
|
|
970
|
+
let hits = 0;
|
|
971
|
+
let misses = 0;
|
|
972
|
+
|
|
973
|
+
// Wrap cache store to track metrics
|
|
974
|
+
const trackingCache = {
|
|
975
|
+
get: async (key: string) => {
|
|
976
|
+
const result = await cache.mx.get(key);
|
|
977
|
+
if (result !== null && result !== undefined) {
|
|
978
|
+
hits++;
|
|
979
|
+
} else {
|
|
980
|
+
misses++;
|
|
981
|
+
}
|
|
982
|
+
return result;
|
|
983
|
+
},
|
|
984
|
+
set: cache.mx.set.bind(cache.mx),
|
|
985
|
+
delete: cache.mx.delete.bind(cache.mx),
|
|
986
|
+
has: cache.mx.has.bind(cache.mx),
|
|
987
|
+
clear: cache.mx.clear.bind(cache.mx),
|
|
988
|
+
size: () => cache.mx.size()
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
// Monitor performance
|
|
992
|
+
console.log(`Cache hit rate: ${(hits / (hits + misses) * 100).toFixed(1)}%`);
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
### Cache Key Structure
|
|
996
|
+
|
|
997
|
+
The library uses the following cache key patterns:
|
|
998
|
+
|
|
999
|
+
- **MX Records**: Domain name (e.g., `mydomain.com`)
|
|
1000
|
+
- **SMTP Verification**: Full email with `:smtp` suffix (e.g., `user@mydomain.com:smtp`)
|
|
1001
|
+
- **Disposable Check**: Domain name (e.g., `tempmail.com`)
|
|
1002
|
+
- **Free Provider Check**: Domain name (e.g., `gmail.com`)
|
|
1003
|
+
- **Domain Validation**: Domain name (e.g., `mydomain.com`)
|
|
1004
|
+
|
|
1005
|
+
### Redis Production Setup
|
|
1006
|
+
|
|
1007
|
+
For production Redis deployment:
|
|
1008
|
+
|
|
1009
|
+
```typescript
|
|
1010
|
+
import Redis from 'ioredis';
|
|
1011
|
+
import { CacheFactory } from '@emailcheck/email-validator-js';
|
|
1012
|
+
|
|
1013
|
+
// Production Redis configuration
|
|
1014
|
+
const redis = new Redis({
|
|
1015
|
+
host: process.env.REDIS_HOST || 'localhost',
|
|
1016
|
+
port: parseInt(process.env.REDIS_PORT || '6379'),
|
|
1017
|
+
password: process.env.REDIS_PASSWORD,
|
|
1018
|
+
db: parseInt(process.env.REDIS_DB || '0'),
|
|
1019
|
+
retryDelayOnFailover: 100,
|
|
1020
|
+
maxRetriesPerRequest: 3,
|
|
1021
|
+
lazyConnect: true,
|
|
1022
|
+
keepAlive: 30000,
|
|
1023
|
+
family: 4,
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
// Handle Redis connection errors
|
|
1027
|
+
redis.on('error', (err) => {
|
|
1028
|
+
console.error('Redis connection error:', err);
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
// Production cache with appropriate settings
|
|
1032
|
+
const productionCache = CacheFactory.createRedisCache(redis, {
|
|
1033
|
+
keyPrefix: 'prod:email:',
|
|
1034
|
+
defaultTtlMs: 3600000,
|
|
1035
|
+
// Configure for high availability
|
|
1036
|
+
jsonSerializer: {
|
|
1037
|
+
stringify: (value: any) => JSON.stringify(value),
|
|
1038
|
+
parse: (value: string) => JSON.parse(value)
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
// Graceful shutdown
|
|
1043
|
+
process.on('SIGTERM', async () => {
|
|
1044
|
+
await redis.quit();
|
|
1045
|
+
process.exit(0);
|
|
1046
|
+
});
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
**Note:** Yahoo, Hotmail, and some providers always return `result.smtp.valid: true` as they don't allow mailbox verification.
|
|
797
1050
|
|
|
798
1051
|
## 🌐 Serverless Deployment
|
|
799
1052
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type LRU } from 'tiny-lru';
|
|
2
|
+
import type { ICacheStore } from '../cache-interface';
|
|
3
|
+
/**
|
|
4
|
+
* Adapter to make tiny-lru compatible with our cache interface
|
|
5
|
+
*/
|
|
6
|
+
export declare class LRUAdapter<T> implements ICacheStore<T> {
|
|
7
|
+
private lru;
|
|
8
|
+
constructor(maxSize?: number, ttlMs?: number);
|
|
9
|
+
get(key: string): T | null | undefined;
|
|
10
|
+
set(key: string, value: T, ttlMs?: number): Promise<void>;
|
|
11
|
+
delete(key: string): Promise<boolean>;
|
|
12
|
+
has(key: string): Promise<boolean>;
|
|
13
|
+
clear(): Promise<void>;
|
|
14
|
+
size(): number;
|
|
15
|
+
/**
|
|
16
|
+
* Get the underlying LRU instance for advanced operations
|
|
17
|
+
*/
|
|
18
|
+
getLRU(): LRU<T>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ICacheStore } from '../cache-interface';
|
|
2
|
+
/**
|
|
3
|
+
* Redis client interface to avoid direct dependency on redis package
|
|
4
|
+
*/
|
|
5
|
+
export interface IRedisClient {
|
|
6
|
+
get(key: string): Promise<string | null>;
|
|
7
|
+
set(key: string, value: string, mode?: string, duration?: number): Promise<string | null>;
|
|
8
|
+
del(key: string): Promise<number>;
|
|
9
|
+
exists(key: string): Promise<number>;
|
|
10
|
+
flushdb(): Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Redis adapter for the cache interface
|
|
14
|
+
* Supports JSON serialization for complex objects
|
|
15
|
+
*/
|
|
16
|
+
export declare class RedisAdapter<T> implements ICacheStore<T> {
|
|
17
|
+
private redis;
|
|
18
|
+
private keyPrefix;
|
|
19
|
+
private defaultTtlMs;
|
|
20
|
+
private jsonSerializer;
|
|
21
|
+
constructor(redis: IRedisClient, options?: {
|
|
22
|
+
keyPrefix?: string;
|
|
23
|
+
defaultTtlMs?: number;
|
|
24
|
+
jsonSerializer?: {
|
|
25
|
+
stringify: (value: T) => string;
|
|
26
|
+
parse: (value: string) => T;
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
private getKey;
|
|
30
|
+
/**
|
|
31
|
+
* Recursively process an object to convert Date instances to a serializable format
|
|
32
|
+
*/
|
|
33
|
+
private processDatesForSerialization;
|
|
34
|
+
get(key: string): Promise<T | null>;
|
|
35
|
+
set(key: string, value: T, ttlMs?: number): Promise<void>;
|
|
36
|
+
delete(key: string): Promise<boolean>;
|
|
37
|
+
has(key: string): Promise<boolean>;
|
|
38
|
+
clear(): Promise<void>;
|
|
39
|
+
size(): number | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Helper method to delete only keys with the configured prefix
|
|
42
|
+
* Requires Redis SCAN command which might not be available in all Redis clients
|
|
43
|
+
*/
|
|
44
|
+
clearPrefixed(): Promise<void>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type IRedisClient } from './adapters/redis-adapter';
|
|
2
|
+
import type { CacheConfig, ICache, ICacheStore } from './cache-interface';
|
|
3
|
+
import { DEFAULT_CACHE_TTL } from './cache-interface';
|
|
4
|
+
/**
|
|
5
|
+
* Cache factory object to create cache instances with different backends
|
|
6
|
+
*/
|
|
7
|
+
export declare const CacheFactory: {
|
|
8
|
+
/**
|
|
9
|
+
* Create a cache with LRU (tiny-lru) backend
|
|
10
|
+
*/
|
|
11
|
+
createLRUCache(customTtl?: Partial<typeof DEFAULT_CACHE_TTL>): ICache;
|
|
12
|
+
/**
|
|
13
|
+
* Create a cache with Redis backend
|
|
14
|
+
*/
|
|
15
|
+
createRedisCache(redis: IRedisClient, options?: {
|
|
16
|
+
keyPrefix?: string;
|
|
17
|
+
customTtl?: Partial<typeof DEFAULT_CACHE_TTL>;
|
|
18
|
+
jsonSerializer?: {
|
|
19
|
+
stringify: (value: any) => string;
|
|
20
|
+
parse: (value: string) => any;
|
|
21
|
+
};
|
|
22
|
+
}): ICache;
|
|
23
|
+
/**
|
|
24
|
+
* Create a cache with custom backend
|
|
25
|
+
*/
|
|
26
|
+
createCustomCache(storeFactory: (cacheType: keyof typeof DEFAULT_CACHE_TTL, defaultTtl: number, defaultSize: number) => ICacheStore, customTtl?: Partial<typeof DEFAULT_CACHE_TTL>): ICache;
|
|
27
|
+
/**
|
|
28
|
+
* Create a mixed cache with different backends for different cache types
|
|
29
|
+
*/
|
|
30
|
+
createMixedCache(config: {
|
|
31
|
+
mx?: CacheConfig;
|
|
32
|
+
disposable?: CacheConfig;
|
|
33
|
+
free?: CacheConfig;
|
|
34
|
+
domainValid?: CacheConfig;
|
|
35
|
+
smtp?: CacheConfig;
|
|
36
|
+
domainSuggestion?: CacheConfig;
|
|
37
|
+
whois?: CacheConfig;
|
|
38
|
+
}): ICache;
|
|
39
|
+
};
|