61 lines
1.3 KiB
TypeScript
61 lines
1.3 KiB
TypeScript
/**
|
|
* Email Value Object
|
|
*
|
|
* Encapsulates email address validation and behavior
|
|
*
|
|
* Business Rules:
|
|
* - Email must be valid format
|
|
* - Email is case-insensitive (stored lowercase)
|
|
* - Email is immutable
|
|
*/
|
|
|
|
export class Email {
|
|
private readonly value: string;
|
|
|
|
private constructor(email: string) {
|
|
this.value = email;
|
|
}
|
|
|
|
static create(email: string): Email {
|
|
if (!email || email.trim().length === 0) {
|
|
throw new Error('Email cannot be empty.');
|
|
}
|
|
|
|
const normalized = email.trim().toLowerCase();
|
|
|
|
if (!Email.isValid(normalized)) {
|
|
throw new Error(`Invalid email format: ${email}`);
|
|
}
|
|
|
|
return new Email(normalized);
|
|
}
|
|
|
|
private static isValid(email: string): boolean {
|
|
// RFC 5322 simplified email regex
|
|
const emailPattern =
|
|
/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
|
|
return emailPattern.test(email);
|
|
}
|
|
|
|
getValue(): string {
|
|
return this.value;
|
|
}
|
|
|
|
getDomain(): string {
|
|
return this.value.split('@')[1];
|
|
}
|
|
|
|
getLocalPart(): string {
|
|
return this.value.split('@')[0];
|
|
}
|
|
|
|
equals(other: Email): boolean {
|
|
return this.value === other.value;
|
|
}
|
|
|
|
toString(): string {
|
|
return this.value;
|
|
}
|
|
}
|