@mikkelscheike/email-provider-links 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,625 +2,94 @@
2
2
  /**
3
3
  * Email Provider Links
4
4
  *
5
- * A TypeScript package that provides direct links to email providers
6
- * based on email addresses to streamline login and password reset flows.
5
+ * A clean, modern email provider detection library with:
6
+ * - 93+ verified email providers covering 180+ domains
7
+ * - Concurrent DNS detection for business domains
8
+ * - Zero runtime dependencies
9
+ * - Comprehensive error handling
10
+ * - Email alias normalization
7
11
  *
8
- * The package uses a two-tier detection system:
9
- * 1. Fast domain lookup against a JSON database of known providers
10
- * 2. DNS-based detection for custom business domains using MX/TXT record analysis
11
- *
12
- * @packageDocumentation
12
+ * @author Email Provider Links Team
13
+ * @license MIT
13
14
  */
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
- };
28
15
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.RateLimit = void 0;
30
- exports.isValidEmail = isValidEmail;
31
- exports.extractDomain = extractDomain;
32
- exports.findEmailProvider = findEmailProvider;
33
- exports.getEmailProviderLink = getEmailProviderLink;
16
+ exports.DOMAIN_COUNT = exports.PROVIDER_COUNT = exports.detectProviderConcurrent = exports.loadProviders = exports.Config = exports.emailsMatch = exports.normalizeEmail = exports.getEmailProviderFast = exports.getEmailProviderSync = exports.getEmailProvider = void 0;
34
17
  exports.getSupportedProviders = getSupportedProviders;
35
18
  exports.isEmailProviderSupported = isEmailProviderSupported;
