@mikkelscheike/email-provider-links 4.0.11 → 5.1.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/api.js CHANGED
@@ -6,14 +6,81 @@
6
6
  * Clean function names and enhanced error context.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.Config = void 0;
9
+ exports.Config = exports.emailsMatch = exports.normalizeEmail = void 0;
10
10
  exports.getEmailProvider = getEmailProvider;
11
11
  exports.getEmailProviderSync = getEmailProviderSync;
12
- exports.normalizeEmail = normalizeEmail;
13
- exports.emailsMatch = emailsMatch;
14
12
  exports.getEmailProviderFast = getEmailProviderFast;
15
13
  const concurrent_dns_1 = require("./concurrent-dns");
16
14
  const provider_loader_1 = require("./provider-loader");
15
+ const idn_1 = require("./idn");
16
+ const alias_detection_1 = require("./alias-detection");
17
+ let cachedProvidersRef = null;
18
+ let cachedDomainMap = null;
19
+ function getDomainMapFromProviders(providers) {
20
+ if (cachedProvidersRef === providers && cachedDomainMap) {
21
+ return cachedDomainMap;
22
+ }
23
+ const domainMap = new Map();
24
+ for (const loadedProvider of providers) {
25
+ for (const domain of loadedProvider.domains) {
26
+ domainMap.set(domain.toLowerCase(), loadedProvider);
27
+ }
28
+ }
29
+ cachedProvidersRef = providers;
30
+ cachedDomainMap = domainMap;
31
+ return domainMap;
32
+ }
33
+ function validateAndParseEmailForLookup(email) {
34
+ if (!email || typeof email !== 'string') {
35
+ return {
36
+ ok: false,
37
+ email: email || '',
38
+ error: {
39
+ type: 'INVALID_EMAIL',
40
+ message: 'Email address is required and must be a string'
41
+ }
42
+ };
43
+ }
44
+ const trimmedEmail = email.trim();
45
+ // Strict validation: treat any IDN validation failure as invalid input.
46
+ // Only surface IDN_VALIDATION_ERROR for true encoding issues.
47
+ const idnError = (0, idn_1.validateInternationalEmail)(trimmedEmail);
48
+ if (idnError) {
49
+ if (idnError.code === idn_1.IDNValidationError.INVALID_ENCODING) {
50
+ return {
51
+ ok: false,
52
+ email: trimmedEmail,
53
+ error: {
54
+ type: 'IDN_VALIDATION_ERROR',
55
+ message: idnError.message,
56
+ idnError: idnError.code
57
+ }
58
+ };
59
+ }
60
+ return {
61
+ ok: false,
62
+ email: trimmedEmail,
63
+ error: {
64
+ type: 'INVALID_EMAIL',
65
+ message: 'Invalid email format'
66
+ }
67
+ };
68
+ }
69
+ const atIndex = trimmedEmail.lastIndexOf('@');
70
+ if (atIndex === -1) {
71
+ return {
72
+ ok: false,
73
+ email: trimmedEmail,
74
+ error: {
75
+ type: 'INVALID_EMAIL',
76
+ message: 'Invalid email format'
77
+ }
78
+ };
79
+ }
80
+ const domainRaw = trimmedEmail.slice(atIndex + 1).toLowerCase();
81
+ const domain = (0, idn_1.domainToPunycode)(domainRaw);
82
+ return { ok: true, trimmedEmail, domain };
83
+ }
17
84
  /**
18
85
  * Get email provider information for any email address.
19
86
  *
@@ -46,46 +113,28 @@ const provider_loader_1 = require("./provider-loader");
46
113
  */
