@mikkelscheike/email-provider-links 5.1.0 → 5.1.1

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
@@ -100,41 +100,85 @@ Fully compatible with the latest Node.js 24.x and 25.x! The library is tested on
100
100
 
101
101
  ### Core Functions
102
102
 
103
- #### `getEmailProvider(email, timeout?)`
103
+ #### `getEmailProvider(email, options?)`
104
104
  **Recommended** - Complete provider detection with business domain support.
105
105
 
106
106
  **✨ Automatic Email Normalization**: The returned `email` field is automatically normalized using provider-specific alias rules. For example, `user+tag@gmail.com` returns `user@gmail.com` in the result.
107
107
 
108
+ **📦 Simplified Response (Default)**: By default, returns only essential fields for frontend use. Use `{ extended: true }` to get full provider details including domains array and alias configuration.
109
+
108
110
  Error notes:
109
111
  - `INVALID_EMAIL` is returned for common malformed inputs (e.g. missing `@`, missing TLD).
110
112
  - `IDN_VALIDATION_ERROR` is reserved for true encoding issues.
111
113
 
112
114
  ```typescript
113
- // Known providers (instant response)
115
+ // Default: Simplified response (recommended for frontend)
114
116
  const result1 = await getEmailProvider('user@gmail.com');
115
- // Returns: { provider: "Gmail", email: "user@gmail.com", loginUrl: "https://mail.google.com/mail/" }
117
+ // Returns: {
118
+ // provider: { companyProvider: "Gmail", loginUrl: "https://mail.google.com/mail/", type: "public_provider" },
119
+ // email: "user@gmail.com",
120
+ // detectionMethod: "domain_match"
121
+ // }
116
122
 
117
123
  // Email normalization is automatic
118
124
  const result2 = await getEmailProvider('user+tag@gmail.com');
119
- // Returns: { provider: "Gmail", email: "user@gmail.com", loginUrl: "https://mail.google.com/mail/" }
125
+ // Returns: {
126
+ // provider: { companyProvider: "Gmail", loginUrl: "https://mail.google.com/mail/", type: "public_provider" },
127
+ // email: "user@gmail.com", // Normalized
128
+ // detectionMethod: "domain_match"
129
+ // }
130
+
131
+ // Extended response (includes domains, alias config, etc.)
132
+ const extended = await getEmailProvider('user@gmail.com', { extended: true });
133
+ // Returns: {
134
+ // provider: {
135
+ // companyProvider: "Gmail",
136
+ // loginUrl: "https://mail.google.com/mail/",
137
+ // domains: ["gmail.com", "googlemail.com"], // Only in extended
138
+ // alias: { dots: { ignore: true, strip: false }, ... }, // Only in extended
139
+ // type: "public_provider"
140
+ // },
141
+ // email: "user@gmail.com",
142
+ // loginUrl: "https://mail.google.com/mail/", // Top-level loginUrl only in extended
143
+ // detectionMethod: "domain_match"
144
+ // }
120
145
 
121
146
  // Business domains (DNS lookup with timeout)
122
- const result3 = await getEmailProvider('user@company.com', 2000);
123
- // Returns: { provider: "Google Workspace", email: "user@company.com", detectionMethod: "mx_record" }
147
+ const result3 = await getEmailProvider('user@company.com', { timeout: 2000 });
148
+ // Returns: {
149
+ // provider: { companyProvider: "Google Workspace", loginUrl: "...", type: "custom_provider" },
150
+ // email: "user@company.com",
151
+ // detectionMethod: "mx_record"
152
+ // }
124
153
  ```
125
154
 
126
- #### `getEmailProviderSync(email)`
155
+ #### `getEmailProviderSync(email, options?)`
127
156
  **Fast** - Instant checks for known providers (no DNS lookup).
128
157
 
129
158
  **✨ Automatic Email Normalization**: The returned `email` field is automatically normalized using provider-specific alias rules.
130
159
 
160
+ **📦 Simplified Response (Default)**: By default, returns only essential fields. Use `{ extended: true }` to get full provider details.
161
+
131
162
  ```typescript
163
+ // Default: Simplified response
132
164
  const result = getEmailProviderSync('user@outlook.com');
133
- // Returns: { provider: "Outlook", email: "user@outlook.com", loginUrl: "https://outlook.live.com/" }
165
+ // Returns: {
166
+ // provider: { companyProvider: "Outlook", loginUrl: "https://outlook.live.com/", type: "public_provider" },
167
+ // email: "user@outlook.com",
168
+ // detectionMethod: "domain_match"
169
+ // }
134
170
 
135
171
  // Email normalization is automatic
136
172
  const result2 = getEmailProviderSync('u.s.e.r+tag@gmail.com');
137
- // Returns: { provider: "Gmail", email: "user@gmail.com", loginUrl: "https://mail.google.com/mail/" }
173
+ // Returns: {
174
+ // provider: { companyProvider: "Gmail", loginUrl: "https://mail.google.com/mail/", type: "public_provider" },
175
+ // email: "user@gmail.com", // Normalized
176
+ // detectionMethod: "domain_match"
177
+ // }
178
+
179
+ // Extended response (includes domains, alias config, etc.)
180
+ const extended = getEmailProviderSync('user@gmail.com', { extended: true });
181
+ // Returns full provider object with domains array and alias configuration
138
182
  ```