36
- exports.detectProviderByDNS = detectProviderByDNS;
37
- exports.getEmailProviderLinkWithDNS = getEmailProviderLinkWithDNS;
38
- const fs_1 = require("fs");
39
- const path_1 = require("path");
40
- const util_1 = require("util");
41
- const dns_1 = require("dns");
42
- // Convert Node.js callback-style DNS functions to Promise-based
43
- const resolveMxAsync = (0, util_1.promisify)(dns_1.resolveMx);
44
- const resolveTxtAsync = (0, util_1.promisify)(dns_1.resolveTxt);
45
- /**
46
- * Default timeout for DNS queries in milliseconds.
47
- */
48
- const DEFAULT_DNS_TIMEOUT = 5000; // 5 seconds
49
- /**
50
- * Rate limiting configuration for DNS queries.
51
- */
52
- const RATE_LIMIT_MAX_REQUESTS = 10; // Maximum requests per time window
53
- const RATE_LIMIT_WINDOW_MS = 60000; // Time window in milliseconds (1 minute)
54
- /**
55
- * Simple rate limiter to prevent excessive DNS queries.
56
- * Tracks request timestamps and enforces a maximum number of requests per time window.
57
- */
58
- class SimpleRateLimiter {
59
- constructor(maxRequests = RATE_LIMIT_MAX_REQUESTS, windowMs = RATE_LIMIT_WINDOW_MS) {
60
- this.maxRequests = maxRequests;
61
- this.windowMs = windowMs;
62
- this.requestTimestamps = [];
63
- }
64
- /**
65
- * Checks if a request is allowed under the current rate limit.
66
- * @returns true if request is allowed, false if rate limited
67
- */
68
- isAllowed() {
69
- const now = Date.now();
70
- // Remove old timestamps outside the current window
71
- this.requestTimestamps = this.requestTimestamps.filter(timestamp => now - timestamp < this.windowMs);
72
- // Check if we're under the limit
73
- if (this.requestTimestamps.length < this.maxRequests) {
74
- this.requestTimestamps.push(now);
75
- return true;
76
- }
77
- return false;
78
- }
79
- /**
80
- * Gets the current number of requests in the window.
81
- */
82
- getCurrentCount() {
83
- const now = Date.now();
84
- this.requestTimestamps = this.requestTimestamps.filter(timestamp => now - timestamp < this.windowMs);
85
- return this.requestTimestamps.length;
86
- }
87
- /**
88
- * Gets the time until the rate limit resets (when oldest request expires).
89
- * @returns milliseconds until reset, or 0 if not rate limited
90
- */
91
- getTimeUntilReset() {
92
- if (this.requestTimestamps.length === 0)
93
- return 0;
94
- const oldestTimestamp = Math.min(...this.requestTimestamps);
95
- const resetTime = oldestTimestamp + this.windowMs;
96
- const now = Date.now();
97
- return Math.max(0, resetTime - now);
98
- }
99
- }
100
- // Global rate limiter instance
101
- const dnsRateLimiter = new SimpleRateLimiter();
102
- /**
103
- * Creates a Promise that rejects after the specified timeout.
104
- *
105
- * @param ms - Timeout in milliseconds
106
- * @returns Promise that rejects with timeout error and a cleanup function
107
- * @internal
108
- */
109
- function createTimeout(ms) {
110
- let timeoutId;
111
- const promise = new Promise((_, reject) => {
112
- timeoutId = setTimeout(() => reject(new Error(`DNS query timeout after ${ms}ms`)), ms);
113
- });
114
- const cleanup = () => {
115
- if (timeoutId) {
116
- clearTimeout(timeoutId);
117
- }
118
- };
119
- return { promise, cleanup };
120
- }
121
- /**
122
- * Wraps a DNS query with a timeout.
123
- *
124
- * @param promise - The DNS query promise
125
- * @param timeoutMs - Timeout in milliseconds
126
- * @returns Promise that resolves with DNS result or rejects on timeout
127
- * @internal
128
- */
129
- function withTimeout(promise, timeoutMs) {
130
- const { promise: timeoutPromise, cleanup } = createTimeout(timeoutMs);
131
- return Promise.race([
132
- promise.finally(() => cleanup()), // Clean up timeout when original promise settles
133
- timeoutPromise
134
- ]);
135
- }
136
- // Load providers from external JSON file
137
- let EMAIL_PROVIDERS = [];
138
- // Performance optimization: Create a domain-to-provider Map for O(1) lookups
139
- let DOMAIN_TO_PROVIDER_MAP = new Map();
140
- /**
141
- * Builds a Map for fast domain-to-provider lookups.
142
- * This optimization improves lookup performance from O(n*m) to O(1)
143
- * where n = number of providers, m = average domains per provider.
144
- *
145
- * @internal
146
- */
147
- function buildDomainMap() {
148
- DOMAIN_TO_PROVIDER_MAP.clear();
149
- for (const provider of EMAIL_PROVIDERS) {
150
- for (const domain of provider.domains) {
151
- DOMAIN_TO_PROVIDER_MAP.set(domain.toLowerCase(), provider);
152
- }
153
- }
154
- }
155
- try {
156
- const providersPath = (0, path_1.join)(__dirname, '..', 'providers', 'emailproviders.json');
157
- const providersData = JSON.parse((0, fs_1.readFileSync)(providersPath, 'utf8'));
158
- EMAIL_PROVIDERS = providersData.providers;
159
- buildDomainMap(); // Build optimized lookup map
160
- }
161
- catch (error) {
162
- // Fallback to hardcoded providers if JSON file is not found
163
- console.warn('Could not load providers from JSON file, using fallback providers', error instanceof Error ? error.message : 'Unknown error');
164
- EMAIL_PROVIDERS = [
165
- {
166
- companyProvider: 'Google',
167
- loginUrl: 'https://accounts.google.com/signin',
168
- domains: ['gmail.com', 'googlemail.com']
169
- },
170
- {
171
- companyProvider: 'Microsoft',
172
- loginUrl: 'https://outlook.live.com/owa/',
173
- domains: ['outlook.com', 'hotmail.com', 'live.com', 'msn.com']
174
- },
175
- {
176
- companyProvider: 'Yahoo',
177
- loginUrl: 'https://login.yahoo.com/',
178
- domains: ['yahoo.com', 'yahoo.co.uk', 'yahoo.ca', 'yahoo.com.au', 'ymail.com', 'rocketmail.com']
179
- }
180
- ];
181
- buildDomainMap(); // Build optimized lookup map for fallback too
182
- }
183
- /**
184
- * Validates if a string is a valid email address using a basic regex pattern.
185
- *
186
- * @param email - The string to validate as an email address
187
- * @returns true if the string matches basic email format, false otherwise
188
- *
189
- * @example
190
- * ```typescript
191
- * isValidEmail('user@gmail.com'); // true
192
- * isValidEmail('invalid-email'); // false
193
- * isValidEmail('user@domain'); // false
194
- * ```
195
- *
196
- * @remarks
197
- * This uses a simple regex pattern that covers most common email formats.
198
- * It may not catch all edge cases defined in RFC 5322, but works for
199
- * standard email addresses.
200
- */
201
- function isValidEmail(email) {
202
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
203
- return emailRegex.test(email);
204
- }
205
- /**
206
- * Extracts the domain portion from an email address.
207
- *
208
- * @param email - The email address to extract the domain from
209
- * @returns The domain in lowercase, or null if the email is invalid
210
- *
211
- * @example
212
- * ```typescript
213
- * extractDomain('user@Gmail.com'); // 'gmail.com'
214
- * extractDomain('test@yahoo.co.uk'); // 'yahoo.co.uk'
215
- * extractDomain('invalid-email'); // null
216
- * ```
217
- *
218
- * @remarks
219
- * The domain is automatically normalized to lowercase for consistent matching.
220
- */
221
- function extractDomain(email) {
222
- if (!isValidEmail(email)) {
223
- return null;
224
- }
225
- const parts = email.split('@');
226
- return parts.length === 2 ? parts[1].toLowerCase() : null;
227
- }
228
- /**
229
- * Finds an email provider by matching the domain against the known providers database.
230
- * Uses an optimized Map for O(1) lookup performance.
231
- *
232
- * @param domain - The email domain to look up (e.g., 'gmail.com')
233
- * @returns The matching EmailProvider object, or null if not found
234
- *
235
- * @example
236
- * ```typescript
237
- * const provider = findEmailProvider('gmail.com');
238
- * console.log(provider?.companyProvider); // 'Gmail'
239
- * console.log(provider?.loginUrl); // 'https://mail.google.com/mail/'
240
- * ```
241
- *
242
- * @remarks
243
- * This function performs a case-insensitive O(1) lookup using a pre-built Map.
244
- * It only checks the predefined JSON database, not DNS records.
245
- * Performance optimized from O(n*m) to O(1) where n=providers, m=domains per provider.
246
- */
247
- function findEmailProvider(domain) {
248
- const normalizedDomain = domain.toLowerCase();
249
- return DOMAIN_TO_PROVIDER_MAP.get(normalizedDomain) || null;
250
- }
251
- /**
252
- * Gets email provider information and login URL for a given email address.
253
- * This is the basic/synchronous version that only checks predefined domains.
254
- *
255
- * @param email - The email address to analyze
256
- * @returns EmailProviderResult containing provider info and login URL
257
- *
258
- * @example
259
- * ```typescript
260
- * const result = getEmailProviderLink('user@gmail.com');
261
- * console.log(result.loginUrl); // 'https://mail.google.com/mail/'
262
- * console.log(result.provider?.companyProvider); // 'Gmail'
263
- * ```
264
- *
265
- * @remarks
266
- * This function only checks against the predefined JSON database of known domains.
267
- * It will NOT detect business domains that use major email providers (e.g.,
268
- * mycompany.com using Google Workspace). For comprehensive detection including
269
- * business domains, use {@link getEmailProviderLinkWithDNS} instead.
270
- *
271
- * **Limitations:**
272
- * - Only synchronous operation (no DNS lookups)
273
- * - Limited to domains in the JSON database
274
- * - Won't detect custom business domains
275
- * - No proxy service detection
276
- */
277
- function getEmailProviderLink(email) {
278
- const domain = extractDomain(email);
279
- if (!domain) {
280
- return {
281
- provider: null,
282
- email,
283
- loginUrl: null
284
- };
285
- }
286
- const provider = findEmailProvider(domain);
287
- return {
288
- provider,
289
- email,
290
- loginUrl: provider ? provider.loginUrl : null
291
- };
292
- }
293
- /**
294
- * Returns an array of all supported email providers.
295
- *
296
- * @returns A copy of the EMAIL_PROVIDERS array
297
- *
298
- * @example
299
- * ```typescript
300
- * const providers = getSupportedProviders();
301
- * console.log(providers.length); // 55+
302
- * console.log(providers[0].companyProvider); // 'Gmail'
303
- * ```
304
- *
305
- * @remarks
306
- * Returns a shallow copy to prevent external modification of the internal
307
- * providers array. The returned array includes both consumer email providers
308
- * (gmail.com, yahoo.com) and business email providers with DNS detection patterns.
19
+ exports.extractDomain = extractDomain;
20
+ exports.isValidEmail = isValidEmail;
21
+ // ===== PRIMARY API =====
22
+ // These are the functions 95% of users need
23
+ var api_1 = require("./api");
24
+ Object.defineProperty(exports, "getEmailProvider", { enumerable: true, get: function () { return api_1.getEmailProvider; } });
25
+ Object.defineProperty(exports, "getEmailProviderSync", { enumerable: true, get: function () { return api_1.getEmailProviderSync; } });
26
+ Object.defineProperty(exports, "getEmailProviderFast", { enumerable: true, get: function () { return api_1.getEmailProviderFast; } });
27
+ Object.defineProperty(exports, "normalizeEmail", { enumerable: true, get: function () { return api_1.normalizeEmail; } });
28
+ Object.defineProperty(exports, "emailsMatch", { enumerable: true, get: function () { return api_1.emailsMatch; } });
29
+ Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return api_1.Config; } });
30
+ // ===== ADVANCED FEATURES =====
31
+ // Export utility functions for advanced use cases
32
+ var loader_1 = require("./loader");
33
+ Object.defineProperty(exports, "loadProviders", { enumerable: true, get: function () { return loader_1.loadProviders; } });
34
+ var concurrent_dns_1 = require("./concurrent-dns");
35
+ Object.defineProperty(exports, "detectProviderConcurrent", { enumerable: true, get: function () { return concurrent_dns_1.detectProviderConcurrent; } });
36
+ // ===== UTILITY FUNCTIONS =====
37
+ // Helper functions for common tasks
38
+ const loader_2 = require("./loader");
39
+ const api_2 = require("./api");
40
+ /**
41
+ * Get list of all supported email providers
42
+ * @returns Array of all email providers in the database
309
43
  */
