/**
* Return UTF-8 byte length for a string.
*
* Calculates the actual byte length of a string when encoded as UTF-8,
* which is important for email size limits since most email systems
* measure message size in bytes, not characters.
*
* Note that multi-byte characters (e.g., emojis, non-ASCII characters)
* count as more than one byte in UTF-8 encoding.
*
* @example
* ```typescript
* byteLength("Hello"); // Returns 5 (ASCII is 1 byte per char)
* byteLength("Hello 世界"); // Returns 11 (7 ASCII + 2 chars × 2 bytes each)
* byteLength("😀"); // Returns 4 (emoji is 4 bytes in UTF-8)
* ```
*
* @param value - The string to measure in bytes.
* @returns The byte length when encoded as UTF-8.
*/
export function byteLength(value: string): number {
return Buffer.byteLength(value, "utf8");
}
/**
* Estimate message size using UTF-8 bytes plus attachments.
*
* Provides a conservative estimate of total email message size for policy enforcement.
* Includes subject, body (text and/or HTML), attachments, and a fixed overhead
* for email headers and MIME encoding (1024 bytes).
*
* The estimate is conservative because it includes:
* - ~33% base64 overhead for attachment encoding
* - A per-attachment boundary/header allowance
* - Fixed overhead for general headers and MIME boundaries
*
* @example
* ```typescript
* // Simple text email
* estimateMessageBytes({
* subjectBytes: 20,
* textBytes: 500,
* htmlBytes: 0,
* attachmentBytes: 0,
* attachmentCount: 0,
* }); // Returns 542 (20 + 500 + 0 + 0 + 1024 overhead)
*
* // Email with HTML and attachment
* estimateMessageBytes({
* subjectBytes: 25,
* textBytes: 300,
* htmlBytes: 1200,
* attachmentBytes: 50000,
* attachmentCount: 1,
* }); // Returns 68424 (25 + 300 + 1200 + 66667 + 200 + 1024 overhead)
* ```
*
* @param parts - Message component sizes in bytes.
* @param parts.subjectBytes - Byte length of the subject line.
* @param parts.textBytes - Byte length of the plain text body.
* @param parts.htmlBytes - Byte length of the HTML body.
* @param parts.attachmentBytes - Combined byte length of all attachments.
* @param parts.attachmentCount - Number of attachments included.
* @returns Estimated total message size in bytes.
*/
export function estimateMessageBytes(parts: {
subjectBytes: number;
textBytes: number;
htmlBytes: number;
attachmentBytes: number;
attachmentCount: number;
}): number {
const overheadBytes = 1024;
const base64Expansion = Math.ceil(parts.attachmentBytes * (4 / 3));
const perAttachmentOverhead = parts.attachmentCount * 200;
return (
parts.subjectBytes +
parts.textBytes +
parts.htmlBytes +
base64Expansion +
perAttachmentOverhead +
overheadBytes
);
}