139
183
 
140
184
  ### Email Alias Support
@@ -171,24 +215,83 @@ async function handlePasswordReset(email: string) {
171
215
  throw new Error(`Invalid email: ${validation.error?.message}`);
172
216
  }
173
217
 
174
- // Get provider information (email is automatically normalized in result)
218
+ // Get provider information (default: simplified response)
219
+ // Email is automatically normalized in result
175
220
  const result = await getEmailProvider(email);
176
221
 
177
222
  return {
178
- providerUrl: result.loginUrl,
223
+ providerUrl: result.provider?.loginUrl || null, // Access loginUrl from provider object
179
224
  providerName: result.provider?.companyProvider || null,
180
225
  normalizedEmail: result.email, // Already normalized (e.g., 'user@gmail.com' from 'user+tag@gmail.com')
181
226
  isSupported: result.provider !== null,
182
227
  detectionMethod: result.detectionMethod
183
228
  };
184
229
  }
230
+
231
+ // If you need full provider details (domains, alias config, etc.)
232
+ async function analyzeEmailProvider(email: string) {
233
+ const result = await getEmailProvider(email, { extended: true });
234
+
235
+ // Access full provider details
236
+ if (result.provider) {
237
+ console.log('All domains:', result.provider.domains);
238
+ console.log('Alias rules:', result.provider.alias);
239
+ }
240
+
241
+ return result;
242
+ }
243
+ ```
244
+
245
+ ## Response Formats
246
+
247
+ ### Simplified Response (Default)
248
+ The default response includes only essential fields needed by most applications:
249
+
250
+ ```typescript
251
+ {
252
+ provider: {
253
+ companyProvider: "Gmail", // Provider name
254
+ loginUrl: "https://mail.google.com/mail/", // Login URL (access via provider.loginUrl)
255
+ type: "public_provider" // Provider type
256
+ },
257
+ email: "user@gmail.com", // Normalized email
258
+ detectionMethod: "domain_match" // How provider was detected
259
+ }
260
+ ```
261
+
262
+ ### Extended Response
263
+ Use `{ extended: true }` to get full provider details including internal metadata:
264
+
265
+ ```typescript
266
+ {
267
+ provider: {
268
+ companyProvider: "Gmail",
269
+ loginUrl: "https://mail.google.com/mail/",
270
+ domains: ["gmail.com", "googlemail.com"], // All domains for this provider
271
+ alias: { // Alias handling configuration
272
+ dots: { ignore: true, strip: false },
273
+ plus: { ignore: true, strip: true },
274
+ case: { ignore: true, strip: true }
275
+ },
276
+ type: "public_provider",
277
+ customDomainDetection: { ... } // DNS detection patterns
278
+ },
279
+ email: "user@gmail.com",
280
+ loginUrl: "https://mail.google.com/mail/",
281
+ detectionMethod: "domain_match"
282
+ }
185
283
  ```
186
284
 
285
+ **When to use Extended**: Only use `{ extended: true }` if you need access to the `domains` array or `alias` configuration for custom email processing logic. For most frontend use cases, the default simplified response is sufficient and more efficient.
286
+
187
287
  ## Configuration
188
288
 
189
289
  ```typescript
190
290
  // Custom DNS timeout (default: 5000ms)
191
- const result = await getEmailProvider(email, 2000);
291
+ const result = await getEmailProvider(email, { timeout: 2000 });
292
+
293
+ // Extended response with custom timeout
294
+ const extended = await getEmailProvider(email, { timeout: 2000, extended: true });
192
295
 
193
296
  // Rate limiting configuration
194
297
  import { Config } from '@mikkelscheike/email-provider-links';
@@ -215,7 +318,7 @@ console.log(`Total providers: ${providers.length}`);
215
318
 
216
319
  ### Email Alias Detection & Normalization
217
320
 
218
- **✨ Note**: `getEmailProvider()`, `getEmailProviderSync()`, and `getEmailProviderFast()` automatically normalize emails in their results. You can use `normalizeEmail()` directly when you only need normalization without provider detection.
321
+ **✨ Note**: `getEmailProvider()`, `getEmailProviderSync()`, and `getEmailProviderFast()` automatically normalize emails in their results and return simplified responses by default (only essential fields). Use `{ extended: true }` to get full provider details. You can use `normalizeEmail()` directly when you only need normalization without provider detection.
219
322
 
