@mikkelscheike/email-provider-links 4.0.10 → 5.0.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
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/%40mikkelscheike%2Femail-provider-links)](https://www.npmjs.com/package/@mikkelscheike/email-provider-links)
4
4
 
5
- > **Generate direct login links for any email address across 93+ providers (Gmail, Outlook, Yahoo, etc.) to streamline user authentication flows.**
5
+ > **Generate direct login links for any email address across 130+ providers (Gmail, Outlook, Yahoo, etc.) to streamline user authentication flows.**
6
6
 
7
- 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.
7
+ A robust TypeScript library providing direct links to **130 email providers** (218 domains) with **concurrent DNS resolution**, **optimized performance**, **comprehensive email validation**, and advanced security features for login and password reset flows.
8
8
 
9
9
  ## 🚀 Try it out
10
10
 
@@ -26,8 +26,8 @@ A robust TypeScript library providing direct links to **93 email providers** (18
26
26
  ## ✨ Core Features
27
27
 
28
28
  - 🚀 **Fast & Lightweight**: Zero dependencies, ultra-low memory (0.10MB initial, 0.00004MB per 1000 ops), small footprint (~39.5KB compressed)
29
- - 📧 **93 Email Providers**: Gmail, Outlook, Yahoo, ProtonMail, iCloud, and many more
30
- - 🌐 **207 Domains Supported**: Comprehensive international coverage
29
+ - 📧 **130 Email Providers**: Gmail, Outlook, Yahoo, ProtonMail, iCloud, and many more
30
+ - 🌐 **218 Domains Supported**: Comprehensive international coverage
31
31
  - 🌍 **Full IDN Support**: International domain names with RFC compliance and Punycode
32
32
  - ✅ **Advanced Email Validation**: International email validation with detailed error reporting
33
33
  - 🏢 **Business Domain Detection**: DNS-based detection for custom domains (Google Workspace, Microsoft 365, etc.)
@@ -66,7 +66,7 @@ Fully compatible with the latest Node.js 24.x and 25.x! The library is tested on
66
66
 
67
67
  ## Supported Providers
68
68
 
69
- **📊 Current Coverage: 93 providers supporting 207 domains**
69
+ **📊 Current Coverage: 130 providers supporting 218 domains**
70
70
 
71
71
  **Consumer Email Providers:**
72
72
  - **Gmail** (2 domains): gmail.com, googlemail.com
@@ -102,6 +102,10 @@ Fully compatible with the latest Node.js 24.x and 25.x! The library is tested on
102
102
  #### `getEmailProvider(email, timeout?)`
103
103
  **Recommended** - Complete provider detection with business domain support.
104
104
 
105
+ Error notes:
106
+ - `INVALID_EMAIL` is returned for common malformed inputs (e.g. missing `@`, missing TLD).
107
+ - `IDN_VALIDATION_ERROR` is reserved for true encoding issues.
108
+
105
109
  ```typescript
106
110
  // Known providers (instant response)
107
111
  const result1 = await getEmailProvider('user@gmail.com');
@@ -286,6 +290,20 @@ npm run benchmark:dns
286
290
  # and can be modified for custom performance testing
287
291
  ```
288
292
 
293
+ ### Live DNS verification (optional)
294
+
295
+ There is an optional test suite that performs real DNS lookups for all domains in `providers/emailproviders.json`:
296
+
297
+ ```bash
298
+ RUN_LIVE_DNS=1 npm test -- __tests__/provider-live-dns.test.ts
299
+ ```
300
+
301
+ Optional strict mode (also validates configured MX/TXT patterns):
302
+
303
+ ```bash
304
+ RUN_LIVE_DNS=1 RUN_LIVE_DNS_STRICT=1 npm test -- __tests__/provider-live-dns.test.ts
305
+ ```
306
+
289
307
  ## Contributing
290
308
 
291
309
  We welcome contributions! See [CONTRIBUTING.md](docs/CONTRIBUTING.md) for guidelines on adding new email providers.
@@ -47,6 +47,7 @@ exports.detectEmailAlias = detectEmailAlias;
47
47
  exports.normalizeEmail = normalizeEmail;
48
48
  exports.emailsMatch = emailsMatch;
49
49
  const loader_1 = require("./loader");
50
+ const idn_1 = require("./idn");
50
51
  /**
51
52
  * Validates email format
52
53
  */
@@ -79,14 +80,14 @@ function isValidEmail(email) {
79
80
  * ```
80
81
  */
81
82
  function detectEmailAlias(email) {
82
- if (!isValidEmail(email)) {
83
+ const originalEmail = email.trim();
84
+ if (!isValidEmail(originalEmail)) {
83
85
  throw new Error('Invalid email format');
84
86
  }
85
- const originalEmail = email.trim();
86
87
  // Split normally, lowering case both for username and domain by default
87
88
  const emailParts = originalEmail.toLowerCase().split('@');
88
89
  const username = emailParts[0];
89
- const domain = emailParts[1]; // domain is always case-insensitive per RFC 5321
90
+ const domain = (0, idn_1.domainToPunycode)(emailParts[1] || ''); // domain is always case-insensitive per RFC 5321
90
91
  if (!username || !domain) {
91
92
  throw new Error('Invalid email format - missing username or domain');
92
93
  }
package/dist/api.d.ts CHANGED
@@ -176,7 +176,7 @@ export declare function getEmailProviderFast(email: string, options?: {
176
176
  total: number;
177
177
  };
178
178
  confidence?: number;
179
- debug?: any;
179
+ debug?: unknown;
180
180
  }>;
181
181
  /**
182
182
  * Configuration constants
@@ -184,7 +184,7 @@ export declare function getEmailProviderFast(email: string, options?: {
184
184
  export declare const Config: {
185
185
  readonly DEFAULT_DNS_TIMEOUT: 5000;
186
186
  readonly MAX_DNS_REQUESTS_PER_MINUTE: 10;
187
- readonly SUPPORTED_PROVIDERS_COUNT: 93;
188
- readonly SUPPORTED_DOMAINS_COUNT: 180;
187
+ readonly SUPPORTED_PROVIDERS_COUNT: 130;
188
+ readonly SUPPORTED_DOMAINS_COUNT: 218;
189
189
  };
190
190
  //# sourceMappingURL=api.d.ts.map
package/dist/api.js CHANGED
@@ -14,6 +14,74 @@ exports.emailsMatch = emailsMatch;
14
14
  exports.getEmailProviderFast = getEmailProviderFast;
15
15
  const concurrent_dns_1 = require("./concurrent-dns");
16
16
  const provider_loader_1 = require("./provider-loader");
17
+ const idn_1 = require("./idn");
18
+ let cachedProvidersRef = null;
19
+ let cachedDomainMap = null;
20
+ function getDomainMapFromProviders(providers) {
21
+ if (cachedProvidersRef === providers && cachedDomainMap) {
22
+ return cachedDomainMap;
23
+ }
24
+ const domainMap = new Map();
25
+ for (const loadedProvider of providers) {
26
+ for (const domain of loadedProvider.domains) {
27
+ domainMap.set(domain.toLowerCase(), loadedProvider);
28
+ }
29
+ }
30
+ cachedProvidersRef = providers;
31
+ cachedDomainMap = domainMap;
32
+ return domainMap;
33
+ }
34
+ function validateAndParseEmailForLookup(email) {
35
+ if (!email || typeof email !== 'string') {
36
+ return {
37
+ ok: false,
38
+ email: email || '',
39
+ error: {
40
+ type: 'INVALID_EMAIL',
41
+ message: 'Email address is required and must be a string'
42
+ }
43
+ };
44
+ }
45
+ const trimmedEmail = email.trim();
46
+ // Strict validation: treat any IDN validation failure as invalid input.
47
+ // Only surface IDN_VALIDATION_ERROR for true encoding issues.
48
+ const idnError = (0, idn_1.validateInternationalEmail)(trimmedEmail);
49
+ if (idnError) {
50
+ if (idnError.code === idn_1.IDNValidationError.INVALID_ENCODING) {
51
+ return {
52
+ ok: false,
53
+ email: trimmedEmail,
54
+ error: {
55
+ type: 'IDN_VALIDATION_ERROR',
56
+ message: idnError.message,
57
+ idnError: idnError.code
58
+ }
59
+ };
60
+ }
61
+ return {
62
+ ok: false,
63
+ email: trimmedEmail,
64
+ error: {
65
+ type: 'INVALID_EMAIL',
66
+ message: 'Invalid email format'
67
+ }
68
+ };
69
+ }
70
+ const atIndex = trimmedEmail.lastIndexOf('@');
71
+ if (atIndex === -1) {
72
+ return {
73
+ ok: false,
74
+ email: trimmedEmail,
75
+ error: {
76
+ type: 'INVALID_EMAIL',
77
+ message: 'Invalid email format'
78
+ }
79
+ };
80
+ }
81
+ const domainRaw = trimmedEmail.slice(atIndex + 1).toLowerCase();
82
+ const domain = (0, idn_1.domainToPunycode)(domainRaw);
83
+ return { ok: true, trimmedEmail, domain };
84
+ }
17
85
  /**
18
86
  * Get email provider information for any email address.
19
87
  *
@@ -46,43 +114,16 @@ const provider_loader_1 = require("./provider-loader");
46
114
  */
47
115
  async function getEmailProvider(email, timeout) {
48
116
  try {
49
- // Input validation
50
- if (!email || typeof email !== 'string') {
117
+ const parsed = validateAndParseEmailForLookup(email);
118
+ if (!parsed.ok) {
51
119
  return {
52
120
  provider: null,
53
- email: email || '',
121
+ email: parsed.email,
54
122
  loginUrl: null,
55
- error: {
56
- type: 'INVALID_EMAIL',
57
- message: 'Email address is required and must be a string'
58
- }
59
- };
60
- }
61
- // Basic email format validation
62
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
63
- if (!emailRegex.test(email)) {
64
- return {
65
- provider: null,
66
- email,
67
- loginUrl: null,
68
- error: {
69
- type: 'INVALID_EMAIL',
70
- message: 'Invalid email format'
71
- }
72
- };
73
- }
74
- const domain = email.split('@')[1]?.toLowerCase();
75
- if (!domain) {
76
- return {
77
- provider: null,
78
- email,
79
- loginUrl: null,
80
- error: {
81
- type: 'INVALID_EMAIL',
82
- message: 'Invalid email format - missing domain'
83
- }
123
+ error: parsed.error
84
124
  };
85
125
  }
126
+ const domain = parsed.domain;
86
127
  // First try synchronous domain matching
87
128
  const syncResult = getEmailProviderSync(email);
88
129
  if (syncResult.provider) {
@@ -130,9 +171,9 @@ async function getEmailProvider(email, timeout) {
130
171
  }
131
172
  catch (error) {
132
173
  // Enhanced error handling
133
- if (error.message?.includes('Rate limit exceeded')) {
174
+ if (error instanceof Error && error.message.includes('Rate limit exceeded')) {
134
175
  const retryMatch = error.message.match(/Try again in (\d+) seconds/);
135
- const retryAfter = retryMatch ? parseInt(retryMatch[1], 10) : undefined;
176
+ const retryAfter = retryMatch?.[1] ? parseInt(retryMatch[1], 10) : undefined;
136
177
  return {
137
178
  provider: null,
138
179
  email,
@@ -144,7 +185,7 @@ async function getEmailProvider(email, timeout) {
144
185
  }
145
186
  };
146
187
  }
147
- if (error.message?.includes('timeout')) {
188
+ if (error instanceof Error && error.message.includes('timeout')) {
148
189
  return {
149
190
  provider: null,
150
191
  email,
@@ -161,7 +202,7 @@ async function getEmailProvider(email, timeout) {
161
202
  loginUrl: null,
162
203
  error: {
163
204
  type: 'NETWORK_ERROR',
164
- message: error.message || 'Unknown network error'
205
+ message: error instanceof Error ? error.message : 'Unknown network error'
165
206
  }
166
207
  };
167
208
  }
@@ -189,44 +230,16 @@ async function getEmailProvider(email, timeout) {
189
230
  */
190
231
  function getEmailProviderSync(email) {
191
232
  try {
192
- // Input validation
193
- if (!email || typeof email !== 'string') {
194
- return {
195
- provider: null,
196
- email: email || '',
197
- loginUrl: null,
198
- error: {
199
- type: 'INVALID_EMAIL',
200
- message: 'Email address is required and must be a string'
201
- }
202
- };
203
- }
204
- // Basic email format validation
205
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
206
- if (!emailRegex.test(email)) {
233
+ const parsed = validateAndParseEmailForLookup(email);
234
+ if (!parsed.ok) {
207
235
  return {
208
236
  provider: null,
209
- email,
237
+ email: parsed.email,
210
238
  loginUrl: null,
211
- error: {
212
- type: 'INVALID_EMAIL',
213
- message: 'Invalid email format'
214
- }
215
- };
216
- }
217
- // Pure synchronous domain matching
218
- const domain = email.split('@')[1]?.toLowerCase();
219
- if (!domain) {
220
- return {
221
- provider: null,
222
- email,
223
- loginUrl: null,
224
- error: {
225
- type: 'INVALID_EMAIL',
226
- message: 'Invalid email format - missing domain'
227
- }
239
+ error: parsed.error
228
240
  };
229
241
  }
242
+ const domain = parsed.domain;
230
243
  // Load providers with verification
231
244
  let provider = null;
232
245
  try {
@@ -246,13 +259,7 @@ function getEmailProviderSync(email) {
246
259
  }
247
260
  };
248
261
  }
249
- const domainMap = new Map();
250
- // Build domain map from loaded providers
251
- for (const loadedProvider of result.providers) {
252
- for (const domain of loadedProvider.domains) {
253
- domainMap.set(domain.toLowerCase(), loadedProvider);
254
- }
255
- }
262
+ const domainMap = getDomainMapFromProviders(result.providers);
256
263
  provider = domainMap.get(domain) || null;
257
264
  }
258
265
  catch (error) {
@@ -291,7 +298,7 @@ function getEmailProviderSync(email) {
291
298
  loginUrl: null,
292
299
  error: {
293
300
  type: 'INVALID_EMAIL',
294
- message: error.message || 'Invalid email address'
301
+ message: error instanceof Error ? error.message : 'Invalid email address'
295
302
  }
296
303
  };
297
304
  }
@@ -327,7 +334,7 @@ function normalizeEmail(email) {
327
334
  return lowercaseEmail;
328
335
  }
329
336
  let localPart = lowercaseEmail.slice(0, atIndex);
330
- const domainPart = lowercaseEmail.slice(atIndex + 1);
337
+ const domainPart = (0, idn_1.domainToPunycode)(lowercaseEmail.slice(atIndex + 1));
331
338
  // Use providers for domain lookup
332
339
  let provider = null;
333
340
  try {
@@ -335,12 +342,7 @@ function normalizeEmail(email) {
335
342
  if (!result.success) {
336
343
  return lowercaseEmail; // Return as-is if providers can't be loaded
337
344
  }
338
- const domainMap = new Map();
339
- for (const loadedProvider of result.providers) {
340
- for (const domain of loadedProvider.domains) {
341
- domainMap.set(domain.toLowerCase(), loadedProvider);
342
- }
343
- }
345
+ const domainMap = getDomainMapFromProviders(result.providers);
344
346
  provider = domainMap.get(domainPart) || null;
345
347
  }
346
348
  catch (error) {
@@ -414,45 +416,19 @@ function emailsMatch(email1, email2) {
414
416
  async function getEmailProviderFast(email, options = {}) {
415
417
  const { timeout = 5000, enableParallel = true, collectDebugInfo = false } = options;
416
418
  try {
417
- // Input validation
418
- if (!email || typeof email !== 'string') {
419
- return {
420
- provider: null,
421
- email: email || '',
422
- loginUrl: null,
423
- error: {
424
- type: 'INVALID_EMAIL',
425
- message: 'Email address is required and must be a string'
426
- }
427
- };
428
- }
429
- // Basic email format validation
430
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
431
- if (!emailRegex.test(email)) {
432
- return {
433
- provider: null,
434
- email,
435
- loginUrl: null,
436
- error: {
437
- type: 'INVALID_EMAIL',
438
- message: 'Invalid email format'
439
- }
440
- };
441
- }
442
- const domain = email.split('@')[1]?.toLowerCase();
443
- if (!domain) {
419
+ const parsed = validateAndParseEmailForLookup(email);
420
+ if (!parsed.ok) {
444
421
  return {
445
422
  provider: null,
446
- email,
423
+ email: parsed.email,
447
424
  loginUrl: null,
448
- error: {
449
- type: 'INVALID_EMAIL',
450
- message: 'Invalid email format - missing domain'
451
- }
425
+ error: parsed.error
452
426
  };
453
427
  }
428
+ const domain = parsed.domain;
429
+ const trimmedEmail = parsed.trimmedEmail;
454
430
  // First try standard domain matching (fast path)
455
- const syncResult = getEmailProviderSync(email);
431
+ const syncResult = getEmailProviderSync(trimmedEmail);
456
432
  if (syncResult.provider) {
457
433
  return {
458
434
  ...syncResult,
@@ -466,7 +442,7 @@ async function getEmailProviderFast(email, options = {}) {
466
442
  if (!result.success) {
467
443
  return {
468
444
  provider: null,
469
- email,
445
+ email: trimmedEmail,
470
446
  loginUrl: null,
471
447
  error: {
472
448
  type: 'NETWORK_ERROR',
@@ -482,7 +458,7 @@ async function getEmailProviderFast(email, options = {}) {
482
458
  });
483
459
  const fastResult = {
484
460
  provider: concurrentResult.provider,
485
- email,
461
+ email: trimmedEmail,
486
462
  loginUrl: concurrentResult.provider?.loginUrl || null,
487
463
  detectionMethod: concurrentResult.detectionMethod || 'mx_record',
488
464
  timing: concurrentResult.timing,
@@ -505,7 +481,7 @@ async function getEmailProviderFast(email, options = {}) {
505
481
  loginUrl: null,
506
482
  error: {
507
483
  type: 'NETWORK_ERROR',
508
- message: error.message || 'DNS detection failed'
484
+ message: error instanceof Error ? error.message : 'DNS detection failed'
509
485
  }
510
486
  };
511
487
  }
@@ -516,7 +492,7 @@ async function getEmailProviderFast(email, options = {}) {
516
492
  exports.Config = {
517
493
  DEFAULT_DNS_TIMEOUT: 5000,
518
494
  MAX_DNS_REQUESTS_PER_MINUTE: 10,
519
- SUPPORTED_PROVIDERS_COUNT: 93,
520
- SUPPORTED_DOMAINS_COUNT: 180
495
+ SUPPORTED_PROVIDERS_COUNT: 130,
496
+ SUPPORTED_DOMAINS_COUNT: 218
521
497
  };
522
498
  //# sourceMappingURL=api.js.map
@@ -5,6 +5,10 @@
5
5
  * Uses Promise.allSettled for fault tolerance and intelligent result merging.
6
6
  */
7
7
  import { EmailProvider } from './api';
8
+ type MXRecordLike = {
9
+ exchange?: string;
10
+ };
11
+ type TXTRecordLike = string;
8
12
  /**
9
13
  * Configuration for concurrent DNS detection
10
14
  */
@@ -29,13 +33,13 @@ export interface DNSQueryResult {
29
33
  /** Whether the query succeeded */
30
34
  success: boolean;
31
35
  /** DNS records returned (if successful) */
32
- records?: any[];
36
+ records?: MXRecordLike[] | TXTRecordLike[];
33
37
  /** Error information (if failed) */
34
38
  error?: Error;
35
39
  /** Query execution time in milliseconds */
36
40
  timing: number;
37
41
  /** Raw DNS response for debugging */
38
- rawResponse?: any;
42
+ rawResponse?: unknown;
39
43
  }
40
44
  /**
41
45
  * Result from concurrent DNS detection
@@ -130,4 +134,5 @@ export declare function createConcurrentDNSDetector(providers: EmailProvider[],
130
134
  * Utility function for quick concurrent DNS detection
131
135
  */
132
136
  export declare function detectProviderConcurrent(domain: string, providers: EmailProvider[], config?: Partial<ConcurrentDNSConfig>): Promise<ConcurrentDNSResult>;
137
+ export {};
133
138
  //# sourceMappingURL=concurrent-dns.d.ts.map
@@ -295,6 +295,8 @@ class ConcurrentDNSDetector {
295
295
  let confidence = 0;
296
296
  if (query.type === 'mx' && detection.mxPatterns) {
297
297
  for (const record of query.records) {
298
+ if (typeof record !== 'object' || record === null)
299
+ continue;
298
300
  const exchange = record.exchange?.toLowerCase() || '';
299
301
  for (const pattern of detection.mxPatterns) {
300
302
  if (exchange.includes(pattern.toLowerCase())) {
@@ -306,6 +308,8 @@ class ConcurrentDNSDetector {
306
308
  }
307
309
  else if (query.type === 'txt' && detection.txtPatterns) {
308
310
  for (const record of query.records) {
311
+ if (typeof record !== 'string')
312
+ continue;
309
313
  const txtRecord = record.toLowerCase();
310
314
  for (const pattern of detection.txtPatterns) {
311
315
  if (txtRecord.includes(pattern.toLowerCase())) {
@@ -368,6 +372,8 @@ class ConcurrentDNSDetector {
368
372
  if (!mxQuery?.records)
369
373
  return null;
370
374
  for (const record of mxQuery.records) {
375
+ if (typeof record !== 'object' || record === null)
376
+ continue;
371
377
  const exchange = record.exchange?.toLowerCase() || '';
372
378
  for (const provider of this.providers) {
373
379
  if (provider.type === 'proxy_service' && provider.customDomainDetection?.mxPatterns) {
@@ -402,9 +408,11 @@ class ConcurrentDNSDetector {
402
408
  return Promise.reject(new Error(`DNS query timeout after ${ms}ms`));
403
409
  }
404
410
  let rejectFn;
411
+ let queryEntry;
405
412
  const wrappedPromise = new Promise((resolve, reject) => {
406
413
  rejectFn = reject;
407
- const timeout = setTimeout(() => reject(new Error(`DNS query timeout after ${ms}ms`)), ms).unref();
414
+ const timeout = setTimeout(() => reject(new Error(`DNS query timeout after ${ms}ms`)), ms);
415
+ timeout.unref?.();
408
416
  // Start the underlying operation only after setting up the timeout
409
417
  fn()
410
418
  .then(resolve)
@@ -412,7 +420,6 @@ class ConcurrentDNSDetector {
412
420
  .finally(() => {
413
421
  clearTimeout(timeout);
414
422
  // Clean up active query
415
- const queryEntry = Array.from(this.activeQueries).find(entry => entry.promise === wrappedPromise);
416
423
  if (queryEntry) {
417
424
  this.activeQueries.delete(queryEntry);
418
425
  }
@@ -420,7 +427,7 @@ class ConcurrentDNSDetector {
420
427
  });
421
428
  // Track active query for potential cleanup in tests
422
429
  if (rejectFn) {
423
- const queryEntry = { promise: wrappedPromise, reject: rejectFn };
430
+ queryEntry = { promise: wrappedPromise, reject: rejectFn };
424
431
  this.activeQueries.add(queryEntry);
425
432
  }
426
433
  return wrappedPromise;
@@ -4,6 +4,10 @@
4
4
  * Provides cryptographic integrity verification for the email providers database
5
5
  * to detect tampering or unauthorized modifications.
6
6
  */
7
+ type ProviderLike = {
8
+ companyProvider?: string;
9
+ loginUrl?: string | null;
10
+ };
7
11
  export interface HashVerificationResult {
8
12
  isValid: boolean;
9
13
  expectedHash?: string;
@@ -40,7 +44,7 @@ export declare function verifyProvidersIntegrity(filePath: string, expectedHash?
40
44
  * @param expectedHash - Expected hash of the JSON string
41
45
  * @returns Verification result
42
46
  */
43
- export declare function verifyProvidersDataIntegrity(providersData: any, expectedHash?: string): HashVerificationResult;
47
+ export declare function verifyProvidersDataIntegrity(providersData: unknown, expectedHash?: string): HashVerificationResult;
44
48
  /**
45
49
  * Generates security hashes for critical files - use this during development
46
50
  *
@@ -85,10 +89,11 @@ export declare function performSecurityAudit(providersFilePath?: string): {
85
89
  * @param providers - Array of email providers
86
90
  * @returns Signed manifest with URL hashes
87
91
  */
88
- export declare function createProviderManifest(providers: any[]): {
92
+ export declare function createProviderManifest(providers: ProviderLike[]): {
89
93
  timestamp: string;
90
94
  providerCount: number;
91
95
  urlHashes: Record<string, string>;
92
96
  manifestHash: string;
93
97
  };
98
+ export {};
94
99
  //# sourceMappingURL=hash-verifier.d.ts.map
@@ -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': 'c74455b537534268c28b7312664c676cb0f511e6674367da603a15c5fe30e16f',
30
+ 'emailproviders.json': 'a4fe056edad44ae5479cc100d5cc67cb5f6df86e19c4209db6c5f715f5bf070e',
31
31
  // You can add hashes for other critical files
32
- 'package.json': '7870604095cdf8c5c1b081fd76cf1bd20d906d9a97b3dfcaf228c93b3f7856f0',
32
+ 'package.json': '4133771103c0b1600b7d19c2290822eaeaaa9b1b84ef42965de4066545e1e960',
33
33
  };
34
34
  /**
35
35
  * Calculates SHA-256 hash of a file or string content
@@ -96,8 +96,17 @@ function verifyProvidersIntegrity(filePath, expectedHash) {
96
96
  */
97
97
  function verifyProvidersDataIntegrity(providersData, expectedHash) {
98
98
  try {
99
+ if (providersData === null || typeof providersData !== 'object') {
100
+ return {
101
+ isValid: false,
102
+ actualHash: '',
103
+ reason: 'Invalid providers data format',
104
+ file: 'providersData'
105
+ };
106
+ }
99
107
  // Create deterministic JSON string (sorted keys)
100
- const jsonString = JSON.stringify(providersData, Object.keys(providersData).sort(), 2);
108
+ const providersObject = providersData;
109
+ const jsonString = JSON.stringify(providersObject, Object.keys(providersObject).sort(), 2);
101
110
  const actualHash = calculateHash(jsonString);
102
111
  const expectedHashToUse = expectedHash || KNOWN_GOOD_HASHES['emailproviders.json'];
103
112
  if (expectedHashToUse === 'TO_BE_CALCULATED') {
@@ -266,7 +275,7 @@ function createProviderManifest(providers) {
266
275
  const urlHashes = {};
267
276
  for (const provider of providers) {
268
277
  if (provider.loginUrl) {
269
- const key = `${provider.companyProvider}::${provider.loginUrl}`;
278
+ const key = `${provider.companyProvider ?? 'Unknown'}::${provider.loginUrl}`;
270
279
  urlHashes[key] = calculateHash(provider.loginUrl);
271
280
  }
272
281
  }
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Email Provider Links
3
3
  *
4
4
  * A modern, robust email provider detection library with:
5
- * - 93+ verified email providers covering 180+ domains
5
+ * - 130+ verified email providers covering 210+ domains
6
6
  * - Concurrent DNS detection for business domains
7
7
  * - Zero runtime dependencies
8
8
  * - Comprehensive error handling with detailed context
@@ -12,13 +12,15 @@
12
12
  *
13
13
  * @author Email Provider Links Team
14
14
  * @license MIT
15
- * @version 2.7.0
15
+ * @version See package.json
16
16
  */
17
- export { getEmailProvider, getEmailProviderSync, getEmailProviderFast, normalizeEmail, emailsMatch, Config } from './api';
17
+ import { loadProviders } from './loader';
18
+ import { getEmailProvider, getEmailProviderSync, getEmailProviderFast, normalizeEmail, emailsMatch, Config } from './api';
19
+ import { detectProviderConcurrent } from './concurrent-dns';
20
+ import { validateInternationalEmail, emailToPunycode, domainToPunycode } from './idn';
21
+ export { getEmailProvider, getEmailProviderSync, getEmailProviderFast, normalizeEmail, emailsMatch, Config };
18
22
  export type { EmailProvider, EmailProviderResult } from './api';
19
- export { loadProviders } from './loader';
20
- export { detectProviderConcurrent } from './concurrent-dns';
21
- export { validateInternationalEmail, emailToPunycode, domainToPunycode } from './idn';
23
+ export { loadProviders, detectProviderConcurrent, validateInternationalEmail, emailToPunycode, domainToPunycode };
22
24
  export type { ConcurrentDNSConfig, ConcurrentDNSResult } from './concurrent-dns';
23
25
  /**
24
26
  * Enhanced email validation with comprehensive error reporting
@@ -45,10 +47,6 @@ export declare function validateEmailAddress(email: string): {
45
47
  message: string;
46
48
  };
47
49
  };
48
- import { loadProviders } from './loader';
49
- import { getEmailProvider, getEmailProviderSync, getEmailProviderFast, normalizeEmail, emailsMatch } from './api';
50
- import { detectProviderConcurrent } from './concurrent-dns';
51
- import { validateInternationalEmail } from './idn';
52
50
  /**
53
51
  * Get comprehensive list of all supported email providers
54
52
  *
@@ -169,8 +167,8 @@ export declare const isValidEmailAddress: typeof isValidEmail;
169
167
  /**
170
168
  * Library metadata (legacy constants)
171
169
  */
172
- export declare const PROVIDER_COUNT = 93;
173
- export declare const DOMAIN_COUNT = 178;
170
+ export declare const PROVIDER_COUNT: 130;
171
+ export declare const DOMAIN_COUNT: 218;
174
172
  /**
175
173
  * Default export for convenience
176
174
  *
@@ -200,15 +198,12 @@ declare const _default: {
200
198
  Config: {
201
199
  readonly DEFAULT_DNS_TIMEOUT: 5000;
202
200
  readonly MAX_DNS_REQUESTS_PER_MINUTE: 10;
203
- readonly SUPPORTED_PROVIDERS_COUNT: 93;
204
- readonly SUPPORTED_DOMAINS_COUNT: 180;
201
+ readonly SUPPORTED_PROVIDERS_COUNT: 130;
202
+ readonly SUPPORTED_DOMAINS_COUNT: 218;
205
203
  };
206
- PROVIDER_COUNT: number;
207
- DOMAIN_COUNT: number;
204
+ PROVIDER_COUNT: 130;
205
+ DOMAIN_COUNT: 218;
208
206
  };
209
207
  export default _default;
210
- /**
211
- * Version information
212
- */
213
- export declare const VERSION = "2.7.0";
208
+ export declare const VERSION: string;
214
209
  //# sourceMappingURL=index.d.ts.map