@mikkelscheike/email-provider-links 1.6.0 โ†’ 1.7.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/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # Email Provider Links
2
2
 
3
- ๐Ÿ”’ **Enterprise-grade secure email provider detection for login and password reset flows**
3
+ ๐Ÿ”’ **Enterprise-grade secure email provider detection with advanced alias normalization**
4
4
 
5
- A TypeScript package that provides direct links to email providers based on email addresses, with comprehensive security features to prevent malicious redirects and supply chain attacks.
5
+ A TypeScript package providing direct links to **93 email providers** (180+ domains) with **email alias detection**, comprehensive security features, and international coverage for login and password reset flows.
6
6
 
7
7
  ## โœจ Features
8
8
 
9
9
  - ๐Ÿš€ **Fast & Lightweight**: Zero dependencies, minimal footprint
10
- - ๐Ÿ“ง **74 Email Providers**: Gmail, Outlook, Yahoo, ProtonMail, iCloud, and more
11
- - ๐ŸŒ **147+ Domains Supported**: Comprehensive international coverage
10
+ - ๐Ÿ“ง **93 Email Providers**: Gmail, Outlook, Yahoo, ProtonMail, iCloud, and many more
11
+ - ๐ŸŒ **180+ Domains Supported**: Comprehensive international coverage
12
12
  - ๐Ÿข **Business Domain Detection**: DNS-based detection for custom domains (Google Workspace, Microsoft 365, etc.)
13
13
  - ๐Ÿ”’ **Enterprise Security**: Multi-layer protection against malicious URLs and supply chain attacks
14
14
  - ๐Ÿ›ก๏ธ **URL Validation**: HTTPS-only enforcement with domain allowlisting
@@ -16,7 +16,9 @@ A TypeScript package that provides direct links to email providers based on emai
16
16
  - ๐Ÿ“ **Type Safe**: Full TypeScript support with comprehensive interfaces
17
17
  - โšก **Performance Optimized**: Smart DNS fallback with configurable timeouts
18
18
  - ๐Ÿšฆ **Rate Limiting**: Built-in DNS query rate limiting to prevent abuse
19
- - ๐Ÿงช **Thoroughly Tested**: 142+ tests with 94.69% code coverage
19
+ - ๐Ÿ”„ **Email Alias Detection**: Normalize Gmail dots, plus addressing, and provider-specific aliases
20
+ - ๐Ÿ›ก๏ธ **Fraud Prevention**: Detect duplicate accounts through email alias manipulation
21
+ - ๐Ÿงช **Thoroughly Tested**: 188+ tests with comprehensive coverage
20
22
 
21
23
  ## Installation
22
24
 
@@ -40,7 +42,7 @@ console.log(business.provider?.companyProvider); // "Google Workspace" (if detec
40
42
 
41
43
  ## Supported Providers
42
44
 
43
- **๐Ÿ“Š Current Coverage: 74 providers supporting 147+ domains**
45
+ **๐Ÿ“Š Current Coverage: 93 providers supporting 180+ domains**
44
46
 
45
47
  **Consumer Email Providers:**
46
48
  - **Gmail** (2 domains): gmail.com, googlemail.com
@@ -64,9 +66,10 @@ console.log(business.provider?.companyProvider); // "Google Workspace" (if detec
64
66
  - **Mailfence, SimpleLogin, AnonAddy**
65
67
 
66
68
  **International Providers:**
67
- - **Europe**: GMX, Web.de, Orange, Free.fr, T-Online, Libero
68
- - **Asia**: QQ Mail, NetEase, Sina Mail, Rakuten, Nifty, **Naver** (Korea), **Daum** (Korea), **Biglobe** (Japan)
69
- - **Other Regions**: UOL (Brazil), Telkom (South Africa), Xtra (New Zealand)
69
+ - **Europe**: GMX, Web.de, Orange, Free.fr, T-Online, Libero, Virgilio, Telekom, Tiscali, Skynet, Telenet, Xs4All, Planet.nl, Bluewin, Eircom
70
+ - **Asia**: QQ Mail, NetEase, Sina Mail, Alibaba Mail, Rakuten, Nifty, **Naver** (Korea), **Daum** (Korea), **Biglobe** (Japan), Sify, IndiatTimes (India)
71
+ - **Eastern Europe**: Centrum (Czech/Slovak), Interia, Onet (Poland), Rambler (Russia)
72
+ - **Other Regions**: UOL, Terra (Brazil), Telkom (South Africa), Xtra (New Zealand)
70
73
 
