@mikkelscheike/email-provider-links
Version:
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
195 lines • 6.31 kB
JavaScript
;
/**
* Email Alias Detection Module
*
* Clean, focused implementation with only essential functions.
* Detects and normalizes email aliases across different providers.
*
* Alias Configuration Behavior:
* ---------------------------
* The module handles email aliases based on provider-specific configurations.
* Each provider can specify how to handle three types of email variations:
*
* 1. Case sensitivity ("case")
* 2. Plus addressing ("plus")
* 3. Dots in username ("dots")
*
* Important: For each of these properties, modifications are only applied if
* explicitly configured in the provider's settings:
*
* - If a property is defined (e.g., "case": {"ignore": true, "strip": true}),
* the specified behavior is applied
*
* - If a property is missing from the provider's alias configuration,
* the original value is preserved without modification
*
* Example:
* ```json
* {
* "alias": {
* "dots": { "ignore": false, "strip": false },
* "plus": { "ignore": true, "strip": true }
* // case is not defined, so case will be preserved
* }
* }
* ```
*
* In this example:
* - Dots will be preserved (configured to not ignore/strip)
* - Plus addressing will be stripped (configured to ignore/strip)
* - Case will be preserved (not configured)
*
* Note: The domain part of email addresses is always converted to lowercase
* as per RFC 5321 standard, regardless of provider configuration.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectEmailAlias = detectEmailAlias;
exports.normalizeEmail = normalizeEmail;
exports.emailsMatch = emailsMatch;
const loader_1 = require("./loader");
/**
* Validates email format
*/
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
/**
* Detects and analyzes email aliases
*
* This function processes email addresses according to provider-specific rules.
* Case is always lowercased in the canonical form for consistency and safety.
* It only applies additional modifications (plus, dots) that are explicitly
* defined in the provider's configuration.
*
* @param email - Email address to analyze
* @returns Detailed analysis of the email alias
*
* @example
* Provider with no case handling defined:
* ```typescript
* detectEmailAlias('User.Name@example.com')
* // Preserves case: User.Name@example.com
* ```
*
* Provider with case handling defined:
* ```typescript
* detectEmailAlias('User.Name@gmail.com')
* // Converts to lowercase: user.name@gmail.com
* ```
*/
function detectEmailAlias(email) {
if (!isValidEmail(email)) {
throw new Error('Invalid email format');
}
const originalEmail = email.trim();
// Split normally, lowering case both for username and domain by default
const emailParts = originalEmail.toLowerCase().split('@');
const username = emailParts[0];
const domain = emailParts[1]; // domain is always case-insensitive per RFC 5321
if (!username || !domain) {
throw new Error('Invalid email format - missing username or domain');
}
const { domainMap } = (0, loader_1.loadProviders)();
const provider = domainMap.get(domain);
const result = {
// Only lowercase domain part by default
canonical: `${username}@${domain}`,
original: originalEmail,
isAlias: false,
aliasType: 'none'
};
if (!provider?.alias) {
return result;
}
result.provider = domain;
let normalizedUsername = username;
let isAlias = false;
let aliasType = 'none';
let aliasPart;
// Canonical form is always lowercased to ensure consistent and
// reliable email handling across different providers.
if (provider.alias?.case?.ignore) {
if (provider.alias.case?.strip) {
normalizedUsername = normalizedUsername.toLowerCase();
}
}
// Handle plus addressing if defined in provider settings
// If plus handling is not defined, preserve plus addressing
if (provider.alias?.plus?.ignore) {
const plusIndex = username.indexOf('+');
if (plusIndex !== -1) {
aliasPart = username.substring(plusIndex + 1);
isAlias = true;
aliasType = 'plus';
if (provider.alias.plus?.strip) {
normalizedUsername = username.slice(0, plusIndex);
}
}
}
// Handle dots if defined in provider settings
// If dots handling is not defined, preserve dots
if (provider.alias?.dots?.ignore) {
const hasDots = username.includes('.');
if (hasDots) {
if (!isAlias) {
aliasPart = username;
isAlias = true;
aliasType = 'dot';
}
if (provider.alias.dots?.strip) {
normalizedUsername = normalizedUsername.replace(/\./g, '');
}
}
}
// Build the canonical form
result.canonical = `${normalizedUsername}@${domain}`;
result.isAlias = isAlias;
result.aliasType = aliasType;
if (aliasPart !== undefined) {
result.aliasPart = aliasPart;
}
return result;
}
/**
* Normalizes an email address to its canonical form.
*
* This is the primary function for preventing duplicate accounts.
*
* @param email - Email address to normalize
* @returns Canonical email address
*
* @example
* ```typescript
* const canonical = normalizeEmail('U.S.E.R+work@GMAIL.COM');
* console.log(canonical); // 'user@gmail.com'
* ```
*/
function normalizeEmail(email) {
const result = detectEmailAlias(email);
return result.canonical;
}
/**
* Checks if two email addresses are the same when normalized.
*
* This is the primary function for matching aliases during login.
*
* @param email1 - First email address
* @param email2 - Second email address
* @returns true if the emails represent the same person
*
* @example
* ```typescript
* const match = emailsMatch('user@gmail.com', 'u.s.e.r+work@gmail.com');
* console.log(match); // true
* ```
*/
function emailsMatch(email1, email2) {
try {
return normalizeEmail(email1) === normalizeEmail(email2);
}
catch {
return false;
}
}
//# sourceMappingURL=alias-detection.js.map