@mikkelscheike/email-provider-links 1.6.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.
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Provider Data Loader
3
+ *
4
+ * Handles loading email provider data with performance optimizations.
5
+ */
6
+ import { EmailProvider } from './index';
7
+ /**
8
+ * Loading statistics for performance monitoring
9
+ */
10
+ interface LoadingStats {
11
+ fileSize: number;
12
+ loadTime: number;
13
+ providerCount: number;
14
+ domainCount: number;
15
+ }
16
+ /**
17
+ * Build optimized domain-to-provider lookup map
18
+ */
19
+ export declare function buildDomainMap(providers: EmailProvider[]): Map<string, EmailProvider>;
20
+ /**
21
+ * Clear all caches (useful for testing or hot reloading)
22
+ */
23
+ export declare function clearCache(): void;
24
+ /**
25
+ * Get loading statistics from the last load operation
26
+ */
27
+ export declare function getLoadingStats(): LoadingStats | null;
28
+ /**
29
+ * Load all providers with optimized domain map for production
30
+ */
31
+ export declare function loadProviders(): {
32
+ providers: EmailProvider[];
33
+ domainMap: Map<string, EmailProvider>;
34
+ stats: LoadingStats;
35
+ };
36
+ /**
37
+ * Load providers with debug information
38
+ */
39
+ export declare function loadProvidersDebug(): {
40
+ providers: EmailProvider[];
41
+ domainMap: Map<string, EmailProvider>;
42
+ stats: LoadingStats;
43
+ };
44
+ export {};
package/dist/loader.js ADDED
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ /**
3
+ * Provider Data Loader
4
+ *
5
+ * Handles loading email provider data with performance optimizations.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.buildDomainMap = buildDomainMap;
9
+ exports.clearCache = clearCache;
10
+ exports.getLoadingStats = getLoadingStats;
11
+ exports.loadProviders = loadProviders;
12
+ exports.loadProvidersDebug = loadProvidersDebug;
13
+ const fs_1 = require("fs");
14
+ const path_1 = require("path");
15
+ const schema_1 = require("./schema");
16
+ /**
17
+ * Internal cached data
18
+ */
19
+ let cachedProviders = null;
20
+ let cachedDomainMap = null;
21
+ let loadingStats = null;
22
+ /**
23
+ * Default loader configuration
24
+ */
25
+ const DEFAULT_CONFIG = {
26
+ debug: false
27
+ };
28
+ /**
29
+ * Convert compressed provider to EmailProvider format
30
+ */
31
+ function convertProviderToEmailProvider(compressedProvider) {
32
+ const provider = {
33
+ companyProvider: compressedProvider.name,
34
+ loginUrl: compressedProvider.url,
35
+ domains: compressedProvider.domains || []
36
+ };
37
+ // Convert DNS detection patterns
38
+ if (compressedProvider.mx?.length || compressedProvider.txt?.length) {
39
+ provider.customDomainDetection = {};
40
+ if (compressedProvider.mx?.length) {
41
+ provider.customDomainDetection.mxPatterns = compressedProvider.mx;
42
+ }
43
+ if (compressedProvider.txt?.length) {
44
+ // Decompress TXT patterns
45
+ provider.customDomainDetection.txtPatterns = compressedProvider.txt.map(schema_1.decompressTxtPattern);
46
+ }
47
+ }
48
+ return provider;
49
+ }
50
+ /**
51
+ * Internal provider data loader with configuration
52
+ */
53
+ function loadProvidersInternal(config = {}) {
54
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
55
+ const startTime = Date.now();
56
+ // Return cached data if available
57
+ if (cachedProviders && !mergedConfig.debug) {
58
+ return {
59
+ providers: cachedProviders,
60
+ stats: loadingStats
61
+ };
62
+ }
63
+ try {
64
+ // Determine file path
65
+ const basePath = (0, path_1.join)(__dirname, '..', 'providers');
66
+ const dataPath = mergedConfig.path || (0, path_1.join)(basePath, 'emailproviders.json');
67
+ if (mergedConfig.debug)
68
+ console.log('🔄 Loading provider data...');
69
+ const content = (0, fs_1.readFileSync)(dataPath, 'utf8');
70
+ const data = JSON.parse(content);
71
+ // Validate format
72
+ if (!data.version || !data.providers || !Array.isArray(data.providers)) {
73
+ throw new Error('Invalid provider data format');
74
+ }
75
+ const providers = data.providers.map(convertProviderToEmailProvider);
76
+ const fileSize = content.length;
77
+ if (mergedConfig.debug) {
78
+ console.log(`✅ Loaded ${providers.length} providers`);
79
+ console.log(`📊 File size: ${(fileSize / 1024).toFixed(1)} KB`);
80
+ }
81
+ const loadTime = Date.now() - startTime;
82
+ const domainCount = providers.reduce((sum, p) => sum + p.domains.length, 0);
83
+ // Cache the results
84
+ cachedProviders = providers;
85
+ loadingStats = {
86
+ fileSize,
87
+ loadTime,
88
+ providerCount: providers.length,
89
+ domainCount
90
+ };
91
+ if (mergedConfig.debug) {
92
+ console.log(`⚡ Loading completed in ${loadTime}ms`);
93
+ console.log(`📊 Stats: ${providers.length} providers, ${domainCount} domains`);
94
+ }
95
+ return {
96
+ providers,
97
+ stats: loadingStats
98
+ };
99
+ }
100
+ catch (error) {
101
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
102
+ throw new Error(`Failed to load provider data: ${errorMessage}`);
103
+ }
104
+ }
105
+ /**
106
+ * Build optimized domain-to-provider lookup map
107
+ */
108
+ function buildDomainMap(providers) {
109
+ if (cachedDomainMap) {
110
+ return cachedDomainMap;
111
+ }
112
+ const startTime = Date.now();
113
+ const domainMap = new Map();
114
+ for (const provider of providers) {
115
+ for (const domain of provider.domains) {
116
+ domainMap.set(domain.toLowerCase(), provider);
117
+ }
118
+ }
119
+ cachedDomainMap = domainMap;
120
+ if (loadingStats) {
121
+ console.log(`🗺️ Domain map built in ${Date.now() - startTime}ms (${domainMap.size} entries)`);
122
+ }
123
+ return domainMap;
124
+ }
125
+ /**
126
+ * Clear all caches (useful for testing or hot reloading)
127
+ */
128
+ function clearCache() {
129
+ cachedProviders = null;
130
+ cachedDomainMap = null;
131
+ loadingStats = null;
132
+ }
133
+ /**
134
+ * Get loading statistics from the last load operation
135
+ */
136
+ function getLoadingStats() {
137
+ return loadingStats;
138
+ }
139
+ /**
140
+ * Load all providers with optimized domain map for production
141
+ */
142
+ function loadProviders() {
143
+ const { providers, stats } = loadProvidersInternal({ debug: false });
144
+ const domainMap = buildDomainMap(providers);
145
+ return { providers, domainMap, stats };
146
+ }
147
+ /**
148
+ * Load providers with debug information
149
+ */
150
+ function loadProvidersDebug() {
151
+ clearCache(); // Always reload in debug mode
152
+ const { providers, stats } = loadProvidersInternal({ debug: true });
153
+ const domainMap = buildDomainMap(providers);
154
+ return { providers, domainMap, stats };
155
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Provider Data Schema
3
+ *
4
+ * This represents the compressed format for provider data
5
+ * designed to reduce file size and improve parsing performance.
6
+ */
7
+ /**
8
+ * Provider interface
9
+ * Uses compact field names for smaller JSON size
10
+ */
11
+ export interface Provider {
12
+ /** Provider ID (short identifier) */
13
+ id: string;
14
+ /** Provider display name */
15
+ name: string;
16
+ /** Login/webmail URL */
17
+ url: string;
18
+ /** Email domains (omitted if empty) */
19
+ domains?: string[];
20
+ /** DNS detection patterns (flattened) */
21
+ mx?: string[];
22
+ txt?: string[];
23
+ /** Alias capabilities */
24
+ alias?: {
25
+ dots?: boolean;
26
+ plus?: boolean;
27
+ };
28
+ }
29
+ /**
30
+ * Providers file structure
31
+ */
32
+ export interface ProvidersData {
33
+ /** Schema version for future migrations */
34
+ version: string;
35
+ /** Compressed providers array */
36
+ providers: Provider[];
37
+ /** Metadata */
38
+ meta: {
39
+ count: number;
40
+ domains: number;
41
+ generated: string;
42
+ };
43
+ }
44
+ /**
45
+ * Common TXT pattern prefixes that can be compressed
46
+ */
47
+ export declare const TXT_PATTERN_COMPRESSION: {
48
+ readonly 'v=spf1 include:': "spf:";
49
+ readonly 'v=spf1 ': "spf1:";
50
+ readonly 'google-site-verification=': "gsv:";
51
+ readonly 'MS=ms': "ms";
52
+ readonly 'zoho-verification=': "zv:";
53
+ readonly 'mailgun-verification=': "mv:";
54
+ };
55
+ /**
56
+ * Compress TXT patterns by removing common prefixes
57
+ */
58
+ export declare function compressTxtPattern(pattern: string): string;
59
+ /**
60
+ * Decompress TXT patterns by restoring prefixes
61
+ */
62
+ export declare function decompressTxtPattern(compressed: string): string;
63
+ /**
64
+ * Validation schema for providers
65
+ */
66
+ export declare function validateProvider(provider: Provider): string[];
package/dist/schema.js ADDED
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ /**
3
+ * Provider Data Schema
4
+ *
5
+ * This represents the compressed format for provider data
6
+ * designed to reduce file size and improve parsing performance.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.TXT_PATTERN_COMPRESSION = void 0;
10
+ exports.compressTxtPattern = compressTxtPattern;
11
+ exports.decompressTxtPattern = decompressTxtPattern;
12
+ exports.validateProvider = validateProvider;
13
+ /**
14
+ * Common TXT pattern prefixes that can be compressed
15
+ */
16
+ exports.TXT_PATTERN_COMPRESSION = {
17
+ 'v=spf1 include:': 'spf:',
18
+ 'v=spf1 ': 'spf1:',
19
+ 'google-site-verification=': 'gsv:',
20
+ 'MS=ms': 'ms',
21
+ 'zoho-verification=': 'zv:',
22
+ 'mailgun-verification=': 'mv:'
23
+ };
24
+ /**
25
+ * Compress TXT patterns by removing common prefixes
26
+ */
27
+ function compressTxtPattern(pattern) {
28
+ for (const [prefix, compressed] of Object.entries(exports.TXT_PATTERN_COMPRESSION)) {
29
+ if (pattern.startsWith(prefix)) {
30
+ return compressed + pattern.substring(prefix.length);
31
+ }
32
+ }
33
+ return pattern;
34
+ }
35
+ /**
36
+ * Decompress TXT patterns by restoring prefixes
37
+ */
38
+ function decompressTxtPattern(compressed) {
39
+ for (const [prefix, compressedPrefix] of Object.entries(exports.TXT_PATTERN_COMPRESSION)) {
40
+ if (compressed.startsWith(compressedPrefix)) {
41
+ return prefix + compressed.substring(compressedPrefix.length);
42
+ }
43
+ }
44
+ return compressed;
45
+ }
46
+ /**
47
+ * Validation schema for providers
48
+ */
49
+ function validateProvider(provider) {
50
+ const errors = [];
51
+ if (!provider.id || typeof provider.id !== 'string') {
52
+ errors.push('Provider ID is required and must be a string');
53
+ }
54
+ if (!provider.name || typeof provider.name !== 'string') {
55
+ errors.push('Provider name is required and must be a string');
56
+ }
57
+ if (!provider.url || typeof provider.url !== 'string') {
58
+ errors.push('Provider URL is required and must be a string');
59
+ }
60
+ else if (!provider.url.startsWith('https://')) {
61
+ errors.push('Provider URL must use HTTPS');
62
+ }
63
+ if (provider.domains && !Array.isArray(provider.domains)) {
64
+ errors.push('Domains must be an array');
65
+ }
66
+ if (provider.mx && !Array.isArray(provider.mx)) {
67
+ errors.push('MX patterns must be an array');
68
+ }
69
+ if (provider.txt && !Array.isArray(provider.txt)) {
70
+ errors.push('TXT patterns must be an array');
71
+ }
72
+ return errors;
73
+ }
@@ -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': 'f77814bf0537019c6f38bf2744ae21640f04a2d39cb67c5116f6e03160c9486f',
31
31
  // You can add hashes for other critical files
32
- 'package.json': 'ba2810fd51f2d044890119e6f49926b80d822a4cafedaff25e3f91ba8ec65c2f'
32
+ 'package.json': '874ac6e2398fcc48e373bd2f7d06a7bd861725c5a299d5d1b6beac92490d037d'
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.8.0",
4
+ "description": "TypeScript library for email provider detection with 93 providers (178 domains), concurrent DNS resolution, optimized performance, 91.75% 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": [
@@ -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"