@mikkelscheike/email-provider-links 2.7.1 β 2.8.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 +84 -181
- package/dist/alias-detection.js +42 -213
- package/dist/api.d.ts +29 -13
- package/dist/api.js +29 -29
- package/dist/concurrent-dns.d.ts +1 -1
- package/dist/concurrent-dns.js +6 -5
- package/dist/hash-verifier.js +2 -2
- package/dist/loader.d.ts +1 -1
- package/dist/loader.js +16 -5
- package/dist/schema.d.ts +25 -6
- package/dist/schema.js +4 -7
- package/dist/url-validator.js +22 -103
- package/package.json +9 -8
- package/providers/emailproviders.json +837 -288
package/README.md
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
π **Modern email provider detection library with enhanced TypeScript support and enterprise security**
|
|
4
4
|
|
|
5
|
-
A robust TypeScript library providing direct links to **93 email providers** (
|
|
5
|
+
A robust TypeScript library providing direct links to **93 email providers** (180 domains) with **concurrent DNS resolution**, **optimized performance**, **comprehensive email validation**, and advanced security features for login and password reset flows.
|
|
6
|
+
|
|
7
|
+
## π Try it out
|
|
8
|
+
|
|
9
|
+
**[Live Demo](https://demo.mikkelscheike.com)** - Test the library with any email address and see it in action!
|
|
6
10
|
|
|
7
11
|
## β¨ New in Version 2.7.0
|
|
8
12
|
|
|
@@ -13,12 +17,13 @@ A robust TypeScript library providing direct links to **93 email providers** (17
|
|
|
13
17
|
- π‘οΈ **Improved Security**: Enhanced cryptographic integrity verification
|
|
14
18
|
- β‘ **Better Performance**: Optimized concurrent DNS with smart caching
|
|
15
19
|
- π― **Developer Experience**: Enhanced error messages and debugging information
|
|
20
|
+
- π **Development Mode**: Memory usage tracking when NODE_ENV=development
|
|
16
21
|
|
|
17
22
|
## β¨ Core Features
|
|
18
23
|
|
|
19
|
-
- π **Fast & Lightweight**: Zero dependencies, ultra-low memory (~0.
|
|
24
|
+
- π **Fast & Lightweight**: Zero dependencies, ultra-low memory (~0.08MB initial, ~0.03MB per 1000 ops)
|
|
20
25
|
- π§ **93 Email Providers**: Gmail, Outlook, Yahoo, ProtonMail, iCloud, and many more
|
|
21
|
-
- π **
|
|
26
|
+
- π **207 Domains Supported**: Comprehensive international coverage
|
|
22
27
|
- π **Full IDN Support**: International domain names with RFC compliance and Punycode
|
|
23
28
|
- β
**Advanced Email Validation**: International email validation with detailed error reporting
|
|
24
29
|
- π’ **Business Domain Detection**: DNS-based detection for custom domains (Google Workspace, Microsoft 365, etc.)
|
|
@@ -31,7 +36,7 @@ A robust TypeScript library providing direct links to **93 email providers** (17
|
|
|
31
36
|
- π **Email Alias Detection**: Normalize Gmail dots, plus addressing, and provider-specific aliases
|
|
32
37
|
- π‘οΈ **Fraud Prevention**: Detect duplicate accounts through email alias manipulation
|
|
33
38
|
- π¦ **Batch Processing**: Efficiently process multiple emails with deduplication
|
|
34
|
-
- π§ͺ **Thoroughly Tested**:
|
|
39
|
+
- π§ͺ **Thoroughly Tested**: 424 tests with 93.16% code coverage
|
|
35
40
|
|
|
36
41
|
## Installation
|
|
37
42
|
|
|
@@ -54,83 +59,9 @@ Fully compatible with the latest Node.js 24.x! The library is tested on:
|
|
|
54
59
|
- Node.js 22.x (Current)
|
|
55
60
|
- **Node.js 24.x (Latest)** - Full support with latest features
|
|
56
61
|
|
|
57
|
-
## Quick Start
|
|
58
|
-
|
|
59
|
-
**One function handles everything** - consumer emails, business domains, and unknown providers:
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
import { getEmailProvider } from '@mikkelscheike/email-provider-links';
|
|
63
|
-
|
|
64
|
-
// Works for ANY email address - the only function you need
|
|
65
|
-
const result = await getEmailProvider('user@gmail.com');
|
|
66
|
-
console.log(result.loginUrl); // "https://mail.google.com/mail/"
|
|
67
|
-
console.log(result.provider?.companyProvider); // "Gmail"
|
|
68
|
-
|
|
69
|
-
// Automatically detects business domains too
|
|
70
|
-
const business = await getEmailProvider('user@mycompany.com');
|
|
71
|
-
console.log(business.provider?.companyProvider); // "Google Workspace" (if detected)
|
|
72
|
-
|
|
73
|
-
// Gracefully handles unknown providers
|
|
74
|
-
const unknown = await getEmailProvider('user@unknown.com');
|
|
75
|
-
console.log(unknown.loginUrl); // null
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Enhanced Email Validation
|
|
79
|
-
|
|
80
|
-
**New in 2.7.0**: Comprehensive email validation with international support:
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { validateEmailAddress, validateInternationalEmail } from '@mikkelscheike/email-provider-links';
|
|
84
|
-
|
|
85
|
-
// Enhanced validation with detailed error reporting
|
|
86
|
-
const result = validateEmailAddress('user@example.com');
|
|
87
|
-
if (result.isValid) {
|
|
88
|
-
console.log('Valid email:', result.normalizedEmail);
|
|
89
|
-
} else {
|
|
90
|
-
console.log('Error:', result.error?.message);
|
|
91
|
-
console.log('Error code:', result.error?.code);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// International domain validation
|
|
95
|
-
const intlResult = validateInternationalEmail('user@mΓΌnchen.de');
|
|
96
|
-
if (!intlResult) {
|
|
97
|
-
console.log('Valid international email');
|
|
98
|
-
} else {
|
|
99
|
-
console.log('Validation error:', intlResult.message);
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Batch Processing
|
|
104
|
-
|
|
105
|
-
**New in 2.7.0**: Process multiple emails efficiently with deduplication:
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
import { batchProcessEmails } from '@mikkelscheike/email-provider-links';
|
|
109
|
-
|
|
110
|
-
const emails = [
|
|
111
|
-
'user@gmail.com',
|
|
112
|
-
'u.s.e.r+work@gmail.com', // Alias of the first email
|
|
113
|
-
'test@yahoo.com',
|
|
114
|
-
'invalid-email'
|
|
115
|
-
];
|
|
116
|
-
|
|
117
|
-
const results = batchProcessEmails(emails, {
|
|
118
|
-
includeProviderInfo: true,
|
|
119
|
-
normalizeEmails: true,
|
|
120
|
-
deduplicateAliases: true
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
results.forEach(result => {
|
|
124
|
-
console.log(`${result.email}: ${result.isValid ? 'Valid' : 'Invalid'}`);
|
|
125
|
-
if (result.isDuplicate) {
|
|
126
|
-
console.log(' π Duplicate detected');
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
```
|
|
130
|
-
|
|
131
62
|
## Supported Providers
|
|
132
63
|
|
|
133
|
-
**π Current Coverage: 93 providers supporting
|
|
64
|
+
**π Current Coverage: 93 providers supporting 207 domains**
|
|
134
65
|
|
|
135
66
|
**Consumer Email Providers:**
|
|
136
67
|
- **Gmail** (2 domains): gmail.com, googlemail.com
|
|
@@ -161,51 +92,53 @@ results.forEach(result => {
|
|
|
161
92
|
|
|
162
93
|
## API Reference
|
|
163
94
|
|
|
164
|
-
###
|
|
165
|
-
|
|
95
|
+
### Core Functions
|
|
96
|
+
|
|
97
|
+
#### `getEmailProvider(email, timeout?)`
|
|
98
|
+
**Recommended** - Complete provider detection with business domain support.
|
|
166
99
|
|
|
167
100
|
```typescript
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// π For business domains - DNS lookup required, timeout matters
|
|
176
|
-
const biz1 = await getEmailProvider('user@mycompany.com'); // 5000ms timeout (default)
|
|
177
|
-
const biz2 = await getEmailProvider('user@mycompany.com', 2000); // 2000ms timeout (faster fail)
|
|
178
|
-
const biz3 = await getEmailProvider('user@mycompany.com', 10000); // 10000ms timeout (slower networks)
|
|
179
|
-
// All may detect: { provider: "Google Workspace", detectionMethod: "mx_record" }
|
|
180
|
-
|
|
181
|
-
// π― WHY USE CUSTOM TIMEOUT?
|
|
182
|
-
// - Faster apps: Use 2000ms to fail fast on unknown domains
|
|
183
|
-
// - Slower networks: Use 10000ms to avoid premature timeouts
|
|
184
|
-
// - Enterprise: Use 1000ms for strict SLA requirements
|
|
101
|
+
// Known providers (instant response)
|
|
102
|
+
const result1 = await getEmailProvider('user@gmail.com');
|
|
103
|
+
// Returns: { provider: "Gmail", loginUrl: "https://mail.google.com/mail/" }
|
|
104
|
+
|
|
105
|
+
// Business domains (DNS lookup with timeout)
|
|
106
|
+
const result2 = await getEmailProvider('user@company.com', 2000);
|
|
107
|
+
// Returns: { provider: "Google Workspace", detectionMethod: "mx_record" }
|
|
185
108
|
```
|
|
186
109
|
|
|
187
|
-
|
|
188
|
-
**
|
|
110
|
+
#### `getEmailProviderSync(email)`
|
|
111
|
+
**Fast** - Instant checks for known providers (no DNS lookup).
|
|
189
112
|
|
|
190
113
|
```typescript
|
|
191
|
-
const result = getEmailProviderSync('user@
|
|
192
|
-
// Returns: { provider, loginUrl
|
|
114
|
+
const result = getEmailProviderSync('user@outlook.com');
|
|
115
|
+
// Returns: { provider: "Outlook", loginUrl: "https://outlook.live.com/" }
|
|
193
116
|
```
|
|
194
117
|
|
|
195
|
-
###
|
|
196
|
-
|
|
118
|
+
### Email Alias Support
|
|
119
|
+
|
|
120
|
+
The library handles provider-specific email alias rules:
|
|
197
121
|
|
|
198
122
|
```typescript
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
123
|
+
// Gmail ignores dots and plus addressing
|
|
124
|
+
emailsMatch('user.name+work@gmail.com', 'username@gmail.com') // true
|
|
125
|
+
|
|
126
|
+
// Outlook preserves dots but ignores plus addressing
|
|
127
|
+
emailsMatch('user.name+work@outlook.com', 'username@outlook.com') // false
|
|
128
|
+
|
|
129
|
+
// Normalize emails to canonical form
|
|
130
|
+
const canonical = normalizeEmail('u.s.e.r+tag@gmail.com');
|
|
131
|
+
console.log(canonical); // 'user@gmail.com'
|
|
207
132
|
```
|
|
208
133
|
|
|
134
|
+
**Provider Rules Overview**:
|
|
135
|
+
- **Gmail**: Ignores dots, supports plus addressing
|
|
136
|
+
- **Outlook**: Preserves dots, supports plus addressing
|
|
137
|
+
- **Yahoo**: Preserves dots, supports plus addressing
|
|
138
|
+
- **ProtonMail**: Preserves dots, supports plus addressing
|
|
139
|
+
- **FastMail**: Preserves dots, supports plus addressing
|
|
140
|
+
- **AOL**: Preserves everything except case
|
|
141
|
+
|
|
209
142
|
## Real-World Example
|
|
210
143
|
|
|
211
144
|
```typescript
|
|
@@ -301,93 +234,63 @@ console.log(domain); // 'example.com'
|
|
|
301
234
|
|
|
302
235
|
</details>
|
|
303
236
|
|
|
304
|
-
##
|
|
305
|
-
|
|
306
|
-
Full TypeScript support with comprehensive interfaces:
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
interface EmailProviderResult {
|
|
310
|
-
provider: EmailProvider | null;
|
|
311
|
-
email: string;
|
|
312
|
-
loginUrl: string | null;
|
|
313
|
-
detectionMethod?: 'domain_match' | 'mx_record' | 'txt_record' | 'both' | 'proxy_detected';
|
|
314
|
-
proxyService?: string;
|
|
315
|
-
error?: {
|
|
316
|
-
type: 'INVALID_EMAIL' | 'DNS_TIMEOUT' | 'RATE_LIMITED' | 'UNKNOWN_DOMAIN' |
|
|
317
|
-
'NETWORK_ERROR' | 'IDN_VALIDATION_ERROR';
|
|
318
|
-
message: string;
|
|
319
|
-
retryAfter?: number; // seconds until retry allowed (for rate limiting)
|
|
320
|
-
idnError?: string; // specific IDN validation error message
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
interface ConfigConstants {
|
|
325
|
-
DEFAULT_DNS_TIMEOUT: number; // 5000ms
|
|
326
|
-
MAX_DNS_REQUESTS_PER_MINUTE: number; // 10 requests
|
|
327
|
-
SUPPORTED_PROVIDERS_COUNT: number; // 93 providers
|
|
328
|
-
SUPPORTED_DOMAINS_COUNT: number; // 178 domains
|
|
329
|
-
}
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
## π‘οΈ Security Features
|
|
333
|
-
|
|
334
|
-
This package implements **enterprise-grade security** to protect against malicious redirects and supply chain attacks:
|
|
237
|
+
## Performance and Detection System
|
|
335
238
|
|
|
336
|
-
|
|
239
|
+
For detailed performance metrics and information about the detection system, refer to [Performance and Detection System](docs/PERFORMANCE.md).
|
|
337
240
|
|
|
338
|
-
|
|
339
|
-
- **Domain Allowlisting**: Only pre-approved domains are allowed (93+ verified providers)
|
|
340
|
-
- **Malicious Pattern Detection**: Blocks IP addresses, URL shorteners, suspicious TLDs
|
|
341
|
-
- **Path Traversal Prevention**: Detects and blocks `../` and encoded variants
|
|
342
|
-
- **JavaScript Injection Protection**: Prevents `javascript:`, `data:`, and script injections
|
|
343
|
-
- **File Integrity Verification**: SHA-256 hash verification for provider database
|
|
241
|
+
### Development Mode Features
|
|
344
242
|
|
|
345
|
-
|
|
243
|
+
When `NODE_ENV` is set to 'development', the library provides additional insights:
|
|
346
244
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
- β **Protocol Downgrade**: Blocked by HTTPS enforcement
|
|
352
|
-
- β **Path Traversal**: Blocked by path validation
|
|
353
|
-
- β **Script Injection**: Blocked by content validation
|
|
354
|
-
- β **Supply Chain Attacks**: Blocked by integrity verification
|
|
355
|
-
|
|
356
|
-
### π§ͺ Security Testing
|
|
245
|
+
```typescript
|
|
246
|
+
// Memory usage is automatically logged:
|
|
247
|
+
// π Current memory usage: 0.08 MB
|
|
248
|
+
```
|
|
357
249
|
|
|
358
|
-
|
|
359
|
-
- **92+ dedicated security tests** covering all attack vectors
|
|
360
|
-
- **Automated security validation** in CI/CD pipeline
|
|
361
|
-
- **Regular security audits** of provider database
|
|
250
|
+
### Memory Management
|
|
362
251
|
|
|
363
|
-
|
|
252
|
+
The library implements careful memory management:
|
|
253
|
+
- Initial load: ~0.08MB heap usage
|
|
254
|
+
- Batch operations: ~0.03MB per 1000 operations
|
|
255
|
+
- Maximum load: < 25MB even under heavy concurrent operations
|
|
256
|
+
- Automatic garbage collection hints
|
|
257
|
+
- Memory usage logging in development mode
|
|
364
258
|
|
|
365
|
-
|
|
259
|
+
### Performance Benchmarks
|
|
366
260
|
|
|
367
|
-
|
|
368
|
-
- **Email lookups**: ~0.02MB heap usage per 100 operations
|
|
369
|
-
- **Concurrent DNS**: ~0.03MB heap usage, ~110ms for 10 lookups
|
|
370
|
-
- **Large scale (1000 ops)**: ~0.02MB heap usage, <3ms total
|
|
371
|
-
- **International validation**: <1ms for complex IDN domains
|
|
261
|
+
Extensively optimized for both speed and memory efficiency:
|
|
372
262
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
263
|
+
**Speed Metrics**:
|
|
264
|
+
- Initial provider load: ~0.5ms
|
|
265
|
+
- Known provider lookup: <1ms
|
|
266
|
+
- DNS-based detection: ~27ms average
|
|
267
|
+
- Batch processing: 1000 operations in ~1.1ms
|
|
268
|
+
- Email validation: <1ms for complex IDN domains
|
|
377
269
|
|
|
378
|
-
|
|
270
|
+
**Memory Usage**:
|
|
271
|
+
- Initial footprint: ~0.08MB
|
|
272
|
+
- Per operation: ~0.03MB per 1000 lookups
|
|
273
|
+
- Peak usage: <25MB under heavy load
|
|
274
|
+
- Cache efficiency: >99% hit rate
|
|
275
|
+
- Garbage collection: Automatic optimization
|
|
379
276
|
|
|
380
|
-
|
|
277
|
+
**Real-World Performance**:
|
|
278
|
+
- 50,000+ operations/second for known providers
|
|
279
|
+
- 100 concurrent DNS lookups in <1 second
|
|
280
|
+
- Average latency: <1ms for cached lookups
|
|
281
|
+
- Maximum latency: <5ms per lookup
|
|
381
282
|
|
|
283
|
+
To run benchmarks:
|
|
382
284
|
```bash
|
|
383
|
-
|
|
285
|
+
npm run benchmark # Basic benchmarks
|
|
286
|
+
node --expose-gc benchmark/memory.ts # Detailed memory analysis
|
|
384
287
|
```
|
|
385
288
|
|
|
386
289
|
## Contributing
|
|
387
290
|
|
|
388
291
|
We welcome contributions! See [CONTRIBUTING.md](docs/CONTRIBUTING.md) for guidelines on adding new email providers.
|
|
389
292
|
|
|
390
|
-
**Quality Assurance**: This project maintains high standards with
|
|
293
|
+
**Quality Assurance**: This project maintains high standards with 424 comprehensive tests achieving 93.16% code coverage (96.46% function coverage).
|
|
391
294
|
**Security Note**: All new providers undergo security validation and must pass our allowlist verification.
|
|
392
295
|
|
|
393
296
|
## Security
|
|
@@ -400,4 +303,4 @@ MIT License - see [LICENSE](LICENSE) file for details.
|
|
|
400
303
|
|
|
401
304
|
---
|
|
402
305
|
|
|
403
|
-
**Zero dependencies β’ TypeScript-first β’ Production ready β’ International support**
|
|
306
|
+
**Zero dependencies β’ TypeScript-first β’ Production ready β’ International support**
|
package/dist/alias-detection.js
CHANGED
|
@@ -9,184 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.detectEmailAlias = detectEmailAlias;
|
|
10
10
|
exports.normalizeEmail = normalizeEmail;
|
|
11
11
|
exports.emailsMatch = emailsMatch;
|
|
12
|
-
|
|
13
|
-
* Email alias rules for major providers
|
|
14
|
-
*/
|
|
15
|
-
const ALIAS_RULES = [
|
|
16
|
-
{
|
|
17
|
-
domains: ['gmail.com', 'googlemail.com'],
|
|
18
|
-
supportsPlusAddressing: true,
|
|
19
|
-
ignoresDots: true,
|
|
20
|
-
normalize: (email) => {
|
|
21
|
-
const parts = email.toLowerCase().split('@');
|
|
22
|
-
const username = parts[0];
|
|
23
|
-
const domain = parts[1];
|
|
24
|
-
if (!username || !domain) {
|
|
25
|
-
return email.toLowerCase();
|
|
26
|
-
}
|
|
27
|
-
// Remove dots and everything after +
|
|
28
|
-
const cleanUsername = username.replace(/\./g, '').split('+')[0];
|
|
29
|
-
return `${cleanUsername}@${domain}`;
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
domains: ['outlook.com', 'hotmail.com', 'live.com', 'msn.com', 'hotmail.co.uk', 'hotmail.fr', 'hotmail.it', 'hotmail.es', 'hotmail.de', 'live.co.uk', 'live.fr', 'live.it', 'live.nl', 'live.com.au', 'live.ca', 'live.jp'],
|
|
34
|
-
supportsPlusAddressing: true,
|
|
35
|
-
ignoresDots: false,
|
|
36
|
-
normalize: (email) => {
|
|
37
|
-
const parts = email.toLowerCase().split('@');
|
|
38
|
-
const username = parts[0];
|
|
39
|
-
const domain = parts[1];
|
|
40
|
-
if (!username || !domain) {
|
|
41
|
-
return email.toLowerCase();
|
|
42
|
-
}
|
|
43
|
-
// Only remove plus addressing for Outlook
|
|
44
|
-
const cleanUsername = username.split('+')[0];
|
|
45
|
-
return `${cleanUsername}@${domain}`;
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
domains: ['yahoo.com', 'yahoo.co.uk', 'yahoo.fr', 'yahoo.co.in', 'yahoo.com.br', 'yahoo.co.jp', 'yahoo.it', 'yahoo.de', 'yahoo.in', 'yahoo.es', 'yahoo.ca', 'yahoo.com.au', 'yahoo.com.ar', 'yahoo.com.mx', 'yahoo.co.id', 'yahoo.com.sg', 'ymail.com', 'rocketmail.com'],
|
|
50
|
-
supportsPlusAddressing: true,
|
|
51
|
-
ignoresDots: false,
|
|
52
|
-
normalize: (email) => {
|
|
53
|
-
const parts = email.toLowerCase().split('@');
|
|
54
|
-
const username = parts[0];
|
|
55
|
-
const domain = parts[1];
|
|
56
|
-
if (!username || !domain) {
|
|
57
|
-
return email.toLowerCase();
|
|
58
|
-
}
|
|
59
|
-
const cleanUsername = username.split('+')[0];
|
|
60
|
-
return `${cleanUsername}@${domain}`;
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
domains: ['fastmail.com', 'fastmail.fm'],
|
|
65
|
-
supportsPlusAddressing: true,
|
|
66
|
-
ignoresDots: false,
|
|
67
|
-
normalize: (email) => {
|
|
68
|
-
const parts = email.toLowerCase().split('@');
|
|
69
|
-
const username = parts[0];
|
|
70
|
-
const domain = parts[1];
|
|
71
|
-
if (!username || !domain) {
|
|
72
|
-
return email.toLowerCase();
|
|
73
|
-
}
|
|
74
|
-
const cleanUsername = username.split('+')[0];
|
|
75
|
-
return `${cleanUsername}@${domain}`;
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
domains: ['proton.me', 'protonmail.com', 'protonmail.ch', 'pm.me'],
|
|
80
|
-
supportsPlusAddressing: true,
|
|
81
|
-
ignoresDots: false,
|
|
82
|
-
normalize: (email) => {
|
|
83
|
-
const parts = email.toLowerCase().split('@');
|
|
84
|
-
const username = parts[0];
|
|
85
|
-
const domain = parts[1];
|
|
86
|
-
if (!username || !domain) {
|
|
87
|
-
return email.toLowerCase();
|
|
88
|
-
}
|
|
89
|
-
const cleanUsername = username.split('+')[0];
|
|
90
|
-
return `${cleanUsername}@${domain}`;
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
domains: ['tutanota.com', 'tutanota.de', 'tutamail.com', 'tuta.io', 'keemail.me', 'tuta.com'],
|
|
95
|
-
supportsPlusAddressing: true,
|
|
96
|
-
ignoresDots: false,
|
|
97
|
-
normalize: (email) => {
|
|
98
|
-
const parts = email.toLowerCase().split('@');
|
|
99
|
-
const username = parts[0];
|
|
100
|
-
const domain = parts[1];
|
|
101
|
-
if (!username || !domain) {
|
|
102
|
-
return email.toLowerCase();
|
|
103
|
-
}
|
|
104
|
-
const cleanUsername = username.split('+')[0];
|
|
105
|
-
return `${cleanUsername}@${domain}`;
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
domains: ['zoho.com', 'zohomail.com', 'zoho.eu'],
|
|
110
|
-
supportsPlusAddressing: true,
|
|
111
|
-
ignoresDots: false,
|
|
112
|
-
normalize: (email) => {
|
|
113
|
-
const parts = email.toLowerCase().split('@');
|
|
114
|
-
const username = parts[0];
|
|
115
|
-
const domain = parts[1];
|
|
116
|
-
if (!username || !domain) {
|
|
117
|
-
return email.toLowerCase();
|
|
118
|
-
}
|
|
119
|
-
const cleanUsername = username.split('+')[0];
|
|
120
|
-
return `${cleanUsername}@${domain}`;
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
domains: ['icloud.com', 'me.com', 'mac.com'],
|
|
125
|
-
supportsPlusAddressing: true,
|
|
126
|
-
ignoresDots: false,
|
|
127
|
-
normalize: (email) => {
|
|
128
|
-
const parts = email.toLowerCase().split('@');
|
|
129
|
-
const username = parts[0];
|
|
130
|
-
const domain = parts[1];
|
|
131
|
-
if (!username || !domain) {
|
|
132
|
-
return email.toLowerCase();
|
|
133
|
-
}
|
|
134
|
-
const cleanUsername = username.split('+')[0];
|
|
135
|
-
return `${cleanUsername}@${domain}`;
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
domains: ['mail.com'],
|
|
140
|
-
supportsPlusAddressing: true,
|
|
141
|
-
ignoresDots: false,
|
|
142
|
-
normalize: (email) => {
|
|
143
|
-
const parts = email.toLowerCase().split('@');
|
|
144
|
-
const username = parts[0];
|
|
145
|
-
const domain = parts[1];
|
|
146
|
-
if (!username || !domain) {
|
|
147
|
-
return email.toLowerCase();
|
|
148
|
-
}
|
|
149
|
-
const cleanUsername = username.split('+')[0];
|
|
150
|
-
return `${cleanUsername}@${domain}`;
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
domains: ['aol.com', 'love.com', 'ygm.com', 'games.com', 'wow.com', 'aim.com'],
|
|
155
|
-
supportsPlusAddressing: false,
|
|
156
|
-
ignoresDots: false,
|
|
157
|
-
normalize: (email) => email.toLowerCase()
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
domains: ['mail.ru'],
|
|
161
|
-
supportsPlusAddressing: true,
|
|
162
|
-
ignoresDots: false,
|
|
163
|
-
normalize: (email) => {
|
|
164
|
-
const parts = email.toLowerCase().split('@');
|
|
165
|
-
const username = parts[0];
|
|
166
|
-
const domain = parts[1];
|
|
167
|
-
if (!username || !domain) {
|
|
168
|
-
return email.toLowerCase();
|
|
169
|
-
}
|
|
170
|
-
const cleanUsername = username.split('+')[0];
|
|
171
|
-
return `${cleanUsername}@${domain}`;
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
domains: ['yandex.com', 'yandex.ru'],
|
|
176
|
-
supportsPlusAddressing: true,
|
|
177
|
-
ignoresDots: false,
|
|
178
|
-
normalize: (email) => {
|
|
179
|
-
const parts = email.toLowerCase().split('@');
|
|
180
|
-
const username = parts[0];
|
|
181
|
-
const domain = parts[1];
|
|
182
|
-
if (!username || !domain) {
|
|
183
|
-
return email.toLowerCase();
|
|
184
|
-
}
|
|
185
|
-
const cleanUsername = username.split('+')[0];
|
|
186
|
-
return `${cleanUsername}@${domain}`;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
];
|
|
12
|
+
const loader_1 = require("./loader");
|
|
190
13
|
/**
|
|
191
14
|
* Validates email format
|
|
192
15
|
*/
|
|
@@ -194,12 +17,6 @@ function isValidEmail(email) {
|
|
|
194
17
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
195
18
|
return emailRegex.test(email);
|
|
196
19
|
}
|
|
197
|
-
/**
|
|
198
|
-
* Gets the alias rule for a given domain
|
|
199
|
-
*/
|
|
200
|
-
function getAliasRule(domain) {
|
|
201
|
-
return ALIAS_RULES.find(rule => rule.domains.includes(domain.toLowerCase())) || null;
|
|
202
|
-
}
|
|
203
20
|
/**
|
|
204
21
|
* Detects and analyzes email aliases
|
|
205
22
|
*
|
|
@@ -217,49 +34,61 @@ function detectEmailAlias(email) {
|
|
|
217
34
|
if (!username || !domain) {
|
|
218
35
|
throw new Error('Invalid email format - missing username or domain');
|
|
219
36
|
}
|
|
220
|
-
const
|
|
37
|
+
const { domainMap } = (0, loader_1.loadProviders)();
|
|
38
|
+
const provider = domainMap.get(domain);
|
|
221
39
|
const result = {
|
|
222
40
|
canonical: originalEmail.toLowerCase(),
|
|
223
41
|
original: originalEmail,
|
|
224
42
|
isAlias: false,
|
|
225
43
|
aliasType: 'none'
|
|
226
44
|
};
|
|
227
|
-
if (!
|
|
228
|
-
// No specific rule, just normalize case
|
|
229
|
-
result.canonical = originalEmail.toLowerCase();
|
|
45
|
+
if (!provider?.alias) {
|
|
230
46
|
return result;
|
|
231
47
|
}
|
|
232
48
|
result.provider = domain;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return result;
|
|
49
|
+
let normalizedUsername = username;
|
|
50
|
+
let isAlias = false;
|
|
51
|
+
let aliasType = 'none';
|
|
52
|
+
let aliasPart;
|
|
53
|
+
// Handle case sensitivity (all modern providers are case-insensitive)
|
|
54
|
+
if (provider.alias?.case?.ignore) {
|
|
55
|
+
if (provider.alias.case?.strip) {
|
|
56
|
+
normalizedUsername = normalizedUsername.toLowerCase();
|
|
57
|
+
}
|
|
243
58
|
}
|
|
244
|
-
//
|
|
245
|
-
if (
|
|
246
|
-
const
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
59
|
+
// Handle plus addressing (common for Gmail, Outlook, Yahoo, etc.)
|
|
60
|
+
if (provider.alias?.plus?.ignore) {
|
|
61
|
+
const plusIndex = username.indexOf('+');
|
|
62
|
+
if (plusIndex !== -1) {
|
|
63
|
+
aliasPart = username.substring(plusIndex + 1);
|
|
64
|
+
isAlias = true;
|
|
65
|
+
aliasType = 'plus';
|
|
66
|
+
if (provider.alias.plus?.strip) {
|
|
67
|
+
normalizedUsername = username.slice(0, plusIndex);
|
|
68
|
+
}
|
|
253
69
|
}
|
|
254
70
|
}
|
|
255
|
-
//
|
|
256
|
-
if (
|
|
257
|
-
const
|
|
258
|
-
if (
|
|
259
|
-
|
|
260
|
-
|
|
71
|
+
// Handle dots (primarily for Gmail)
|
|
72
|
+
if (provider.alias?.dots?.ignore) {
|
|
73
|
+
const hasDots = username.includes('.');
|
|
74
|
+
if (hasDots) {
|
|
75
|
+
if (!isAlias) {
|
|
76
|
+
aliasPart = username;
|
|
77
|
+
isAlias = true;
|
|
78
|
+
aliasType = 'dot';
|
|
79
|
+
}
|
|
80
|
+
if (provider.alias.dots?.strip) {
|
|
81
|
+
normalizedUsername = normalizedUsername.replace(/\./g, '');
|
|
82
|
+
}
|
|
261
83
|
}
|
|
262
84
|
}
|
|
85
|
+
// Build the canonical form
|
|
86
|
+
result.canonical = `${normalizedUsername}@${domain}`;
|
|
87
|
+
result.isAlias = isAlias;
|
|
88
|
+
result.aliasType = aliasType;
|
|
89
|
+
if (aliasPart !== undefined) {
|
|
90
|
+
result.aliasPart = aliasPart;
|
|
91
|
+
}
|
|
263
92
|
return result;
|
|
264
93
|
}
|
|
265
94
|
/**
|