310
44
  function getSupportedProviders() {
311
- return [...EMAIL_PROVIDERS];
45
+ const { providers } = (0, loader_2.loadProviders)();
46
+ return [...providers]; // Return a copy to prevent external mutations
312
47
  }
313
48
  /**
314
- * Checks if an email address uses a supported email provider.
315
- *
316
- * @param email - The email address to check
317
- * @returns true if the provider is supported, false otherwise
318
- *
319
- * @example
320
- * ```typescript
321
- * isEmailProviderSupported('user@gmail.com'); // true
322
- * isEmailProviderSupported('user@unknown.com'); // false
323
- * ```
324
- *
325
- * @remarks
326
- * This is a convenience function that uses {@link getEmailProviderLink} internally.
327
- * It only checks predefined domains, not DNS-based detection. For business
328
- * domain support checking, you would need to use {@link getEmailProviderLinkWithDNS}.
49
+ * Check if an email provider is supported
50
+ * @param email - Email address to check
51
+ * @returns true if the provider is supported
329
52
  */
330
53
  function isEmailProviderSupported(email) {
331
- const result = getEmailProviderLink(email);
54
+ const result = (0, api_2.getEmailProviderSync)(email);
332
55
  return result.provider !== null;
333
56
  }
334
57
  /**
335
- * Detects proxy services that obscure the actual email provider by analyzing MX records.
336
- *
337
- * @param mxRecords - Array of MX records from DNS lookup
338
- * @returns The name of the detected proxy service, or null if none detected
339
- *
340
- * @internal
341
- * @remarks
342
- * This function checks MX record patterns against known proxy/CDN services.
343
- * When a proxy is detected, it means we cannot determine the actual email
344
- * provider behind the proxy service. Currently detects:
345
- * - Cloudflare Email Routing
346
- * - CloudFront (AWS)
347
- * - Fastly
348
- * - MaxCDN
349
- * - KeyCDN
350
- * - Mailgun proxy configurations
351
- * - SendGrid proxy configurations
352
- *
353
- * @example
354
- * ```typescript
355
- * const mxRecords = [{ exchange: 'isaac.mx.cloudflare.net', priority: 10 }];
356
- * const proxy = detectProxyService(mxRecords);
357
- * console.log(proxy); // 'Cloudflare'
358
- * ```
58
+ * Extract domain from email address
59
+ * @param email - Email address
60
+ * @returns Domain portion or null if invalid
359
61
  */