71
74
  ## API
72
75
 
@@ -100,6 +103,94 @@ async function handlePasswordReset(email: string) {
100
103
  }
101
104
  ```
102
105
 
106
+ ## ๐Ÿ”„ Email Alias Detection
107
+
108
+ **NEW in v1.7.0** - Advanced email alias detection and normalization to prevent duplicate accounts and improve user experience.
109
+
110
+ ### Features
111
+
112
+ - **Gmail Dot Normalization**: `u.s.e.r@gmail.com` โ†’ `user@gmail.com`
113
+ - **Plus Addressing**: `user+newsletter@provider.com` โ†’ `user@provider.com`
114
+ - **Provider-Specific Rules**: Different providers have different aliasing capabilities
115
+ - **Fraud Prevention**: Detect when users try to create multiple accounts with aliases
116
+ - **Email Deduplication**: Normalize email lists to get accurate unique user counts
117
+
118
+ ### Quick Start
119
+
120
+ ```typescript
121
+ import {
122
+ detectEmailAlias,
123
+ normalizeEmail,
124
+ emailsMatch
125
+ } from '@mikkelscheike/email-provider-links';
126
+
127
+ // Detect and analyze aliases
128
+ const result = detectEmailAlias('u.s.e.r+work@gmail.com');
129
+ console.log(result.canonical); // 'user@gmail.com'
130
+ console.log(result.isAlias); // true
131
+ console.log(result.aliasType); // 'plus'
132
+ console.log(result.aliasPart); // 'work'
133
+
134
+ // Normalize any email to canonical form
135
+ const canonical = normalizeEmail('U.S.E.R+Newsletter@GMAIL.COM');
136
+ console.log(canonical); // 'user@gmail.com'
137
+
138
+ // Check if two emails are the same person
139
+ const match = emailsMatch('user@gmail.com', 'u.s.e.r+work@gmail.com');
140
+ console.log(match); // true
141
+ ```
142
+
143
+ ### Provider Support
144
+
145
+ | Provider | Plus Addressing | Dots Ignored | Example |
146
+ |----------|----------------|--------------|----------|
147
+ | **Gmail** | โœ… Yes | โœ… Yes | `u.s.e.r+tag@gmail.com` โ†’ `user@gmail.com` |
148
+ | **Outlook** | โœ… Yes | โŒ No | `user+tag@outlook.com` โ†’ `user@outlook.com` |
149
+ | **Yahoo** | โœ… Yes | โŒ No | `user+tag@yahoo.com` โ†’ `user@yahoo.com` |
150
+ | **ProtonMail** | โœ… Yes | โŒ No | `user+tag@proton.me` โ†’ `user@proton.me` |
151
+ | **AOL** | โŒ No | โŒ No | No aliasing support |
152
+
153
+ ### Advanced Usage
154
+
155
+ ```typescript
156
+ // Prevent duplicate account creation
157
+ async function registerUser(email: string) {
158
+ const canonical = normalizeEmail(email);
159
+ const existingUser = await findUserByEmail(canonical);
160
+
161
+ if (existingUser) {
162
+ throw new Error('Email already registered');
163
+ }
164
+
165
+ // Store canonical email to prevent future duplicates
166
+ await createUser({ email: canonical });
167
+ }
168
+
169
+ // Analyze email list for duplicates
170
+ import { analyzeEmailAliases } from '@mikkelscheike/email-provider-links';
171
+
172
+ const emailList = [
173
+ 'user@gmail.com',
174
+ 'u.s.e.r@gmail.com',
175
+ 'user+newsletter@gmail.com',
176
+ 'different@gmail.com'
177
+ ];
178
+
179
+ const analysis = analyzeEmailAliases(emailList);
180
+ console.log(analysis.totalEmails); // 4
181
+ console.log(analysis.uniqueCanonical); // 2 (actual unique users)
182
+
183
+ // Generate test aliases
184
+ import { generateAliases } from '@mikkelscheike/email-provider-links';
185
+
186
+ const aliases = generateAliases('user@gmail.com', {
187
+ plusAliases: ['work', 'personal'],
188
+ includeDotVariations: true,
189
+ maxDotVariations: 3
190
+ });
191
+ // Returns: ['user+work@gmail.com', 'user+personal@gmail.com', 'u.ser@gmail.com', ...]
192
+ ```
193
+
103
194
  ## Configuration
104
195
 
105
196
  ```typescript
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Email Alias Detection Module
3
+ *
4
+ * Detects and normalizes email aliases across different providers.
5
+ * Supports plus addressing (+), dot variations, and provider-specific aliasing.
6
+ */
7
+ export interface AliasDetectionResult {
8
+ /** The normalized/canonical email address */
9
+ canonical: string;
10
+ /** The original email address */
11
+ original: string;
12
+ /** Whether an alias was detected */
13
+ isAlias: boolean;
14
+ /** Type of alias detected */
15
+ aliasType: 'plus' | 'dot' | 'subdomain' | 'none';
16
+ /** The alias part (if any) */
17
+ aliasPart?: string;
18
+ /** The provider that supports this alias type */
19
+ provider?: string;
20
+ }
21
+ export interface AliasRule {
22
+ /** Email domains this rule applies to */
23
+ domains: string[];
24
+ /** Whether this provider supports plus addressing (user+alias@domain.com) */
25
+ supportsPlusAddressing: boolean;
26
+ /** Whether this provider ignores dots in username (user.name@domain.com = username@domain.com) */
27
+ ignoresDots: boolean;
28
+ /** Whether this provider supports subdomain aliases (user@alias.domain.com) */
29
+ supportsSubdomainAlias: boolean;
30
+ /** Custom alias patterns specific to this provider */
31
+ customPatterns?: RegExp[];
32
+ /** Function to normalize email for this provider */
33
+ normalize?: (email: string) => string;
34
+ }
35
+ /**
36
+ * Detects and analyzes email aliases
37
+ */
38
+ export declare function detectEmailAlias(email: string): AliasDetectionResult;
39
+ /**
40
+ * Normalizes an email address to its canonical form
41
+ */
42
+ export declare function normalizeEmail(email: string): string;
43
+ /**
44
+ * Checks if two email addresses are the same when normalized
45
+ */
46
+ export declare function emailsMatch(email1: string, email2: string): boolean;
47
+ /**
48
+ * Gets alias capabilities for a domain
49
+ */
50
+ export declare function getAliasCapabilities(domain: string): AliasRule | null;
51
+ /**
52
+ * Generates potential aliases for an email address
53
+ */
54
+ export declare function generateAliases(email: string, options?: {
55
+ plusAliases?: string[];
56
+ includeDotVariations?: boolean;
57
+ maxDotVariations?: number;
58
+ }): string[];
59
+ /**
60
+ * Analyzes email aliasing patterns in a list of emails
61
+ */
62
+ export declare function analyzeEmailAliases(emails: string[]): {
63
+ totalEmails: number;
64
+ uniqueCanonical: number;
65
+ aliasGroups: Array<{
66
+ canonical: string;
67
+ aliases: string[];
68
+ count: number;
69
+ }>;
70
+ providerStats: Record<string, {
71
+ total: number;
72
+ aliases: number;
73
+ types: Record<string, number>;
74
+ }>;
75
+ };
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+ /**
3
+ * Email Alias Detection Module
4
+ *
5
+ * Detects and normalizes email aliases across different providers.
6
+ * Supports plus addressing (+), dot variations, and provider-specific aliasing.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.detectEmailAlias = detectEmailAlias;
10
+ exports.normalizeEmail = normalizeEmail;
11
+ exports.emailsMatch = emailsMatch;
12
+ exports.getAliasCapabilities = getAliasCapabilities;
13
+ exports.generateAliases = generateAliases;
14
+ exports.analyzeEmailAliases = analyzeEmailAliases;
15
+ /**
16
+ * Email alias rules for major providers
17
+ */
18
+ const ALIAS_RULES = [
19
+ {
20
+ domains: ['gmail.com', 'googlemail.com'],
21
+ supportsPlusAddressing: true,
22
+ ignoresDots: true,
23
+ supportsSubdomainAlias: false,
24
+ normalize: (email) => {
25
+ const [username, domain] = email.toLowerCase().split('@');
26
+ // Remove dots and everything after +
27
+ const cleanUsername = username.replace(/\./g, '').split('+')[0];
28
+ return `${cleanUsername}@${domain}`;
29
+ }
30
+ },
31
+ {
32
+ 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'],
33
+ supportsPlusAddressing: true,
34
+ ignoresDots: false,
35
+ supportsSubdomainAlias: false,
36
+ normalize: (email) => {
37
+ const [username, domain] = email.toLowerCase().split('@');
38
+ // Only remove plus addressing for Outlook
39
+ const cleanUsername = username.split('+')[0];
40
+ return `${cleanUsername}@${domain}`;
41
+ }
42
+ },
43
+ {
44
+ 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'],
45
+ supportsPlusAddressing: true,
46
+ ignoresDots: false,
47
+ supportsSubdomainAlias: false,
48
+ normalize: (email) => {
49
+ const [username, domain] = email.toLowerCase().split('@');
50
+ const cleanUsername = username.split('+')[0];
51
+ return `${cleanUsername}@${domain}`;
52
+ }
53
+ },
54
+ {
55
+ domains: ['fastmail.com', 'fastmail.fm'],
56
+ supportsPlusAddressing: true,
57
+ ignoresDots: false,
58
+ supportsSubdomainAlias: true,
59
+ normalize: (email) => {
60
+ const [username, domain] = email.toLowerCase().split('@');
61
+ const cleanUsername = username.split('+')[0];
62
+ return `${cleanUsername}@${domain}`;
63
+ }
64
+ },
65
+ {
66
+ domains: ['proton.me', 'protonmail.com', 'protonmail.ch', 'pm.me'],
67
+ supportsPlusAddressing: true,
68
+ ignoresDots: false,
69
+ supportsSubdomainAlias: false,
70
+ normalize: (email) => {
71
+ const [username, domain] = email.toLowerCase().split('@');
72
+ const cleanUsername = username.split('+')[0];
73
+ return `${cleanUsername}@${domain}`;
74
+ }
75
+ },
76
+ {
77
+ domains: ['tutanota.com', 'tutanota.de', 'tutamail.com', 'tuta.io', 'keemail.me', 'tuta.com'],
78
+ supportsPlusAddressing: true,
79
+ ignoresDots: false,
80
+ supportsSubdomainAlias: false,
81
+ normalize: (email) => {
82
+ const [username, domain] = email.toLowerCase().split('@');
83
+ const cleanUsername = username.split('+')[0];
84
+ return `${cleanUsername}@${domain}`;
85
+ }
86
+ },
87
+ {
88
+ domains: ['zoho.com', 'zohomail.com', 'zoho.eu'],
89
+ supportsPlusAddressing: true,
90
+ ignoresDots: false,
91
+ supportsSubdomainAlias: false,
92
+ normalize: (email) => {
93
+ const [username, domain] = email.toLowerCase().split('@');
94
+ const cleanUsername = username.split('+')[0];
95
+ return `${cleanUsername}@${domain}`;
96
+ }
97
+ },
98
+ {
99
+ domains: ['icloud.com', 'me.com', 'mac.com'],
100
+ supportsPlusAddressing: true,
101
+ ignoresDots: false,
102
+ supportsSubdomainAlias: false,
103
+ normalize: (email) => {
104
+ const [username, domain] = email.toLowerCase().split('@');
105
+ const cleanUsername = username.split('+')[0];
106
+ return `${cleanUsername}@${domain}`;
107
+ }
108
+ },
109
+ {
110
+ domains: ['mail.com'],
111
+ supportsPlusAddressing: true,
112
+ ignoresDots: false,
113
+ supportsSubdomainAlias: false,
114
+ normalize: (email) => {
115
+ const [username, domain] = email.toLowerCase().split('@');
116
+ const cleanUsername = username.split('+')[0];
117
+ return `${cleanUsername}@${domain}`;
118
+ }
119
+ },
120
+ {
121
+ domains: ['aol.com', 'love.com', 'ygm.com', 'games.com', 'wow.com', 'aim.com'],
122
+ supportsPlusAddressing: false,
123
+ ignoresDots: false,
124
+ supportsSubdomainAlias: false,
125
+ normalize: (email) => email.toLowerCase()
126
+ },
127
+ {
128
+ domains: ['mail.ru'],
129
+ supportsPlusAddressing: true,
130
+ ignoresDots: false,
131
+ supportsSubdomainAlias: false,
132
+ normalize: (email) => {
133
+ const [username, domain] = email.toLowerCase().split('@');
134
+ const cleanUsername = username.split('+')[0];
135
+ return `${cleanUsername}@${domain}`;
136
+ }
137
+ },
138
+ {
139
+ domains: ['yandex.com', 'yandex.ru'],
140
+ supportsPlusAddressing: true,
141
+ ignoresDots: false,
142
+ supportsSubdomainAlias: false,
143
+ normalize: (email) => {
144
+ const [username, domain] = email.toLowerCase().split('@');
145
+ const cleanUsername = username.split('+')[0];
146
+ return `${cleanUsername}@${domain}`;
147
+ }
148
+ }
149
+ ];
150
+ /**
151
+ * Validates email format
152
+ */
153
+ function isValidEmail(email) {
154
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
155
+ return emailRegex.test(email);
156
+ }
157
+ /**
158
+ * Gets the alias rule for a given domain
159
+ */
160
+ function getAliasRule(domain) {
161
+ return ALIAS_RULES.find(rule => rule.domains.includes(domain.toLowerCase()));
162
+ }
163
+ /**
164
+ * Detects and analyzes email aliases
165
+ */
166
+ function detectEmailAlias(email) {
167
+ if (!isValidEmail(email)) {
168
+ throw new Error('Invalid email format');
169
+ }
170
+ const originalEmail = email.trim();
171
+ const [username, domain] = originalEmail.toLowerCase().split('@');
172
+ const rule = getAliasRule(domain);
173
+ const result = {
174
+ canonical: originalEmail.toLowerCase(),
175
+ original: originalEmail,
176
+ isAlias: false,
177
+ aliasType: 'none'
178
+ };
179
+ if (!rule) {
180
+ // No specific rule, just normalize case
181
+ result.canonical = originalEmail.toLowerCase();
182
+ return result;
183
+ }
184
+ result.provider = domain;
185
+ // Check for plus addressing
186
+ if (rule.supportsPlusAddressing && username.includes('+')) {
187
+ const plusIndex = username.indexOf('+');
188
+ const baseUsername = username.substring(0, plusIndex);
189
+ const aliasPart = username.substring(plusIndex + 1);
190
+ result.isAlias = true;
191
+ result.aliasType = 'plus';
192
+ result.aliasPart = aliasPart;
193
+ result.canonical = rule.normalize ? rule.normalize(originalEmail) : `${baseUsername}@${domain}`;
194
+ return result;
195
+ }
196
+ // Check for dot variations (Gmail)
197
+ if (rule.ignoresDots && username.includes('.')) {
198
+ const baseUsername = username.replace(/\./g, '');
199
+ if (baseUsername !== username) {
200
+ result.isAlias = true;
201
+ result.aliasType = 'dot';
202
+ result.aliasPart = username;
203
+ result.canonical = rule.normalize ? rule.normalize(originalEmail) : `${baseUsername}@${domain}`;
204
+ return result;
205
+ }
206
+ }
207
+ // Apply provider-specific normalization
208
+ if (rule.normalize) {
209
+ const normalized = rule.normalize(originalEmail);
210
+ if (normalized !== originalEmail.toLowerCase()) {
211
+ result.isAlias = true;
212
+ result.canonical = normalized;
213
+ }
214
+ }
215
+ return result;
216
+ }
217
+ /**
218
+ * Normalizes an email address to its canonical form
219
+ */
220
+ function normalizeEmail(email) {
221
+ const result = detectEmailAlias(email);
222
+ return result.canonical;
223
+ }
224
+ /**
225
+ * Checks if two email addresses are the same when normalized
226
+ */
227
+ function emailsMatch(email1, email2) {
228
+ try {
229
+ return normalizeEmail(email1) === normalizeEmail(email2);
230
+ }
231
+ catch {
232
+ return false;
233
+ }
234
+ }
235
+ /**
236
+ * Gets alias capabilities for a domain
237
+ */
238
+ function getAliasCapabilities(domain) {
239
+ const rule = getAliasRule(domain.toLowerCase());
240
+ return rule ? { ...rule } : null;
241
+ }
242
+ /**
243
+ * Generates potential aliases for an email address
244
+ */
245
+ function generateAliases(email, options = {}) {
246
+ if (!isValidEmail(email)) {
247
+ throw new Error('Invalid email format');
248
+ }
249
+ const [username, domain] = email.toLowerCase().split('@');
250
+ const rule = getAliasRule(domain);
251
+ const aliases = [];
252
+ if (!rule) {
253
+ return [email.toLowerCase()];
254
+ }
255
+ // Generate plus aliases
256
+ if (rule.supportsPlusAddressing && options.plusAliases) {
257
+ for (const alias of options.plusAliases) {
258
+ aliases.push(`${username}+${alias}@${domain}`);
259
+ }
260
+ }
261
+ // Generate dot variations (Gmail only)
262
+ if (rule.ignoresDots && options.includeDotVariations && username.length > 1) {
263
+ const maxVariations = Math.min(options.maxDotVariations ?? 5, username.length - 1);
264
+ const variations = new Set();
265
+ // Simple dot variations (insert dots between characters)
266
+ for (let i = 1; i < username.length && variations.size < maxVariations; i++) {
267
+ const withDot = username.slice(0, i) + '.' + username.slice(i);
268
+ variations.add(`${withDot}@${domain}`);
269
+ }
270
+ aliases.push(...Array.from(variations));
271
+ }
272
+ return aliases.length > 0 ? aliases : [email.toLowerCase()];
273
+ }
274
+ /**
275
+ * Analyzes email aliasing patterns in a list of emails
276
+ */
277
+ function analyzeEmailAliases(emails) {
278
+ const canonicalMap = new Map();
279
+ const providerStats = {};
280
+ for (const email of emails) {
281
+ try {
282
+ const result = detectEmailAlias(email);
283
+ const canonical = result.canonical;
284
+ if (!canonicalMap.has(canonical)) {
285
+ canonicalMap.set(canonical, []);
286
+ }
287
+ canonicalMap.get(canonical).push(email);
288
+ // Update provider stats
289
+ if (result.provider) {
290
+ if (!providerStats[result.provider]) {
291
+ providerStats[result.provider] = {
292
+ total: 0,
293
+ aliases: 0,
294
+ types: {}
295
+ };
296
+ }
297
+ providerStats[result.provider].total++;
298
+ if (result.isAlias) {
299
+ providerStats[result.provider].aliases++;
300
+ const type = result.aliasType;
301
+ providerStats[result.provider].types[type] = (providerStats[result.provider].types[type] || 0) + 1;
302
+ }
303
+ }
304
+ }
305
+ catch {
306
+ // Skip invalid emails
307
+ }
308
+ }
309
+ const aliasGroups = Array.from(canonicalMap.entries()).map(([canonical, aliases]) => ({
310
+ canonical,
311
+ aliases,
312
+ count: aliases.length
313
+ })).sort((a, b) => b.count - a.count);
314
+ return {
315
+ totalEmails: emails.length,
316
+ uniqueCanonical: canonicalMap.size,
317
+ aliasGroups,
318
+ providerStats
319
+ };
320
+ }
package/dist/index.d.ts CHANGED
@@ -347,6 +347,10 @@ export declare const RateLimit: {
347
347
  */
348
348
  getCurrentLimiter: () => SimpleRateLimiter;
349
349
  };