220
323
  ```typescript
221
324
  import {
package/dist/api.d.ts CHANGED
@@ -30,7 +30,21 @@ export interface EmailProvider {
30
30
  };
31
31
  }
32
32
  /**
33
- * Enhanced result interface with rich error context
33
+ * Simplified provider information for frontend use
34
+ * Contains only essential fields needed by consumers
35
+ */
36
+ export interface SimplifiedProvider {
37
+ /** The provider name (e.g., "Gmail", "ProtonMail") */
38
+ companyProvider: string;
39
+ /** Direct URL to the email provider's login page */
40
+ loginUrl: string | null;
41
+ /** Provider type for UI differentiation */
42
+ type: ProviderType;
43
+ }
44
+ /**
45
+ * Extended result interface with full provider details
46
+ * Includes internal implementation details like domains array and alias configuration.
47
+ * Use this when you need access to all provider metadata.
34
48
  */
35
49
  export interface EmailProviderResult {
36
50
  /** The detected email provider, or null if not found */
@@ -51,6 +65,26 @@ export interface EmailProviderResult {
51
65
  idnError?: string;
52
66
  };
53
67
  }
68
+ /**
69
+ * Standard result interface (default)
70
+ * Contains only essential information needed by consumers.
71
+ * This is the default response format for all API functions.
72
+ */
73
+ export interface SimplifiedEmailProviderResult {
74
+ /** The detected email provider (simplified), or null if not found */
75
+ provider: SimplifiedProvider | null;
76
+ /** The normalized email address */
77
+ email: string;
78
+ /** Method used to detect the provider */
79
+ detectionMethod?: 'domain_match' | 'mx_record' | 'txt_record' | 'both' | 'proxy_detected';
80
+ /** Error information if detection failed */
81
+ error?: {
82
+ type: 'INVALID_EMAIL' | 'DNS_TIMEOUT' | 'RATE_LIMITED' | 'UNKNOWN_DOMAIN' | 'NETWORK_ERROR' | 'IDN_VALIDATION_ERROR';
83
+ message: string;
84
+ retryAfter?: number;
85
+ idnError?: string;
86
+ };
87
+ }
54
88
  /**
55
89
  * Get email provider information for any email address.
56
90
  *
@@ -59,16 +93,22 @@ export interface EmailProviderResult {
59
93
  * - Business domains (mycompany.com using Google Workspace, etc.)
60
94
  * - Unknown providers (graceful fallback)
61
95
  *
96
+ * By default, returns a simplified response with only essential fields.
97
+ * Use the `extended` option to get full provider details including domains and alias configuration.
98
+ *
62
99
  * @param email - The email address to analyze
63
- * @param timeout - Optional timeout for DNS queries in milliseconds (default: 5000ms)
64
- * @returns Promise resolving to EmailProviderResult with provider info and error context
100
+ * @param options - Optional configuration: timeout for DNS queries (default: 5000ms) and extended response flag
101
+ * @returns Promise resolving to SimplifiedEmailProviderResult (default) or EmailProviderResult (if extended)
65
102
  *
66
103
  * @example
67
104
  * ```typescript
68
- * // Consumer email
69
- * const result = await getEmailProvider('local@domain.tld');
70
- * console.log(result.provider?.companyProvider); // Provider name
71
- * console.log(result.loginUrl); // Login URL
105
+ * // Default: Simplified response (recommended for frontend)
106
+ * const result = await getEmailProvider('user@gmail.com');
107
+ * // Returns: { provider: { companyProvider, loginUrl, type }, email, loginUrl, detectionMethod }
108
+ *
109
+ * // Extended response (includes domains, alias config, etc.)
110
+ * const extended = await getEmailProvider('user@gmail.com', { extended: true });
111
+ * // Returns: { provider: { companyProvider, loginUrl, domains, alias, type, ... }, ... }
72
112
  *
73
113
  * // Business domain
74
114
  * const business = await getEmailProvider('local@business.tld');
