@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/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Email Provider Links
4
4
  *
5
5
  * A modern, robust email provider detection library with:
6
- * - 93+ verified email providers covering 180+ domains
6
+ * - 130+ verified email providers covering 210+ domains
7
7
  * - Concurrent DNS detection for business domains
8
8
  * - Zero runtime dependencies
9
9
  * - Comprehensive error handling with detailed context
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * @author Email Provider Links Team
15
15
  * @license MIT
16
- * @version 2.7.0
16
+ * @version See package.json
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.VERSION = exports.DOMAIN_COUNT = exports.PROVIDER_COUNT = exports.isValidEmailAddress = exports.domainToPunycode = exports.emailToPunycode = exports.validateInternationalEmail = exports.detectProviderConcurrent = exports.loadProviders = exports.Config = exports.emailsMatch = exports.normalizeEmail = exports.getEmailProviderFast = exports.getEmailProviderSync = exports.getEmailProvider = void 0;
@@ -24,22 +24,18 @@ exports.extractDomain = extractDomain;
24
24
  exports.isValidEmail = isValidEmail;
25
25
  exports.getLibraryStats = getLibraryStats;
26
26
  exports.batchProcessEmails = batchProcessEmails;
27
- // ===== PRIMARY API =====
28
- // Core functions that 95% of users need
29
- var api_1 = require("./api");
27
+ const loader_1 = require("./loader");
28
+ Object.defineProperty(exports, "loadProviders", { enumerable: true, get: function () { return loader_1.loadProviders; } });
29
+ const api_1 = require("./api");
30
30
  Object.defineProperty(exports, "getEmailProvider", { enumerable: true, get: function () { return api_1.getEmailProvider; } });
31
31
  Object.defineProperty(exports, "getEmailProviderSync", { enumerable: true, get: function () { return api_1.getEmailProviderSync; } });
32
32
  Object.defineProperty(exports, "getEmailProviderFast", { enumerable: true, get: function () { return api_1.getEmailProviderFast; } });
33
33
  Object.defineProperty(exports, "normalizeEmail", { enumerable: true, get: function () { return api_1.normalizeEmail; } });
34
34
  Object.defineProperty(exports, "emailsMatch", { enumerable: true, get: function () { return api_1.emailsMatch; } });
35
35
  Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return api_1.Config; } });
36
- // ===== ADVANCED FEATURES =====
37
- // For power users and custom implementations
38
- var loader_1 = require("./loader");
39
- Object.defineProperty(exports, "loadProviders", { enumerable: true, get: function () { return loader_1.loadProviders; } });
40
- var concurrent_dns_1 = require("./concurrent-dns");
36
+ const concurrent_dns_1 = require("./concurrent-dns");
41
37
  Object.defineProperty(exports, "detectProviderConcurrent", { enumerable: true, get: function () { return concurrent_dns_1.detectProviderConcurrent; } });
42
- var idn_1 = require("./idn");
38
+ const idn_1 = require("./idn");
43
39
  Object.defineProperty(exports, "validateInternationalEmail", { enumerable: true, get: function () { return idn_1.validateInternationalEmail; } });
44
40
  Object.defineProperty(exports, "emailToPunycode", { enumerable: true, get: function () { return idn_1.emailToPunycode; } });
45
41
  Object.defineProperty(exports, "domainToPunycode", { enumerable: true, get: function () { return idn_1.domainToPunycode; } });
@@ -86,7 +82,7 @@ function validateEmailAddress(email) {
86
82
  };
87
83
  }
88
84
  // Use international validation