360
- function detectProxyService(mxRecords) {
361
- const proxyPatterns = [
362
- { service: 'Cloudflare', patterns: ['mxrecord.io', 'mxrecord.mx', 'cloudflare'] },
363
- { service: 'CloudFront', patterns: ['cloudfront.net'] },
364
- { service: 'Fastly', patterns: ['fastly.com'] },
365
- { service: 'MaxCDN', patterns: ['maxcdn.com'] },
366
- { service: 'KeyCDN', patterns: ['keycdn.com'] },
367
- { service: 'Mailgun Proxy', patterns: ['mailgun.org', 'mg.', 'mailgun'] },
368
- { service: 'SendGrid Proxy', patterns: ['sendgrid.net', 'sendgrid.com'] }
369
- ];
370
- for (const mxRecord of mxRecords) {
371
- const exchange = mxRecord.exchange.toLowerCase();
372
- for (const proxyService of proxyPatterns) {
373
- for (const pattern of proxyService.patterns) {
374
- if (exchange.includes(pattern.toLowerCase())) {
375
- return proxyService.service;
376
- }
377
- }
378
- }
62
+ function extractDomain(email) {
63
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
64
+ if (!emailRegex.test(email)) {
65
+ return null;
379
66
  }
380
- return null;
67
+ return email.split('@')[1]?.toLowerCase() || null;
381
68
  }
382
69
  /**
383
- * Performs DNS-based detection for custom business domains using MX and TXT record analysis.
384
- * This function is used internally by {@link getEmailProviderLinkWithDNS} but can also be
385
- * called directly to analyze domain email configuration.
386
- *
387
- * @param domain - The domain to analyze (e.g., 'mycompany.com')
388
- * @param timeoutMs - Optional timeout for DNS queries in milliseconds (default: 5000ms)
389
- * @returns Promise resolving to DNSDetectionResult with provider info or proxy detection
390
- *
391
- * @example
392
- * ```typescript
393
- * const result = await detectProviderByDNS('microsoft.com');
394
- * console.log(result.provider?.companyProvider); // 'Microsoft 365 (Business)'
395
- * console.log(result.detectionMethod); // 'mx_record'
396
- * ```
397
- *
398
- * @remarks
399
- * **Detection Algorithm:**
400
- * 1. Checks rate limiting (max 10 requests per minute)
401
- * 2. Performs MX record lookup for the domain
402
- * 3. Checks if MX records match known proxy services (Cloudflare, etc.)
403
- * 4. If proxy detected, returns null provider with proxy info
404
- * 5. Otherwise, matches MX records against business email provider patterns
405
- * 6. If no MX match, falls back to TXT record analysis (SPF records, etc.)
406
- * 7. Returns the first matching provider or null if none found
407
- *
408
- * **Rate Limiting:**
409
- * - Maximum 10 DNS requests per 60-second window
410
- * - Rate limit exceeded returns error with retry information
411
- * - Prevents abuse and excessive DNS queries
412
- *
413
- * **Provider Patterns Checked:**
414
- * - Google Workspace: aspmx.l.google.com, aspmx2.googlemail.com, etc.
415
- * - Microsoft 365: *.protection.outlook.com, *.outlook.com
416
- * - ProtonMail: mail.protonmail.ch, mailsec.protonmail.ch
417
- * - FastMail: *.messagingengine.com
418
- * - And many others...
419
- *
420
- * **Error Handling:**
421
- * DNS lookup failures are caught and the function gracefully falls back
422
- * to the next detection method or returns null if all methods fail.
70
+ * Validate email format
71
+ * @param email - Email address to validate
72
+ * @returns true if valid format
423
73
  */
424
- async function detectProviderByDNS(domain, timeoutMs = DEFAULT_DNS_TIMEOUT) {
425
- const normalizedDomain = domain.toLowerCase();
426
- // Check rate limiting
427
- if (!dnsRateLimiter.isAllowed()) {
428
- const retryAfter = Math.ceil(dnsRateLimiter.getTimeUntilReset() / 1000);
429
- throw new Error(`Rate limit exceeded. DNS queries limited to ${RATE_LIMIT_MAX_REQUESTS} requests per minute. Try again in ${retryAfter} seconds.`);
430
- }
431
- // Get providers that support custom domain detection
432
- const customDomainProviders = EMAIL_PROVIDERS.filter(provider => provider.customDomainDetection &&
433
- (provider.customDomainDetection.mxPatterns || provider.customDomainDetection.txtPatterns));
434
- // Try MX record detection first with timeout
435
- try {
436
- const mxRecords = await withTimeout(resolveMxAsync(normalizedDomain), timeoutMs);
437
- // Check for proxy services first
438
- const proxyService = detectProxyService(mxRecords);
439
- if (proxyService) {
440
- return {
441
- provider: null,
442
- detectionMethod: 'proxy_detected',
443
- proxyService
444
- };
445
- }
446
- for (const provider of customDomainProviders) {
447
- if (provider.customDomainDetection?.mxPatterns) {
448
- for (const mxRecord of mxRecords) {
449
- const exchange = mxRecord.exchange.toLowerCase();
450
- for (const pattern of provider.customDomainDetection.mxPatterns) {
451
- if (exchange.includes(pattern.toLowerCase())) {
452
- return {
453
- provider,
454
- detectionMethod: 'mx_record'
455
- };
456
- }
457
- }
458
- }
459
- }
460
- }
461
- }
462
- catch (error) {
463
- // MX lookup failed, continue to TXT records
464
- }
465
- // Try TXT record detection with timeout
466
- try {
467
- const txtRecords = await withTimeout(resolveTxtAsync(normalizedDomain), timeoutMs);
468
- const flatTxtRecords = txtRecords.flat();
469
- for (const provider of customDomainProviders) {
470
- if (provider.customDomainDetection?.txtPatterns) {
471
- for (const txtRecord of flatTxtRecords) {
472
- const record = txtRecord.toLowerCase();
473
- for (const pattern of provider.customDomainDetection.txtPatterns) {
474
- if (record.includes(pattern.toLowerCase())) {
475
- return {
476
- provider,
477
- detectionMethod: 'txt_record'
478
- };
479
- }
480
- }
481
- }
482
- }
483
- }
484
- }
485
- catch (error) {
486
- // TXT lookup failed
487
- }
488
- return {
489
- provider: null,
490
- detectionMethod: null
491
- };
74
+ function isValidEmail(email) {
75
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
76
+ return emailRegex.test(email);
492
77
  }
493
78
  /**
494
- * Enhanced email provider detection with automatic DNS-based custom domain detection.
495
- * This is the recommended function for most use cases as it provides comprehensive
496
- * detection coverage including business domains and proxy services.
497
- *
498
- * @param email - The email address to analyze
499
- * @param timeoutMs - Optional timeout for DNS queries in milliseconds (default: 5000ms)
500
- * @returns Promise resolving to EmailProviderResult with provider info and detection method
501
- *
502
- * @example
503
- * ```typescript
504
- * // Consumer email (fast domain match)
505
- * const gmail = await getEmailProviderLinkWithDNS('user@gmail.com');
506
- * console.log(gmail.provider?.companyProvider); // 'Gmail'
507
- * console.log(gmail.detectionMethod); // 'domain_match'
508
- *
509
- * // Business domain (DNS detection)
510
- * const business = await getEmailProviderLinkWithDNS('user@mycompany.com');
511
- * console.log(business.provider?.companyProvider); // 'Google Workspace' (if detected)
512
- * console.log(business.detectionMethod); // 'mx_record'
513
- *
514
- * // Proxied domain (proxy detection)
515
- * const proxied = await getEmailProviderLinkWithDNS('user@proxied-domain.com');
516
- * console.log(proxied.provider); // null
517
- * console.log(proxied.proxyService); // 'Cloudflare'
518
- * console.log(proxied.detectionMethod); // 'proxy_detected'
519
- * ```
520
- *
521
- * @remarks
522
- * **Detection Hierarchy (in order):**
523
- * 1. **Domain Match**: Fast lookup against predefined domains (gmail.com, yahoo.com, etc.)
524
- * 2. **DNS MX Records**: Analyzes mail exchange records for business email providers
525
- * 3. **DNS TXT Records**: Checks SPF and verification records as fallback
526
- * 4. **Proxy Detection**: Identifies when domains are behind CDN/proxy services
527
- *
528
- * **Supported Detection Cases:**
529
- * - ✅ Consumer emails: gmail.com, yahoo.com, outlook.com, etc.
530
- * - ✅ Business domains: Google Workspace, Microsoft 365, ProtonMail Business, etc.
531
- * - ✅ Proxy services: Cloudflare, CloudFront, Fastly, etc.
532
- * - ✅ International providers: QQ Mail, NetEase, Yandex, etc.
533
- *
534
- * **Performance:**
535
- * - Fast for known consumer domains (synchronous JSON lookup)
536
- * - Additional DNS lookup time for unknown domains (~100-500ms)
537
- * - Graceful degradation if DNS lookups fail
538
- *
539
- * **Error Handling:**
540
- * - Invalid email addresses return null provider
541
- * - DNS lookup failures are caught and don't throw errors
542
- * - Network timeouts gracefully fall back to null detection
543
- *
544
- * **Use Cases:**
545
- * - Password reset flows ("Check your Gmail inbox")
546
- * - Login form enhancements (direct links to email providers)
547
- * - Email client detection for support purposes
548
- * - Business domain analysis for enterprise features
79
+ * Library metadata
549
80
  */
550
- async function getEmailProviderLinkWithDNS(email, timeoutMs = DEFAULT_DNS_TIMEOUT) {
551
- const domain = extractDomain(email);
552
- if (!domain) {
553
- return {
554
- provider: null,
555
- email,
556
- loginUrl: null
557
- };
558
- }
559
- // First try standard domain matching
560
- const provider = findEmailProvider(domain);
561
- if (provider) {
562
- return {
563
- provider,
564
- email,
565
- loginUrl: provider.loginUrl,
566
- detectionMethod: 'domain_match'
567
- };
568
- }
569
- // If no direct match, try DNS-based detection for custom domains
570
- const dnsResult = await detectProviderByDNS(domain, timeoutMs);
571
- if (dnsResult.provider) {
572
- return {
573
- provider: dnsResult.provider,
574
- email,
575
- loginUrl: dnsResult.provider.loginUrl,
576
- detectionMethod: dnsResult.detectionMethod || 'mx_record'
577
- };
578
- }
579
- // Handle proxy detection case
580
- if (dnsResult.detectionMethod === 'proxy_detected') {
581
- return {
582
- provider: null,
583
- email,
584
- loginUrl: null,
585
- detectionMethod: 'proxy_detected',
586
- proxyService: dnsResult.proxyService
587
- };
588
- }
589
- return {
590
- provider: null,
591
- email,
592
- loginUrl: null
593
- };
594
- }
81
+ exports.PROVIDER_COUNT = 93;
82
+ exports.DOMAIN_COUNT = 178;
595
83
  /**
596
- * Rate limiting configuration constants and utilities.
597
- * @public
84
+ * Default export for convenience
598
85
  */
599
- exports.RateLimit = {
600
- MAX_REQUESTS: RATE_LIMIT_MAX_REQUESTS,
601
- WINDOW_MS: RATE_LIMIT_WINDOW_MS,
602
- SimpleRateLimiter,
603
- /**
604
- * Gets the current rate limiter instance for inspection or testing.
605
- * @internal
606
- */
607
- getCurrentLimiter: () => dnsRateLimiter
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);
615
- // Default export for convenience
616
86
  exports.default = {
617
- getEmailProviderLink,
618
- getEmailProviderLinkWithDNS,
619
- detectProviderByDNS,
620
- isValidEmail,
621
- extractDomain,
622
- findEmailProvider,
623
- getSupportedProviders,
624
- isEmailProviderSupported,
625
- RateLimit: exports.RateLimit
87
+ getEmailProvider: api_2.getEmailProvider,
88
+ getEmailProviderSync: api_2.getEmailProviderSync,
89
+ getEmailProviderFast: api_2.getEmailProviderFast,
90
+ normalizeEmail: api_2.normalizeEmail,
91
+ emailsMatch: api_2.emailsMatch,
92
+ Config: api_2.Config,
93
+ PROVIDER_COUNT: exports.PROVIDER_COUNT,
94
+ DOMAIN_COUNT: exports.DOMAIN_COUNT
626
95
  };