@@ -81,21 +121,32 @@ export interface EmailProviderResult {
81
121
  * console.log(invalid.error?.message); // "Invalid email format"
82
122
  * ```
83
123
  */
84
- export declare function getEmailProvider(email: string, timeout?: number): Promise<EmailProviderResult>;
124
+ export declare function getEmailProvider(email: string, options?: number | {
125
+ timeout?: number;
126
+ extended?: boolean;
127
+ }): Promise<SimplifiedEmailProviderResult | EmailProviderResult>;
85
128
  /**
86
129
  * Get email provider information synchronously (no DNS lookup).
87
130
  *
88
131
  * This function only checks predefined domains and returns immediately.
89
132
  * Use this when you can't use async functions or don't want DNS lookups.
90
133
  *
134
+ * By default, returns a simplified response with only essential fields.
135
+ * Use the `extended` option to get full provider details including domains and alias configuration.
136
+ *
91
137
  * @param email - The email address to analyze
92
- * @returns EmailProviderResult with provider info (limited to known domains)
138
+ * @param options - Optional configuration: extended response flag
139
+ * @returns SimplifiedEmailProviderResult (default) or EmailProviderResult (if extended) with provider info (limited to known domains)
93
140
  *
94
141
  * @example
95
142
  * ```typescript
96
- * // Works for known domains
143
+ * // Default: Simplified response (recommended for frontend)
97
144
  * const gmail = getEmailProviderSync('user@gmail.com');
98
- * console.log(gmail.provider?.companyProvider); // "Gmail"
145
+ * // Returns: { provider: { companyProvider, loginUrl, type }, email, loginUrl }
146
+ *
147
+ * // Extended response (includes domains, alias config, etc.)
148
+ * const extended = getEmailProviderSync('user@gmail.com', { extended: true });
149
+ * // Returns: { provider: { companyProvider, loginUrl, domains, alias, type, ... }, ... }
99
150
  *
100
151
  * // Unknown domains return null
101
152
  * const unknown = getEmailProviderSync('user@mycompany.com');
@@ -103,34 +154,46 @@ export declare function getEmailProvider(email: string, timeout?: number): Promi
103
154
  * console.log(unknown.error?.type); // "UNKNOWN_DOMAIN"
104
155
  * ```
105
156
  */
106
- export declare function getEmailProviderSync(email: string): EmailProviderResult;
157
+ export declare function getEmailProviderSync(email: string, options?: {
158
+ extended?: boolean;
159
+ }): SimplifiedEmailProviderResult | EmailProviderResult;
107
160
  export { normalizeEmail, emailsMatch } from './alias-detection';
108
161
  /**
109
162
  * Enhanced email provider detection with concurrent DNS for maximum performance.
110
163
  * This function uses parallel MX/TXT lookups for 2x faster business domain detection.
111
164
  *
165
+ * By default, returns a simplified response with only essential fields.
166
+ * Use the `extended` option to get full provider details including domains and alias configuration.
167
+ *
112
168
  * @param email - The email address to analyze
113
169
  * @param options - Configuration options for DNS detection
114
- * @returns Promise resolving to EmailProviderResult with enhanced performance data
170
+ * @returns Promise resolving to SimplifiedEmailProviderResult (default) or EmailProviderResult (if extended) with enhanced performance data
115
171
  *
116
172
  * @example
117
173
  * ```typescript
118
- * // High-performance detection with concurrent DNS
174
+ * // Default: Simplified response with performance data
119
175
  * const result = await getEmailProviderFast('user@mycompany.com', {
120
176
  * enableParallel: true,
121
177
  * collectDebugInfo: true
122
178
  * });
179
+ * // Returns: { provider: { companyProvider, loginUrl, type }, email, loginUrl, detectionMethod, timing, confidence }
123
180
  *
124
- * console.log(result.provider?.companyProvider); // "Google Workspace"
125
- * console.log(result.detectionMethod); // "mx_record"
126
- * console.log(result.timing); // { mx: 120, txt: 95, total: 125 }
181
+ * // Extended response (includes domains, alias config, etc.)
182
+ * const extended = await getEmailProviderFast('user@mycompany.com', {
183
+ * enableParallel: true,
184
+ * extended: true
185
+ * });
186
+ * console.log(extended.provider?.companyProvider); // "Google Workspace"
187
+ * console.log(extended.detectionMethod); // "mx_record"
188
+ * console.log(extended.timing); // { mx: 120, txt: 95, total: 125 }
127
189
  * ```
128
190
  */
129
191
  export declare function getEmailProviderFast(email: string, options?: {
130
192
  timeout?: number;
131
193
  enableParallel?: boolean;
132
194
  collectDebugInfo?: boolean;
133
- }): Promise<EmailProviderResult & {
195
+ extended?: boolean;
196
+ }): Promise<(SimplifiedEmailProviderResult | EmailProviderResult) & {
134
197
  timing?: {
135
198
  mx: number;
136
199
  txt: number;
package/dist/api.js CHANGED
@@ -81,6 +81,18 @@ function validateAndParseEmailForLookup(email) {
81
81
  const domain = (0, idn_1.domainToPunycode)(domainRaw);
82
82
  return { ok: true, trimmedEmail, domain };
83
83
  }
84
+ /**
85
+ * Convert a full EmailProvider to a simplified version
86
+ */
87
+ function simplifyProvider(provider) {
88
+ if (!provider)
89
+ return null;
90
+ return {
91
+ companyProvider: provider.companyProvider,
92
+ loginUrl: provider.loginUrl,
93
+ type: provider.type
94
+ };
95
+ }
84
96
  /**
85
97
  * Get email provider information for any email address.
86
98
  *
@@ -89,16 +101,22 @@ function validateAndParseEmailForLookup(email) {
89
101
  * - Business domains (mycompany.com using Google Workspace, etc.)
90
102
  * - Unknown providers (graceful fallback)
91
103
  *
104
+ * By default, returns a simplified response with only essential fields.
105
+ * Use the `extended` option to get full provider details including domains and alias configuration.
106
+ *
92
107
  * @param email - The email address to analyze
93
- * @param timeout - Optional timeout for DNS queries in milliseconds (default: 5000ms)
94
- * @returns Promise resolving to EmailProviderResult with provider info and error context
108
+ * @param options - Optional configuration: timeout for DNS queries (default: 5000ms) and extended response flag
109
+ * @returns Promise resolving to SimplifiedEmailProviderResult (default) or EmailProviderResult (if extended)
95
110
  *
96
111
  * @example
97
112
  * ```typescript
98
- * // Consumer email
99
- * const result = await getEmailProvider('local@domain.tld');
100
- * console.log(result.provider?.companyProvider); // Provider name
101
- * console.log(result.loginUrl); // Login URL
113
+ * // Default: Simplified response (recommended for frontend)
114
+ * const result = await getEmailProvider('user@gmail.com');
115
+ * // Returns: { provider: { companyProvider, loginUrl, type }, email, loginUrl, detectionMethod }
116
+ *
117
+ * // Extended response (includes domains, alias config, etc.)
118
+ * const extended = await getEmailProvider('user@gmail.com', { extended: true });
119
+ * // Returns: { provider: { companyProvider, loginUrl, domains, alias, type, ... }, ... }
102
120
  *
103
121
  * // Business domain
104
122
  * const business = await getEmailProvider('local@business.tld');
@@ -111,7 +129,10 @@ function validateAndParseEmailForLookup(email) {
111
129
  * console.log(invalid.error?.message); // "Invalid email format"
112
130
  * ```
113
131
  */
114
- async function getEmailProvider(email, timeout) {
132
+ async function getEmailProvider(email, options) {
133
+ // Parse options - support both legacy (number) and new (object) format
134
+ const timeout = typeof options === 'number' ? options : options?.timeout;
135
+ const extended = typeof options === 'object' && options?.extended === true;
115
136
  try {
116
137
  const parsed = validateAndParseEmailForLookup(email);
117
138
  if (!parsed.ok) {
@@ -123,16 +144,17 @@ async function getEmailProvider(email, timeout) {
123
144
  catch {
124
145
  // If normalization fails, use original email
125
146
  }
126
- return {
147
+ const errorResult = {
127
148
  provider: null,
128
149
  email: normalizedEmail,
129
- loginUrl: null,
150
+ ...(extended ? { loginUrl: null } : {}),
130
151
  error: parsed.error
131
152
  };
153
+ return extended ? errorResult : errorResult;
132
154
  }
133
155
  const domain = parsed.domain;
134
156
  // First try synchronous domain matching
135
- const syncResult = getEmailProviderSync(email);
157
+ const syncResult = getEmailProviderSync(email, { extended });
136
158
  if (syncResult.provider) {
137
159
  // Email is already normalized in getEmailProviderSync
138
160
  return {
@@ -143,15 +165,16 @@ async function getEmailProvider(email, timeout) {
143
165
  // Fall back to DNS detection for business domains
144
166
  const loadResult = (0, provider_loader_1.loadProviders)();
145
167
  if (!loadResult.success) {
146
- return {
168
+ const errorResult = {
147
169
  provider: null,
148
170
  email,
149
- loginUrl: null,
171
+ ...(extended ? { loginUrl: null } : {}),
150
172
  error: {
151
173
  type: 'NETWORK_ERROR',
152
174
  message: 'Service temporarily unavailable'
153
175
  }
154
176
  };
177
+ return extended ? errorResult : errorResult;
155
178
  }
156
179
  const providers = loadResult.providers;
157
180
  const concurrentResult = await (0, concurrent_dns_1.detectProviderConcurrent)(domain, providers, {
@@ -168,17 +191,33 @@ async function getEmailProvider(email, timeout) {
168
191
  catch {
169
192
  // If normalization fails, use original email
170
193
  }
194
+ if (extended) {
195
+ const result = {
196
+ provider: concurrentResult.provider,
197
+ email: normalizedEmail,
198
+ loginUrl: concurrentResult.provider?.loginUrl || null,
199
+ detectionMethod: concurrentResult.detectionMethod || 'mx_record'
200
+ };
201
+ if (concurrentResult.proxyService) {
202
+ result.proxyService = concurrentResult.proxyService;
203
+ }
204
+ // Add error context for null results
205
+ if (!result.provider && !result.proxyService) {
206
+ result.error = {
207
+ type: 'UNKNOWN_DOMAIN',
208
+ message: `No email provider found for domain: ${domain}`
209
+ };
210
+ }
211
+ return result;
212
+ }
213
+ // Default: simplified response
171
214
  const result = {
172
- provider: concurrentResult.provider,
215
+ provider: simplifyProvider(concurrentResult.provider),
173
216
  email: normalizedEmail,
174
- loginUrl: concurrentResult.provider?.loginUrl || null,
175
217
  detectionMethod: concurrentResult.detectionMethod || 'mx_record'
176
218
  };
177
- if (concurrentResult.proxyService) {
178
- result.proxyService = concurrentResult.proxyService;
179
- }
180
219
  // Add error context for null results
181
- if (!result.provider && !result.proxyService) {
220
+ if (!result.provider) {
182
221
  result.error = {
183
222
  type: 'UNKNOWN_DOMAIN',
184
223
  message: `No email provider found for domain: ${domain}`
@@ -188,40 +227,36 @@ async function getEmailProvider(email, timeout) {
188
227
  }
189
228
  catch (error) {
190
229
  // Enhanced error handling
230
+ const errorResult = {
231
+ provider: null,
232
+ email,
233
+ error: {}
234
+ };
235
+ if (extended) {
236
+ errorResult.loginUrl = null;
237
+ }
191
238
  if (error instanceof Error && error.message.includes('Rate limit exceeded')) {
192
239
  const retryMatch = error.message.match(/Try again in (\d+) seconds/);
193
240
  const retryAfter = retryMatch?.[1] ? parseInt(retryMatch[1], 10) : undefined;
194
- return {
195
- provider: null,
196
- email,
197
- loginUrl: null,
198
- error: {
199
- type: 'RATE_LIMITED',
200
- message: 'DNS query rate limit exceeded',
201
- ...(retryAfter !== undefined ? { retryAfter } : {})
202
- }
241
+ errorResult.error = {
242
+ type: 'RATE_LIMITED',
243
+ message: 'DNS query rate limit exceeded',
244
+ ...(retryAfter !== undefined ? { retryAfter } : {})
203
245
  };
246
+ return extended ? errorResult : errorResult;
204
247
  }
205
248
  if (error instanceof Error && error.message.includes('timeout')) {
206
- return {
207
- provider: null,
208
- email,
209
- loginUrl: null,
210
- error: {
211
- type: 'DNS_TIMEOUT',
212
- message: `DNS lookup timed out after ${timeout || 5000}ms`
213
- }
249
+ errorResult.error = {
250
+ type: 'DNS_TIMEOUT',
251
+ message: `DNS lookup timed out after ${timeout || 5000}ms`
214
252
  };
253
+ return extended ? errorResult : errorResult;
215
254
  }
216
- return {
217
- provider: null,
218
- email,
219
- loginUrl: null,
220
- error: {
221
- type: 'NETWORK_ERROR',
222
- message: error instanceof Error ? error.message : 'Unknown network error'
223
- }
255
+ errorResult.error = {
256
+ type: 'NETWORK_ERROR',
257
+ message: error instanceof Error ? error.message : 'Unknown network error'
224
258
  };
259
+ return extended ? errorResult : errorResult;
225
260
  }
226
261
  }
227
262
  /**
@@ -230,14 +265,22 @@ async function getEmailProvider(email, timeout) {
230
265
  * This function only checks predefined domains and returns immediately.
231
266
  * Use this when you can't use async functions or don't want DNS lookups.
232
267
  *
268
+ * By default, returns a simplified response with only essential fields.
269
+ * Use the `extended` option to get full provider details including domains and alias configuration.
270
+ *
233
271
  * @param email - The email address to analyze
234
- * @returns EmailProviderResult with provider info (limited to known domains)
272
+ * @param options - Optional configuration: extended response flag
273
+ * @returns SimplifiedEmailProviderResult (default) or EmailProviderResult (if extended) with provider info (limited to known domains)
235
274
  *
236
275
  * @example
237
276
  * ```typescript
238
- * // Works for known domains
277
+ * // Default: Simplified response (recommended for frontend)
239
278
  * const gmail = getEmailProviderSync('user@gmail.com');
240
- * console.log(gmail.provider?.companyProvider); // "Gmail"
279
+ * // Returns: { provider: { companyProvider, loginUrl, type }, email, loginUrl }
280
+ *
281
+ * // Extended response (includes domains, alias config, etc.)
282
+ * const extended = getEmailProviderSync('user@gmail.com', { extended: true });
283
+ * // Returns: { provider: { companyProvider, loginUrl, domains, alias, type, ... }, ... }
241
284
  *
242
285
  * // Unknown domains return null
243
286
  * const unknown = getEmailProviderSync('user@mycompany.com');
@@ -245,7 +288,8 @@ async function getEmailProvider(email, timeout) {
245
288
  * console.log(unknown.error?.type); // "UNKNOWN_DOMAIN"
246
289
  * ```
247
290
  */
248
- function getEmailProviderSync(email) {
291
+ function getEmailProviderSync(email, options) {
292
+ const extended = options?.extended === true;
249
293
  try {
250
294
  const parsed = validateAndParseEmailForLookup(email);
251
295
  if (!parsed.ok) {
@@ -257,12 +301,15 @@ function getEmailProviderSync(email) {
257
301
  catch {
258
302
  // If normalization fails, use original email
259
303
  }
260
- return {
304
+ const errorResult = {
261
305
  provider: null,
262
306
  email: normalizedEmail,
263
- loginUrl: null,
264
307
  error: parsed.error
265
308
  };
309
+ if (extended) {
310
+ errorResult.loginUrl = null;
311
+ }
312
+ return extended ? errorResult : errorResult;
266
313
  }
267
314
  const domain = parsed.domain;
268
315
  // Load providers with verification
@@ -274,15 +321,18 @@ function getEmailProviderSync(email) {
274
321
  if (process.env.NODE_ENV !== 'test' && !process.env.JEST_WORKER_ID) {
275
322
  console.error('🚨 Provider lookup blocked due to validation failure');
276
323
  }
277
- return {
324
+ const errorResult = {
278
325
  provider: null,
279
326
  email,
280
- loginUrl: null,
281
327
  error: {
282
328
  type: 'NETWORK_ERROR',
283
329
  message: 'Service temporarily unavailable'
284
330
  }
285
331
  };
332
+ if (extended) {
333
+ errorResult.loginUrl = null;
334
+ }
335
+ return extended ? errorResult : errorResult;
286
336
  }
287
337
  const domainMap = getDomainMapFromProviders(result.providers);
288
338
  provider = domainMap.get(domain) || null;
@@ -291,15 +341,18 @@ function getEmailProviderSync(email) {
291
341
  if (process.env.NODE_ENV !== 'test' && !process.env.JEST_WORKER_ID) {
292
342
  console.error('🚨 Provider lookup failed:', error);
293
343
  }
294
- return {
344
+ const errorResult = {
295
345
  provider: null,
296
346
  email,
297
- loginUrl: null,
298
347
  error: {
299
348
  type: 'NETWORK_ERROR',
300
349
  message: 'Service temporarily unavailable'
301
350
  }
302
351
  };
352
+ if (extended) {
353
+ errorResult.loginUrl = null;
354
+ }
355
+ return extended ? errorResult : errorResult;
303
356
  }
304
357
  // Normalize email using alias detection (even if no provider found)
305
358
  // This ensures consistent email format regardless of provider detection result
@@ -310,10 +363,26 @@ function getEmailProviderSync(email) {
310
363
  catch {
311
364
  // If normalization fails, use original email
312
365
  }
366
+ if (extended) {
367
+ const result = {
368
+ provider: provider || null,
369
+ email: normalizedEmail,
370
+ loginUrl: provider?.loginUrl || null,
371
+ detectionMethod: 'domain_match'
372
+ };
373
+ // Add error context for null results
374
+ if (!result.provider) {
375
+ result.error = {
376
+ type: 'UNKNOWN_DOMAIN',
377
+ message: `No email provider found for domain: ${domain} (sync mode - business domains not supported)`
378
+ };
379
+ }
380
+ return result;
381
+ }
382
+ // Default: simplified response
313
383
  const result = {
314
- provider: provider || null,
384
+ provider: simplifyProvider(provider),
315
385
  email: normalizedEmail,
316
- loginUrl: provider?.loginUrl || null,
317
386
  detectionMethod: 'domain_match'
318
387
  };
319
388
  // Add error context for null results
@@ -326,15 +395,18 @@ function getEmailProviderSync(email) {
326
395
  return result;
327
396
  }
328
397
  catch (error) {
329
- return {
398
+ const errorResult = {
330
399
  provider: null,
331
400
  email,
332
- loginUrl: null,
333
401
  error: {
334
402
  type: 'INVALID_EMAIL',
335
403
  message: error instanceof Error ? error.message : 'Invalid email address'
336
404
  }
337
405
  };
406
+ if (extended) {
407
+ errorResult.loginUrl = null;
408
+ }
409
+ return extended ? errorResult : errorResult;
338
410
  }
339
411
  }
340
412
  // Re-export alias detection functions from the dedicated module
@@ -345,25 +417,34 @@ Object.defineProperty(exports, "emailsMatch", { enumerable: true, get: function
345
417
  * Enhanced email provider detection with concurrent DNS for maximum performance.
346
418
  * This function uses parallel MX/TXT lookups for 2x faster business domain detection.
347
419
  *
420
+ * By default, returns a simplified response with only essential fields.
421
+ * Use the `extended` option to get full provider details including domains and alias configuration.
422
+ *
348
423
  * @param email - The email address to analyze
349
424
  * @param options - Configuration options for DNS detection
350
- * @returns Promise resolving to EmailProviderResult with enhanced performance data
425
+ * @returns Promise resolving to SimplifiedEmailProviderResult (default) or EmailProviderResult (if extended) with enhanced performance data
351
426
  *
352
427
  * @example
353
428
  * ```typescript
354
- * // High-performance detection with concurrent DNS
429
+ * // Default: Simplified response with performance data
355
430
  * const result = await getEmailProviderFast('user@mycompany.com', {
356
431
  * enableParallel: true,
357
432
  * collectDebugInfo: true
358
433
  * });
434
+ * // Returns: { provider: { companyProvider, loginUrl, type }, email, loginUrl, detectionMethod, timing, confidence }
359
435
  *
360
- * console.log(result.provider?.companyProvider); // "Google Workspace"
361
- * console.log(result.detectionMethod); // "mx_record"
362
- * console.log(result.timing); // { mx: 120, txt: 95, total: 125 }
436
+ * // Extended response (includes domains, alias config, etc.)
437
+ * const extended = await getEmailProviderFast('user@mycompany.com', {
438
+ * enableParallel: true,
439
+ * extended: true
440
+ * });
441
+ * console.log(extended.provider?.companyProvider); // "Google Workspace"
442
+ * console.log(extended.detectionMethod); // "mx_record"
443
+ * console.log(extended.timing); // { mx: 120, txt: 95, total: 125 }
363
444
  * ```
364
445
  */
365
446
  async function getEmailProviderFast(email, options = {}) {
366
- const { timeout = 5000, enableParallel = true, collectDebugInfo = false } = options;
447
+ const { timeout = 5000, enableParallel = true, collectDebugInfo = false, extended = false } = options;
367
448
  try {
368
449
  const parsed = validateAndParseEmailForLookup(email);
369
450
  if (!parsed.ok) {
@@ -377,7 +458,7 @@ async function getEmailProviderFast(email, options = {}) {
377
458
  const domain = parsed.domain;
378
459
  const trimmedEmail = parsed.trimmedEmail;
379
460
  // First try standard domain matching (fast path)
380
- const syncResult = getEmailProviderSync(trimmedEmail);
461
+ const syncResult = getEmailProviderSync(trimmedEmail, { extended });
381
462
  if (syncResult.provider) {
382
463
  // Email is already normalized in getEmailProviderSync
383
464
  return {
@@ -415,34 +496,53 @@ async function getEmailProviderFast(email, options = {}) {
415
496
  catch {
416
497
  // If normalization fails, use original email
417
498
  }
499
+ if (extended) {
500
+ const fastResult = {
501
+ provider: concurrentResult.provider,
502
+ email: normalizedEmail,
503
+ loginUrl: concurrentResult.provider?.loginUrl || null,
504
+ detectionMethod: concurrentResult.detectionMethod || 'mx_record',
505
+ timing: concurrentResult.timing,
506
+ confidence: concurrentResult.confidence,
507
+ debug: concurrentResult.debug,
508
+ error: !concurrentResult.provider && !concurrentResult.proxyService ? {
509
+ type: 'UNKNOWN_DOMAIN',
510
+ message: `No email provider found for domain: ${domain}`
511
+ } : undefined
512
+ };
513
+ if (concurrentResult.proxyService) {
514
+ fastResult.proxyService = concurrentResult.proxyService;
515
+ }
516
+ return fastResult;
517
+ }
518
+ // Default: simplified response
418
519
  const fastResult = {
419
- provider: concurrentResult.provider,
520
+ provider: simplifyProvider(concurrentResult.provider),
420
521
  email: normalizedEmail,
421
- loginUrl: concurrentResult.provider?.loginUrl || null,
422
522
  detectionMethod: concurrentResult.detectionMethod || 'mx_record',
423
523
  timing: concurrentResult.timing,
424
524
  confidence: concurrentResult.confidence,
425
525
  debug: concurrentResult.debug,
426
- error: !concurrentResult.provider && !concurrentResult.proxyService ? {
526
+ error: !concurrentResult.provider ? {
427
527
  type: 'UNKNOWN_DOMAIN',
428
528
  message: `No email provider found for domain: ${domain}`
429
529
  } : undefined
430
530
  };
431
- if (concurrentResult.proxyService) {
432
- fastResult.proxyService = concurrentResult.proxyService;
433
- }
434
531
  return fastResult;
435
532
  }
436
533
  catch (error) {
437
- return {
534
+ const errorResult = {
438
535
  provider: null,
439
536
  email,
440
- loginUrl: null,
441
537
  error: {
442
538
  type: 'NETWORK_ERROR',
443
539
  message: error instanceof Error ? error.message : 'DNS detection failed'
444
540
  }
445
541
  };
542
+ if (extended) {
543
+ errorResult.loginUrl = null;
544
+ }
545
+ return errorResult;
446
546
  }
447
547
  }
448
548
  /**
@@ -29,7 +29,7 @@ const KNOWN_GOOD_HASHES = {
29
29
  // SHA-256 hash of the legitimate emailproviders.json
30
30
  'emailproviders.json': 'a4fe056edad44ae5479cc100d5cc67cb5f6df86e19c4209db6c5f715f5bf070e',
31
31
  // You can add hashes for other critical files
32
- 'package.json': 'ea417a1548c59f3945ea727968c90a43d6a1e24f72c646ff708edd7da2dcc964',
32
+ 'package.json': 'fc7bab75bddc7af13adce8cd3e51f67f597c999eea500bcdc983cd79b60b13c0',
33
33
  };
34
34
  /**
35
35
  * Calculates SHA-256 hash of a file or string content
package/dist/index.d.ts CHANGED
@@ -20,7 +20,7 @@ import { getEmailProvider, getEmailProviderSync, getEmailProviderFast, normalize
20
20
  import { detectProviderConcurrent } from './concurrent-dns';
21
21
  import { validateInternationalEmail, emailToPunycode, domainToPunycode } from './idn';
22
22
  export { getEmailProvider, getEmailProviderSync, getEmailProviderFast, normalizeEmail, emailsMatch, detectEmailAlias, Config };
23
- export type { EmailProvider, EmailProviderResult } from './api';
23
+ export type { EmailProvider, EmailProviderResult, SimplifiedProvider, SimplifiedEmailProviderResult } from './api';
24
24
  export type { AliasDetectionResult } from './alias-detection';
25
25
  export { loadProviders, detectProviderConcurrent, validateInternationalEmail, emailToPunycode, domainToPunycode };
26
26
  export type { ConcurrentDNSConfig, ConcurrentDNSResult } from './concurrent-dns';
package/dist/index.js CHANGED
@@ -304,7 +304,7 @@ function batchProcessEmails(emails, options = {}) {
304
304
  try {
305
305
  const providerResult = (0, api_1.getEmailProviderSync)(validation.normalizedEmail);
306
306
  result.provider = providerResult.provider?.companyProvider || null;
307
- result.loginUrl = providerResult.loginUrl;
307
+ result.loginUrl = providerResult.provider?.loginUrl || null;
308
308
  }
309
309
  catch {
310
310
  result.provider = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikkelscheike/email-provider-links",
3
- "version": "5.1.0",
3
+ "version": "5.1.1",
4
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",