350
+ export * from './alias-detection';
351
+ export * from './security/url-validator';
352
+ export * from './security/hash-verifier';
353
+ export * from './security/secure-loader';
350
354
  declare const _default: {
351
355
  getEmailProviderLink: typeof getEmailProviderLink;
352
356
  getEmailProviderLinkWithDNS: typeof getEmailProviderLinkWithDNS;
package/dist/index.js CHANGED
@@ -11,6 +11,20 @@
11
11
  *
12
12
  * @packageDocumentation
13
13
  */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
26
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
27
+ };
14
28
  Object.defineProperty(exports, "__esModule", { value: true });
15
29
  exports.RateLimit = void 0;
16
30
  exports.isValidEmail = isValidEmail;
@@ -592,6 +606,12 @@ exports.RateLimit = {
592
606
  */
593
607
  getCurrentLimiter: () => dnsRateLimiter
594
608
  };
609
+ // Export alias detection module
610
+ __exportStar(require("./alias-detection"), exports);
611
+ // Export security modules
612
+ __exportStar(require("./security/url-validator"), exports);
613
+ __exportStar(require("./security/hash-verifier"), exports);
614
+ __exportStar(require("./security/secure-loader"), exports);
595
615
  // Default export for convenience
596
616
  exports.default = {
597
617
  getEmailProviderLink,
@@ -27,9 +27,9 @@ const path_1 = require("path");
27
27
  */
28
28
  const KNOWN_GOOD_HASHES = {
29
29
  // SHA-256 hash of the legitimate emailproviders.json
30
- 'emailproviders.json': '1c7925d02cc66b487046afdce81f744bd7a42f5a489e16e53a1a979575e4473b',
30
+ 'emailproviders.json': '41d2084580270f77ae2c2c5593995ebace7877a74f7d1c1e1bc7ed3b37611aa4',
31
31
  // You can add hashes for other critical files
32
- 'package.json': 'ba2810fd51f2d044890119e6f49926b80d822a4cafedaff25e3f91ba8ec65c2f'
32
+ 'package.json': 'c73f9f005cc4204d321cb1382f80354e932dffc38804600cdcf307ef11a0525e'
33
33
  };
34
34
  /**
35
35
  * Calculates SHA-256 hash of a file or string content
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mikkelscheike/email-provider-links",
3
- "version": "1.6.0",
4
- "description": "A TypeScript package providing direct links to 74 email providers (147+ domains) with enterprise security features, rate limiting, and comprehensive international coverage for login and password reset flows",
3
+ "version": "1.7.0",
4
+ "description": "A TypeScript package providing direct links to 93 email providers (180+ domains) with enterprise security features, email alias detection, rate limiting, and comprehensive international coverage for login and password reset flows",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [
@@ -30,6 +30,11 @@
30
30
  "gmail",
31
31
  "outlook",
32
32
  "yahoo",
33
+ "alias-detection",
34
+ "email-normalization",
35
+ "plus-addressing",
36
+ "fraud-prevention",
37
+ "deduplication",
33
38
  "typescript",
34
39
  "npm",
35
40
  "utility"
@@ -853,6 +853,161 @@
853
853
  "domains": [
854
854
  "zoho.eu"
855
855
  ]
856
+ },
857
+ {
858
+ "companyProvider": "Alibaba Mail",
859
+ "loginUrl": "https://mail.aliyun.com",
860
+ "domains": [
861
+ "aliyun.com",
862
+ "alibaba.com",
863
+ "taobao.com",
864
+ "tmall.com"
865
+ ],
866
+ "customDomainDetection": {
867
+ "mxPatterns": [
868
+ "mx1.qiye.aliyun.com",
869
+ "mx2.qiye.aliyun.com",
870
+ "mx3.qiye.aliyun.com"
871
+ ],
872
+ "txtPatterns": [
873
+ "v=spf1 include:spf.qiye.aliyun.com"
874
+ ]
875
+ }
876
+ },
877
+ {
878
+ "companyProvider": "Sify Mail",
879
+ "loginUrl": "https://mail.sify.com",
880
+ "domains": [
881
+ "sify.com",
882
+ "sifymail.com"
883
+ ]
884
+ },
885
+ {
886
+ "companyProvider": "IndiatTimes Mail",
887
+ "loginUrl": "https://mail.indiatimes.com",
888
+ "domains": [
889
+ "indiatimes.com"
890
+ ]
891
+ },
892
+ {
893
+ "companyProvider": "Virgilio Mail",
894
+ "loginUrl": "https://mail.virgilio.it",
895
+ "domains": [
896
+ "virgilio.it",
897
+ "alice.it",
898
+ "tin.it"
899
+ ]
900
+ },
901
+ {
902
+ "companyProvider": "Telekom Mail",
903
+ "loginUrl": "https://email.magenta.de",
904
+ "domains": [
905
+ "magenta.de",
906
+ "telekom.de"
907
+ ]
908
+ },
909
+ {
910
+ "companyProvider": "Tiscali Mail",
911
+ "loginUrl": "https://mail.tiscali.it",
912
+ "domains": [
913
+ "tiscali.it"
914
+ ]
915
+ },
916
+ {
917
+ "companyProvider": "Skynet Mail",
918
+ "loginUrl": "https://webmail.skynet.be",
919
+ "domains": [
920
+ "skynet.be"
921
+ ]
922
+ },
923
+ {
924
+ "companyProvider": "Telenet Mail",
925
+ "loginUrl": "https://webmail.telenet.be",
926
+ "domains": [
927
+ "telenet.be"
928
+ ]
929
+ },
930
+ {
931
+ "companyProvider": "Xs4All",
932
+ "loginUrl": "https://webmail.xs4all.nl",
933
+ "domains": [
934
+ "xs4all.nl"
935
+ ]
936
+ },
937
+ {
938
+ "companyProvider": "Planet.nl",
939
+ "loginUrl": "https://webmail.planet.nl",
940
+ "domains": [
941
+ "planet.nl"
942
+ ]
943
+ },
944
+ {
945
+ "companyProvider": "Bluewin Mail",
946
+ "loginUrl": "https://webmail.bluewin.ch",
947
+ "domains": [
948
+ "bluewin.ch"
949
+ ]
950
+ },
951
+ {
952
+ "companyProvider": "Eircom Mail",
953
+ "loginUrl": "https://webmail.eircom.net",
954
+ "domains": [
955
+ "eircom.net"
956
+ ]
957
+ },
958
+ {
959
+ "companyProvider": "Centrum Mail",
960
+ "loginUrl": "https://www.centrum.cz",
961
+ "domains": [
962
+ "centrum.cz",
963
+ "centrum.sk"
964
+ ]
965
+ },
966
+ {
967
+ "companyProvider": "Interia Mail",
968
+ "loginUrl": "https://poczta.interia.pl",
969
+ "domains": [
970
+ "interia.pl"
971
+ ]
972
+ },
973
+ {
974
+ "companyProvider": "Onet Mail",
975
+ "loginUrl": "https://poczta.onet.pl",
976
+ "domains": [
977
+ "onet.pl",
978
+ "poczta.onet.pl",
979
+ "op.pl"
980
+ ]
981
+ },
982
+ {
983
+ "companyProvider": "Rambler Mail",
984
+ "loginUrl": "https://mail.rambler.ru",
985
+ "domains": [
986
+ "rambler.ru",
987
+ "lenta.ru",
988
+ "autorambler.ru"
989
+ ]
990
+ },
991
+ {
992
+ "companyProvider": "Nate Mail",
993
+ "loginUrl": "https://mail.nate.com",
994
+ "domains": [
995
+ "nate.com"
996
+ ]
997
+ },
998
+ {
999
+ "companyProvider": "Hanmail",
1000
+ "loginUrl": "https://mail.hanmail.net",
1001
+ "domains": [
1002
+ "hanmail.net"
1003
+ ]
1004
+ },
1005
+ {
1006
+ "companyProvider": "Live.jp",
1007
+ "loginUrl": "https://login.live.com",
1008
+ "domains": [
1009
+ "live.jp"
1010
+ ]
856
1011
  }
857
1012
  ]
858
- }
1013
+ }