47
114
  async function getEmailProvider(email, timeout) {
48
115
  try {
49
- // Input validation
50
- if (!email || typeof email !== 'string') {
51
- return {
52
- provider: null,
53
- email: email || '',
54
- 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) {
116
+ const parsed = validateAndParseEmailForLookup(email);
117
+ if (!parsed.ok) {
118
+ // Try to normalize even invalid emails (may help with some edge cases)
119
+ let normalizedEmail = parsed.email;
120
+ try {
121
+ normalizedEmail = (0, alias_detection_1.normalizeEmail)(parsed.email);
122
+ }
123
+ catch {
124
+ // If normalization fails, use original email
125
+ }
76
126
  return {
77
127
  provider: null,
78
- email,
128
+ email: normalizedEmail,
79
129
  loginUrl: null,
80
- error: {
81
- type: 'INVALID_EMAIL',
82
- message: 'Invalid email format - missing domain'
83
- }
130
+ error: parsed.error
84
131
  };
85
132
  }
133
+ const domain = parsed.domain;
86
134
  // First try synchronous domain matching
87
135
  const syncResult = getEmailProviderSync(email);
88
136
  if (syncResult.provider) {
137
+ // Email is already normalized in getEmailProviderSync
89
138
  return {
90
139
  ...syncResult,
91
140
  detectionMethod: 'domain_match'
@@ -110,9 +159,18 @@ async function getEmailProvider(email, timeout) {
110
159
  enableParallel: true,
111
160
  collectDebugInfo: false
112
161
  });
162
+ // Normalize email using alias detection (even if no provider found)
163
+ // This ensures consistent email format regardless of provider detection result
164
+ let normalizedEmail = email;
165
+ try {
166
+ normalizedEmail = (0, alias_detection_1.normalizeEmail)(email);
167
+ }
168
+ catch {
169
+ // If normalization fails, use original email
170
+ }
113
171
  const result = {
114
172
  provider: concurrentResult.provider,
115
- email,
173
+ email: normalizedEmail,
116
174
  loginUrl: concurrentResult.provider?.loginUrl || null,
117
175
  detectionMethod: concurrentResult.detectionMethod || 'mx_record'
118
176
  };
@@ -130,9 +188,9 @@ async function getEmailProvider(email, timeout) {
130
188
  }
131
189
  catch (error) {
132
190
  // Enhanced error handling
133
- if (error.message?.includes('Rate limit exceeded')) {
191
+ if (error instanceof Error && error.message.includes('Rate limit exceeded')) {
134
192
  const retryMatch = error.message.match(/Try again in (\d+) seconds/);
135
- const retryAfter = retryMatch ? parseInt(retryMatch[1], 10) : undefined;
193
+ const retryAfter = retryMatch?.[1] ? parseInt(retryMatch[1], 10) : undefined;
136
194
  return {
137
195
  provider: null,
138
196
  email,
@@ -144,7 +202,7 @@ async function getEmailProvider(email, timeout) {
144
202
  }
145
203
  };
146
204
  }
147
- if (error.message?.includes('timeout')) {
205
+ if (error instanceof Error && error.message.includes('timeout')) {
148
206
  return {
149
207
  provider: null,
150
208
  email,
@@ -161,7 +219,7 @@ async function getEmailProvider(email, timeout) {
161
219
  loginUrl: null,
162
220
  error: {
163
221
  type: 'NETWORK_ERROR',
164
- message: error.message || 'Unknown network error'
222
+ message: error instanceof Error ? error.message : 'Unknown network error'
165
223
  }
166
224
  };
167
225
  }
@@ -189,44 +247,24 @@ async function getEmailProvider(email, timeout) {
189
247
  */
190
248
  function getEmailProviderSync(email) {
191
249
  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)) {
207
- return {
208
- provider: null,
209
- email,
210
- 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) {
250
+ const parsed = validateAndParseEmailForLookup(email);
251
+ if (!parsed.ok) {
252
+ // Try to normalize even invalid emails (may help with some edge cases)
253
+ let normalizedEmail = parsed.email;
254
+ try {
255
+ normalizedEmail = (0, alias_detection_1.normalizeEmail)(parsed.email);
256
+ }
257
+ catch {
258
+ // If normalization fails, use original email
259
+ }
220
260
  return {
221
261
  provider: null,
222
- email,
262
+ email: normalizedEmail,
223
263
  loginUrl: null,
224
- error: {
225
- type: 'INVALID_EMAIL',
226
- message: 'Invalid email format - missing domain'
227
- }
264
+ error: parsed.error
228
265
  };
229
266
  }
267
+ const domain = parsed.domain;
230
268
  // Load providers with verification
231
269
  let provider = null;
232
270
  try {
@@ -246,13 +284,7 @@ function getEmailProviderSync(email) {
246
284
  }
247
285
  };
248
286
  }
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
- }
287
+ const domainMap = getDomainMapFromProviders(result.providers);
256
288
  provider = domainMap.get(domain) || null;
257
289
  }
258
290
  catch (error) {
@@ -269,9 +301,18 @@ function getEmailProviderSync(email) {
269
301
  }
270
302
  };
271
303
  }
304
+ // Normalize email using alias detection (even if no provider found)
305
+ // This ensures consistent email format regardless of provider detection result
306
+ let normalizedEmail = email;
307
+ try {
308
+ normalizedEmail = (0, alias_detection_1.normalizeEmail)(email);
309
+ }
310
+ catch {
311
+ // If normalization fails, use original email
312
+ }
272
313
  const result = {
273
314
  provider: provider || null,
274
- email,
315
+ email: normalizedEmail,
275
316
  loginUrl: provider?.loginUrl || null,
276
317
  detectionMethod: 'domain_match'
277
318
  };
@@ -291,105 +332,15 @@ function getEmailProviderSync(email) {
291
332
  loginUrl: null,
292
333
  error: {
293
334
  type: 'INVALID_EMAIL',
294
- message: error.message || 'Invalid email address'
335
+ message: error instanceof Error ? error.message : 'Invalid email address'
295
336
  }
296
337
  };
297
338
  }
298
339
  }
299
- /**
300
- * Normalize an email address to its canonical form.
301
- *
302
- * This handles provider-specific aliasing rules:
303
- * - Gmail: removes dots and plus addressing
304
- * - Other providers: removes plus addressing only
305
- *
306
- * @param email - The email address to normalize
307
- * @returns The canonical email address
308
- *
309
- * @example
310
- * ```typescript
311
- * const canonical = normalizeEmail('L.O.C.A.L+work@DOMAIN.TLD');
312
- * console.log(canonical); // 'local@domain.tld'
313
- *
314
- * const provider = normalizeEmail('local+newsletter@provider.tld');
315
- * console.log(provider); // 'local@provider.tld'
316
- * ```
317
- */
318
- function normalizeEmail(email) {
319
- if (!email || typeof email !== 'string') {
320
- return email;
321
- }
322
- // Convert to lowercase
323
- const lowercaseEmail = email.toLowerCase().trim();
324
- // Split email into local and domain parts
325
- const atIndex = lowercaseEmail.lastIndexOf('@');
326
- if (atIndex === -1) {
327
- return lowercaseEmail;
328
- }
329
- let localPart = lowercaseEmail.slice(0, atIndex);
330
- const domainPart = lowercaseEmail.slice(atIndex + 1);
331
- // Use providers for domain lookup
332
- let provider = null;
333
- try {
334
- const result = (0, provider_loader_1.loadProviders)();
335
- if (!result.success) {
336
- return lowercaseEmail; // Return as-is if providers can't be loaded
337
- }
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
- }
344
- provider = domainMap.get(domainPart) || null;
345
- }
346
- catch (error) {
347
- return lowercaseEmail; // Return as-is if error occurs
348
- }
349
- if (provider?.alias) {
350
- // Provider supports aliasing
351
- if (provider.alias.dots) {
352
- // Remove all dots from local part (e.g. Gmail)
353
- localPart = localPart.replace(/\./g, '');
354
- }
355
- if (provider.alias.plus) {
356
- // Remove plus addressing (everything after +)
357
- const plusIndex = localPart.indexOf('+');
358
- if (plusIndex !== -1) {
359
- localPart = localPart.slice(0, plusIndex);
360
- }
361
- }
362
- }
363
- return `${localPart}@${domainPart}`;
364
- }
365
- /**
366
- * Check if two email addresses are the same person (accounting for aliases).
367
- *
368
- * This normalizes both emails and compares their canonical forms.
369
- * Useful for preventing duplicate accounts and matching login attempts.
370
- *
371
- * @param email1 - First email address
372
- * @param email2 - Second email address
373
- * @returns true if the emails represent the same person
374
- *
375
- * @example
376
- * ```typescript
377
- * const match = emailsMatch('local@domain.tld', 'l.o.c.a.l+work@domain.tld');
378
- * console.log(match); // true
379
- *
380
- * const different = emailsMatch('local@domain.tld', 'other@domain.tld');
381
- * console.log(different); // false
382
- * ```
383
- */
384
- function emailsMatch(email1, email2) {
385
- if (!email1 || !email2 || typeof email1 !== 'string' || typeof email2 !== 'string') {
386
- return false;
387
- }
388
- // Normalize both emails and compare
389
- const normalized1 = normalizeEmail(email1);
390
- const normalized2 = normalizeEmail(email2);
391
- return normalized1 === normalized2;
392
- }
340
+ // Re-export alias detection functions from the dedicated module
341
+ var alias_detection_2 = require("./alias-detection");
342
+ Object.defineProperty(exports, "normalizeEmail", { enumerable: true, get: function () { return alias_detection_2.normalizeEmail; } });
343
+ Object.defineProperty(exports, "emailsMatch", { enumerable: true, get: function () { return alias_detection_2.emailsMatch; } });
393
344
  /**
394
345
  * Enhanced email provider detection with concurrent DNS for maximum performance.
395
346
  * This function uses parallel MX/TXT lookups for 2x faster business domain detection.
@@ -414,46 +365,21 @@ function emailsMatch(email1, email2) {
414
365
  async function getEmailProviderFast(email, options = {}) {
415
366
  const { timeout = 5000, enableParallel = true, collectDebugInfo = false } = options;
416
367
  try {
417
- // Input validation
418
- if (!email || typeof email !== 'string') {
368
+ const parsed = validateAndParseEmailForLookup(email);
369
+ if (!parsed.ok) {
419
370
  return {
420
371
  provider: null,
421
- email: email || '',
372
+ email: parsed.email,
422
373
  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) {
444
- return {
445
- provider: null,
446
- email,
447
- loginUrl: null,
448
- error: {
449
- type: 'INVALID_EMAIL',
450
- message: 'Invalid email format - missing domain'
451
- }
374
+ error: parsed.error
452
375
  };
453
376
  }
377
+ const domain = parsed.domain;
378
+ const trimmedEmail = parsed.trimmedEmail;
454
379
  // First try standard domain matching (fast path)
455
- const syncResult = getEmailProviderSync(email);
380
+ const syncResult = getEmailProviderSync(trimmedEmail);
456
381
  if (syncResult.provider) {
382
+ // Email is already normalized in getEmailProviderSync
457
383
  return {
458
384
  ...syncResult,
459
385
  detectionMethod: 'domain_match',
@@ -466,7 +392,7 @@ async function getEmailProviderFast(email, options = {}) {
466
392
  if (!result.success) {
467
393
  return {
468
394
  provider: null,
469
- email,
395
+ email: trimmedEmail,
470
396
  loginUrl: null,
471
397
  error: {
472
398
  type: 'NETWORK_ERROR',
@@ -480,9 +406,18 @@ async function getEmailProviderFast(email, options = {}) {
480
406
  enableParallel,
481
407
  collectDebugInfo
482
408
  });
409
+ // Normalize email using alias detection (even if no provider found)
410
+ // This ensures consistent email format regardless of provider detection result
411
+ let normalizedEmail = trimmedEmail;
412
+ try {
413
+ normalizedEmail = (0, alias_detection_1.normalizeEmail)(trimmedEmail);
414
+ }
415
+ catch {
416
+ // If normalization fails, use original email
417
+ }
483
418
  const fastResult = {
484
419
  provider: concurrentResult.provider,
485
- email,
420
+ email: normalizedEmail,
486
421
  loginUrl: concurrentResult.provider?.loginUrl || null,
487
422
  detectionMethod: concurrentResult.detectionMethod || 'mx_record',
488
423
  timing: concurrentResult.timing,
@@ -505,7 +440,7 @@ async function getEmailProviderFast(email, options = {}) {
505
440
  loginUrl: null,
506
441
  error: {
507
442
  type: 'NETWORK_ERROR',
508
- message: error.message || 'DNS detection failed'
443
+ message: error instanceof Error ? error.message : 'DNS detection failed'
509
444
  }
510
445
  };
511
446
  }
@@ -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;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Email validation constants based on RFC 5321
3
+ *
4
+ * These limits are defined in RFC 5321 Section 4.5.3.1:
5
+ * - Maximum total email length: 254 characters
6
+ * - Maximum local part length: 64 characters
7
+ * - Maximum domain length: 253 characters
8
+ */
9
+ export declare const EmailLimits: {
10
+ /** Maximum total email address length (local + @ + domain) */
11
+ readonly MAX_EMAIL_LENGTH: 254;
12
+ /** Maximum local part length (before @) */
13
+ readonly MAX_LOCAL_PART_LENGTH: 64;
14
+ /** Maximum domain length (after @) */
15
+ readonly MAX_DOMAIN_LENGTH: 253;
16
+ };
17
+ /**
18
+ * Memory calculation constants
19
+ */
20
+ export declare const MemoryConstants: {
21
+ /** Bytes per kilobyte */
22
+ readonly BYTES_PER_KB: 1024;
23
+ /** Kilobytes per megabyte */
24
+ readonly KB_PER_MB: 1024;
25
+ };
26
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryConstants = exports.EmailLimits = void 0;
4
+ /**
5
+ * Email validation constants based on RFC 5321
6
+ *
7
+ * These limits are defined in RFC 5321 Section 4.5.3.1:
8
+ * - Maximum total email length: 254 characters
9
+ * - Maximum local part length: 64 characters
10
+ * - Maximum domain length: 253 characters
11
+ */
12
+ exports.EmailLimits = {
13
+ /** Maximum total email address length (local + @ + domain) */
14
+ MAX_EMAIL_LENGTH: 254,
15
+ /** Maximum local part length (before @) */
16
+ MAX_LOCAL_PART_LENGTH: 64,
17
+ /** Maximum domain length (after @) */
18
+ MAX_DOMAIN_LENGTH: 253,
19
+ };
20
+ /**
21
+ * Memory calculation constants
22
+ */
23
+ exports.MemoryConstants = {
24
+ /** Bytes per kilobyte */
25
+ BYTES_PER_KB: 1024,
26
+ /** Kilobytes per megabyte */
27
+ KB_PER_MB: 1024,
28
+ };
29
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Error handling utilities
3
+ *
4
+ * Provides consistent error handling patterns across the codebase.
5
+ */
6
+ /**
7
+ * Extracts a human-readable error message from an unknown error value.
8
+ *
9
+ * @param error - The error value (Error, string, or unknown)
10
+ * @returns A string error message
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * try {
15
+ * // some operation
16
+ * } catch (error: unknown) {
17
+ * const message = getErrorMessage(error);
18
+ * console.error(message);
19
+ * }
20
+ * ```
21
+ */
22
+ export declare function getErrorMessage(error: unknown): string;
23
+ /**
24
+ * Extracts an error code from an unknown error value.
25
+ *
26
+ * @param error - The error value
27
+ * @returns The error code if available, undefined otherwise
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * try {
32
+ * // some operation
33
+ * } catch (error: unknown) {
34
+ * const code = getErrorCode(error);
35
+ * if (code === 'ENOENT') {
36
+ * // handle file not found
37
+ * }
38
+ * }
39
+ * ```
40
+ */
41
+ export declare function getErrorCode(error: unknown): string | undefined;
42
+ /**
43
+ * Checks if an error is a file not found error (ENOENT).
44
+ *
45
+ * @param error - The error value
46
+ * @returns true if the error is a file not found error
47
+ */
48
+ export declare function isFileNotFoundError(error: unknown): boolean;
49
+ /**
50
+ * Checks if an error is a JSON parsing error.
51
+ *
52
+ * @param error - The error value
53
+ * @returns true if the error is a JSON parsing error
54
+ */
55
+ export declare function isJsonError(error: unknown): boolean;
56
+ //# sourceMappingURL=error-utils.d.ts.map