89
- const idnError = (0, idn_2.validateInternationalEmail)(trimmedEmail);
85
+ const idnError = (0, idn_1.validateInternationalEmail)(trimmedEmail);
90
86
  if (idnError) {
91
87
  return {
92
88
  isValid: false,
@@ -104,10 +100,6 @@ function validateEmailAddress(email) {
104
100
  }
105
101
  // ===== UTILITY FUNCTIONS =====
106
102
  // Helper functions for common tasks
107
- const loader_2 = require("./loader");
108
- const api_2 = require("./api");
109
- const concurrent_dns_2 = require("./concurrent-dns");
110
- const idn_2 = require("./idn");
111
103
  /**
112
104
  * Get comprehensive list of all supported email providers
113
105
  *
@@ -124,7 +116,7 @@ const idn_2 = require("./idn");
124
116
  */
125
117
  function getSupportedProviders() {
126
118
  try {
127
- const { providers } = (0, loader_2.loadProviders)();
119
+ const { providers } = (0, loader_1.loadProviders)();
128
120
  return [...providers]; // Return defensive copy to prevent external mutations
129
121
  }
130
122
  catch (error) {
@@ -150,7 +142,7 @@ function isEmailProviderSupported(email) {
150
142
  if (!email || typeof email !== 'string') {
151
143
  return false;
152
144
  }
153
- const result = (0, api_2.getEmailProviderSync)(email);
145
+ const result = (0, api_1.getEmailProviderSync)(email);
154
146
  return result.provider !== null;
155
147
  }
156
148
  catch {
@@ -227,7 +219,7 @@ function getLibraryStats() {
227
219
  return {
228
220
  providerCount: providers.length,
229
221
  domainCount,
230
- version: '2.7.0',
222
+ version: exports.VERSION,
231
223
  supportsAsync: true,
232
224
  supportsIDN: true,
233
225
  supportsAliasDetection: true,
@@ -238,7 +230,7 @@ function getLibraryStats() {
238
230
  return {
239
231
  providerCount: 0,
240
232
  domainCount: 0,
241
- version: '2.7.0',
233
+ version: exports.VERSION,
242
234
  supportsAsync: true,
243
235
  supportsIDN: true,
244
236
  supportsAliasDetection: true,
@@ -282,7 +274,7 @@ function batchProcessEmails(emails, options = {}) {
282
274
  // Add normalized email if requested
283
275
  if (normalizeEmails && validation.normalizedEmail) {
284
276
  try {
285
- result.normalized = (0, api_2.normalizeEmail)(validation.normalizedEmail);
277
+ result.normalized = (0, api_1.normalizeEmail)(validation.normalizedEmail);
286
278
  }
287
279
  catch {
288
280
  result.normalized = validation.normalizedEmail;
@@ -300,7 +292,7 @@ function batchProcessEmails(emails, options = {}) {
300
292
  // Add provider info if requested
301
293
  if (includeProviderInfo && validation.normalizedEmail) {
302
294
  try {
303
- const providerResult = (0, api_2.getEmailProviderSync)(validation.normalizedEmail);
295
+ const providerResult = (0, api_1.getEmailProviderSync)(validation.normalizedEmail);
304
296
  result.provider = providerResult.provider?.companyProvider || null;
305
297
  result.loginUrl = providerResult.loginUrl;
306
298
  }
@@ -329,8 +321,8 @@ exports.isValidEmailAddress = isValidEmail;
329
321
  /**
330
322
  * Library metadata (legacy constants)
331
323
  */
332
- exports.PROVIDER_COUNT = 93;
333
- exports.DOMAIN_COUNT = 178;
324
+ exports.PROVIDER_COUNT = api_1.Config?.SUPPORTED_PROVIDERS_COUNT ?? 130;
325
+ exports.DOMAIN_COUNT = api_1.Config?.SUPPORTED_DOMAINS_COUNT ?? 218;
334
326
  /**
335
327
  * Default export for convenience
336
328
  *
@@ -343,14 +335,14 @@ exports.DOMAIN_COUNT = 178;
343
335
  */
344
336
  exports.default = {
345
337
  // Core functions
346
- getEmailProvider: api_2.getEmailProvider,
347
- getEmailProviderSync: api_2.getEmailProviderSync,
348
- getEmailProviderFast: api_2.getEmailProviderFast,
338
+ getEmailProvider: api_1.getEmailProvider,
339
+ getEmailProviderSync: api_1.getEmailProviderSync,
340
+ getEmailProviderFast: api_1.getEmailProviderFast,
349
341
  // Validation
350
342
  validateEmailAddress,
351
343
  isValidEmail,
352
- normalizeEmail: api_2.normalizeEmail,
353
- emailsMatch: api_2.emailsMatch,
344
+ normalizeEmail: api_1.normalizeEmail,
345
+ emailsMatch: api_1.emailsMatch,
354
346
  // Utilities
355
347
  getSupportedProviders,
356
348
  isEmailProviderSupported,
@@ -358,16 +350,25 @@ exports.default = {
358
350
  getLibraryStats,
359
351
  batchProcessEmails,
360
352
  // Advanced
361
- loadProviders: loader_2.loadProviders,
362
- detectProviderConcurrent: concurrent_dns_2.detectProviderConcurrent,
363
- validateInternationalEmail: idn_2.validateInternationalEmail,
353
+ loadProviders: loader_1.loadProviders,
354
+ detectProviderConcurrent: concurrent_dns_1.detectProviderConcurrent,
355
+ validateInternationalEmail: idn_1.validateInternationalEmail,
364
356
  // Constants
365
- Config: api_2.Config,
357
+ Config: api_1.Config,
366
358
  PROVIDER_COUNT: exports.PROVIDER_COUNT,
367
359
  DOMAIN_COUNT: exports.DOMAIN_COUNT
368
360
  };
369
361
  /**
370
362
  * Version information
371
363
  */
372
- exports.VERSION = '2.7.0';
364
+ function readPackageVersion() {
365
+ try {
366
+ const pkg = require('../package.json');
367
+ return pkg.version || 'unknown';
368
+ }
369
+ catch {
370
+ return 'unknown';
371
+ }
372
+ }
373
+ exports.VERSION = readPackageVersion();
373
374
  //# sourceMappingURL=index.js.map
package/dist/loader.js CHANGED
@@ -10,9 +10,8 @@ exports.clearCache = clearCache;
10
10
  exports.getLoadingStats = getLoadingStats;
11
11
  exports.loadProviders = loadProviders;
12
12
  exports.loadProvidersDebug = loadProvidersDebug;
13
- const fs_1 = require("fs");
14
13
  const path_1 = require("path");
15
- const schema_1 = require("./schema");
14
+ const provider_store_1 = require("./provider-store");
16
15
  /**
17
16
  * Internal cached data
18
17
  */
@@ -25,35 +24,6 @@ let loadingStats = null;
25
24
  const DEFAULT_CONFIG = {
26
25
  debug: false
27
26
  };
28
- /**
29
- * Convert compressed provider to EmailProvider format
30
- */
31
- function convertProviderToEmailProvider(compressedProvider) {
32
- if (!compressedProvider.type) {
33
- console.warn(`Missing type for provider ${compressedProvider.id}`);
34
- }
35
- const provider = {
36
- companyProvider: compressedProvider.companyProvider,
37
- loginUrl: compressedProvider.loginUrl || null,
38
- domains: compressedProvider.domains || [],
39
- type: compressedProvider.type,
40
- alias: compressedProvider.alias
41
- };
42
- // Include DNS detection patterns for business email services and proxy services
43
- const needsCustomDomainDetection = compressedProvider.type === 'custom_provider' ||
44
- compressedProvider.type === 'proxy_service';
45
- if (needsCustomDomainDetection && (compressedProvider.mx?.length || compressedProvider.txt?.length)) {
46
- provider.customDomainDetection = {};
47
- if (compressedProvider.mx?.length) {
48
- provider.customDomainDetection.mxPatterns = compressedProvider.mx;
49
- }
50
- if (compressedProvider.txt?.length) {
51
- // Decompress TXT patterns
52
- provider.customDomainDetection.txtPatterns = compressedProvider.txt.map(schema_1.decompressTxtPattern);
53
- }
54
- }
55
- return provider;
56
- }
57
27
  /**
58
28
  * Internal provider data loader with configuration
59
29
  */
@@ -73,14 +43,8 @@ function loadProvidersInternal(config = {}) {
73
43
  const dataPath = mergedConfig.path || (0, path_1.join)(basePath, 'emailproviders.json');
74
44
  if (mergedConfig.debug)
75
45
  console.log('🔄 Loading provider data...');
76
- const content = (0, fs_1.readFileSync)(dataPath, 'utf8');
77
- const data = JSON.parse(content);
78
- // Validate format
79
- if (!data.version || !data.providers || !Array.isArray(data.providers)) {
80
- throw new Error('Invalid provider data format');
81
- }
82
- const providers = data.providers.map(convertProviderToEmailProvider);
83
- const fileSize = content.length;
46
+ const { data, fileSize } = (0, provider_store_1.readProvidersDataFile)(dataPath);
47
+ const providers = data.providers.map(provider_store_1.convertProviderToEmailProviderShared);
84
48
  if (mergedConfig.debug) {
85
49
  console.log(`✅ Loaded ${providers.length} providers`);
86
50
  console.log(`📊 File size: ${(fileSize / 1024).toFixed(1)} KB`);
@@ -120,14 +84,8 @@ function buildDomainMap(providers) {
120
84
  if (cachedDomainMap) {
121
85
  return cachedDomainMap;
122
86
  }
123
- const domainMap = new Map();
124
- for (const provider of providers) {
125
- for (const domain of provider.domains) {
126
- domainMap.set(domain.toLowerCase(), provider);
127
- }
128
- }
129
- cachedDomainMap = domainMap;
130
- return domainMap;
87
+ cachedDomainMap = (0, provider_store_1.buildDomainMapShared)(providers);
88
+ return cachedDomainMap;
131
89
  }
132
90
  /**
133
91
  * Clear all caches (useful for testing or hot reloading)
@@ -4,7 +4,17 @@
4
4
  * Integrates URL validation and hash verification to load and validate
5
5
  * email provider data with security checks.
6
6
  */
7
- import type { EmailProvider } from './index';
7
+ import type { EmailProvider } from './api';
8
+ type MiddlewareRequestLike = Record<string, unknown> & {
9
+ secureProviders?: EmailProvider[];
10
+ securityReport?: LoadResult['securityReport'];
11
+ };
12
+ type MiddlewareResponseLike = {
13
+ status: (code: number) => {
14
+ json: (body: unknown) => unknown;
15
+ };
16
+ };
17
+ type MiddlewareNextLike = () => void;
8
18
  export interface LoadResult {
9
19
  success: boolean;
10
20
  providers: EmailProvider[];
@@ -43,7 +53,7 @@ interface SecurityMiddlewareOptions {
43
53
  onSecurityIssue?: (report: LoadResult['securityReport']) => void;
44
54
  getProviders?: () => LoadResult;
45
55
  }
46
- export declare function createSecurityMiddleware(options?: SecurityMiddlewareOptions): (req: any, res: any, next: any) => any;
56
+ export declare function createSecurityMiddleware(options?: SecurityMiddlewareOptions): (req: MiddlewareRequestLike, res: MiddlewareResponseLike, next: MiddlewareNextLike) => void;
47
57
  declare const _default: {
48
58
  loadProviders: typeof loadProviders;
49
59
  initializeSecurity: typeof initializeSecurity;
@@ -10,11 +10,10 @@ exports.clearCache = clearCache;
10
10
  exports.loadProviders = loadProviders;
11
11
  exports.initializeSecurity = initializeSecurity;
12
12
  exports.createSecurityMiddleware = createSecurityMiddleware;
13
- const fs_1 = require("fs");
14
13
  const path_1 = require("path");
15
14
  const url_validator_1 = require("./url-validator");
16
15
  const hash_verifier_1 = require("./hash-verifier");
17
- const schema_1 = require("./schema");
16
+ const provider_store_1 = require("./provider-store");
18
17
  // Cache for load results
19
18
  let cachedLoadResult = null;
20
19
  /**
@@ -23,35 +22,6 @@ let cachedLoadResult = null;
23
22
  function clearCache() {
24
23
  cachedLoadResult = null;
25
24
  }
26
- /**
27
- * Convert compressed provider to EmailProvider format
28
- */
29
- function convertProviderToEmailProvider(compressedProvider) {
30
- if (!compressedProvider.type) {
31
- console.warn(`Missing type for provider ${compressedProvider.id}`);
32
- }
33
- const provider = {
34
- companyProvider: compressedProvider.companyProvider,
35
- loginUrl: compressedProvider.loginUrl || null,
36
- domains: compressedProvider.domains || [],
37
- type: compressedProvider.type,
38
- alias: compressedProvider.alias
39
- };
40
- // Include DNS detection patterns for business email services and proxy services
41
- const needsCustomDomainDetection = compressedProvider.type === 'custom_provider' ||
42
- compressedProvider.type === 'proxy_service';
43
- if (needsCustomDomainDetection && (compressedProvider.mx?.length || compressedProvider.txt?.length)) {
44
- provider.customDomainDetection = {};
45
- if (compressedProvider.mx?.length) {
46
- provider.customDomainDetection.mxPatterns = compressedProvider.mx;
47
- }
48
- if (compressedProvider.txt?.length) {
49
- // Decompress TXT patterns
50
- provider.customDomainDetection.txtPatterns = compressedProvider.txt.map(schema_1.decompressTxtPattern);
51
- }
52
- }
53
- return provider;
54
- }
55
25
  /**
56
26
  * Loads and validates email provider data with security checks
57
27
  *
@@ -83,14 +53,8 @@ function loadProviders(providersPath, expectedHash) {
83
53
  }
84
54
  // Step 2: Load and parse JSON
85
55
  try {
86
- const fileContent = (0, fs_1.readFileSync)(filePath, 'utf8');
87
- const data = JSON.parse(fileContent);
88
- // Validate format
89
- if (!data.version || !data.providers || !Array.isArray(data.providers)) {
90
- throw new Error('Invalid provider data format');
91
- }
92
- // Convert to EmailProvider format
93
- providers = data.providers.map(convertProviderToEmailProvider);
56
+ const { data } = (0, provider_store_1.readProvidersDataFile)(filePath);
57
+ providers = data.providers.map(provider_store_1.convertProviderToEmailProviderShared);
94
58
  }
95
59
  catch (error) {
96
60
  issues.push(`Failed to load providers file: ${error instanceof Error ? error.message : 'Unknown error'}`);
@@ -178,15 +142,17 @@ function createSecurityMiddleware(options = {}) {
178
142
  if (options.onSecurityIssue) {
179
143
  options.onSecurityIssue(result.securityReport);
180
144
  }
181
- return res.status(500).json({
145
+ res.status(500).json({
182
146
  error: 'Security validation failed',
183
147
  details: result.securityReport
184
148
  });
149
+ return;
185
150
  }
186
151
  // Attach secure providers to request
187
152
  req.secureProviders = result.providers;
188
153
  req.securityReport = result.securityReport;
189
154
  next();
155
+ return;
190
156
  };
191
157
  }
192
158
  exports.default = {
@@ -0,0 +1,9 @@
1
+ import type { EmailProvider } from './api';
2
+ import { Provider, ProvidersData } from './schema';
3
+ export declare function convertProviderToEmailProviderShared(compressedProvider: Provider): EmailProvider;
4
+ export declare function readProvidersDataFile(filePath: string): {
5
+ data: ProvidersData;
6
+ fileSize: number;
7
+ };
8
+ export declare function buildDomainMapShared(providers: EmailProvider[]): Map<string, EmailProvider>;
9
+ //# sourceMappingURL=provider-store.d.ts.map
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertProviderToEmailProviderShared = convertProviderToEmailProviderShared;
4
+ exports.readProvidersDataFile = readProvidersDataFile;
5
+ exports.buildDomainMapShared = buildDomainMapShared;
6
+ const fs_1 = require("fs");
7
+ const schema_1 = require("./schema");
8
+ function convertProviderToEmailProviderShared(compressedProvider) {
9
+ if (!compressedProvider.type) {
10
+ console.warn(`Missing type for provider ${compressedProvider.id}`);
11
+ }
12
+ const provider = {
13
+ companyProvider: compressedProvider.companyProvider,
14
+ loginUrl: compressedProvider.loginUrl || null,
15
+ domains: compressedProvider.domains || [],
16
+ type: compressedProvider.type,
17
+ alias: compressedProvider.alias
18
+ };
19
+ const needsCustomDomainDetection = compressedProvider.type === 'custom_provider' ||
20
+ compressedProvider.type === 'proxy_service';
21
+ if (needsCustomDomainDetection && (compressedProvider.mx?.length || compressedProvider.txt?.length)) {
22
+ provider.customDomainDetection = {};
23
+ if (compressedProvider.mx?.length) {
24
+ provider.customDomainDetection.mxPatterns = compressedProvider.mx;
25
+ }
26
+ if (compressedProvider.txt?.length) {
27
+ provider.customDomainDetection.txtPatterns = compressedProvider.txt.map(schema_1.decompressTxtPattern);
28
+ }
29
+ }
30
+ return provider;
31
+ }
32
+ function readProvidersDataFile(filePath) {
33
+ const content = (0, fs_1.readFileSync)(filePath, 'utf8');
34
+ const data = JSON.parse(content);
35
+ if (!data.version || !data.providers || !Array.isArray(data.providers)) {
36
+ throw new Error('Invalid provider data format');
37
+ }
38
+ return { data, fileSize: content.length };
39
+ }
40
+ function buildDomainMapShared(providers) {
41
+ const domainMap = new Map();
42
+ for (const provider of providers) {
43
+ for (const domain of provider.domains) {
44
+ domainMap.set(domain.toLowerCase(), provider);
45
+ }
46
+ }
47
+ return domainMap;
48
+ }
49
+ //# sourceMappingURL=provider-store.js.map
@@ -4,6 +4,10 @@
4
4
  * Provides validation and allowlisting for email provider URLs to prevent
5
5
  * malicious redirects and supply chain attacks.
6
6
  */
7
+ type ProviderUrlLike = {
8
+ companyProvider?: string;
9
+ loginUrl?: string | null;
10
+ };
7
11
  export interface URLValidationResult {
8
12
  isValid: boolean;
9
13
  reason?: string;
@@ -23,7 +27,7 @@ export declare function validateEmailProviderUrl(url: string): URLValidationResu
23
27
  * @param providers - Array of email providers to validate
24
28
  * @returns Array of validation results
25
29
  */
26
- export declare function validateAllProviderUrls(providers: any[]): Array<{
30
+ export declare function validateAllProviderUrls(providers: ProviderUrlLike[]): Array<{
27
31
  provider: string;
28
32
  url: string;
29
33
  validation: URLValidationResult;
@@ -34,7 +38,7 @@ export declare function validateAllProviderUrls(providers: any[]): Array<{
34
38
  * @param providers - Array of email providers to audit
35
39
  * @returns Security audit report
36
40
  */
37
- export declare function auditProviderSecurity(providers: any[]): {
41
+ export declare function auditProviderSecurity(providers: ProviderUrlLike[]): {
38
42
  total: number;
39
43
  valid: number;
40
44
  invalid: number;
@@ -45,4 +49,5 @@ export declare function auditProviderSecurity(providers: any[]): {
45
49
  }[];
46
50
  report: string;
47
51
  };
52
+ export {};
48
53
  //# sourceMappingURL=url-validator.d.ts.map
@@ -9,19 +9,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.validateEmailProviderUrl = validateEmailProviderUrl;
10
10
  exports.validateAllProviderUrls = validateAllProviderUrls;
11
11
  exports.auditProviderSecurity = auditProviderSecurity;
12
- const loader_1 = require("./loader");
12
+ const fs_1 = require("fs");
13
+ const path_1 = require("path");
14
+ const hash_verifier_1 = require("./hash-verifier");
13
15
  /**
14
16
  * Get allowlisted domains from provider data
15
17
  * Only URLs from these domains will be considered safe.
16
18
  */
17
19
  function getAllowedDomains() {
18
- const { providers } = (0, loader_1.loadProviders)();
20
+ const filePath = (0, path_1.join)(__dirname, '..', 'providers', 'emailproviders.json');
21
+ const integrity = (0, hash_verifier_1.verifyProvidersIntegrity)(filePath);
22
+ if (!integrity.isValid) {
23
+ return new Set();
24
+ }
25
+ if (cachedAllowedDomains && cachedAllowlistHash === integrity.actualHash) {
26
+ return cachedAllowedDomains;
27
+ }
28
+ const fileContent = (0, fs_1.readFileSync)(filePath, 'utf8');
29
+ const data = JSON.parse(fileContent);
30
+ const providers = data.providers;
19
31
  const allowedDomains = new Set();
20
32
  for (const provider of providers) {
21
33
  if (provider.loginUrl) {
22
34
  try {
23
35
  const url = new URL(provider.loginUrl);
24
- allowedDomains.add(url.hostname);
36
+ allowedDomains.add((0, idn_1.domainToPunycode)(url.hostname.toLowerCase()));
25
37
  }
26
38
  catch {
27
39
  // Skip invalid URLs
@@ -29,8 +41,12 @@ function getAllowedDomains() {
29
41
  }
30
42
  }
31
43
  }
44
+ cachedAllowedDomains = allowedDomains;
45
+ cachedAllowlistHash = integrity.actualHash;
32
46
  return allowedDomains;
33
47
  }
48
+ let cachedAllowedDomains = null;
49
+ let cachedAllowlistHash = null;
34
50
  /**
35
51
  * Suspicious URL patterns that should always be rejected
36
52
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mikkelscheike/email-provider-links",
3
- "version": "4.0.10",
4
- "description": "TypeScript library for email provider detection with 93 providers (207 domains), concurrent DNS resolution, optimized performance, 94.65% test coverage, and enterprise security for login and password reset flows",
3
+ "version": "5.0.0",
4
+ "description": "TypeScript library for email provider detection with 130 providers (218 domains), concurrent DNS resolution, optimized performance, 94.65% test coverage, and enterprise security for login and password reset flows",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [