/** * CENTRAL EMAIL FORMATTER * * This is the primary and only email formatting utility to be used. * All email formatting should go through here to ensure consistent * handling of text direction and HTML sanitization. * * IMPORTANT: This formatter is configured for English-only content * and enforces left-to-right text direction. */ import DOMPurify from 'isomorphic-dompurify'; // Reset any existing hooks to start clean DOMPurify.removeAllHooks(); // Configure DOMPurify for English-only content (always LTR) DOMPurify.addHook('afterSanitizeAttributes', function(node) { // Force LTR direction on all elements that can have a dir attribute if (node instanceof HTMLElement) { node.setAttribute('dir', 'ltr'); // Handle style attribute if (node.hasAttribute('style')) { let style = node.getAttribute('style') || ''; // Remove any RTL-related styles style = style.replace(/direction\s*:\s*rtl\s*;?/gi, ''); style = style.replace(/text-align\s*:\s*right\s*;?/gi, ''); style = style.replace(/unicode-bidi\s*:[^;]*;?/gi, ''); // Add explicit LTR styles style = style.trim(); if (style && !style.endsWith(';')) style += ';'; style += ' direction: ltr; text-align: left;'; node.setAttribute('style', style); } else { // If no style exists, add default LTR styles node.setAttribute('style', 'direction: ltr; text-align: left;'); } } }); // Configure DOMPurify to add certain attributes and forbid others DOMPurify.setConfig({ ADD_ATTR: ['dir'], FORBID_ATTR: ['lang', 'bidi'] }); // Interface definitions export interface EmailAddress { name: string; address: string; } export interface EmailMessage { id: string; messageId?: string; subject: string; from: EmailAddress[]; to: EmailAddress[]; cc?: EmailAddress[]; bcc?: EmailAddress[]; date: Date | string; flags?: { seen: boolean; flagged: boolean; answered: boolean; deleted: boolean; draft: boolean; }; preview?: string; content?: string; html?: string; text?: string; hasAttachments?: boolean; attachments?: any[]; folder?: string; size?: number; contentFetched?: boolean; } /** * Format email addresses for display */ export function formatEmailAddresses(addresses: EmailAddress[]): string { if (!addresses || addresses.length === 0) return ''; return addresses.map(addr => addr.name && addr.name !== addr.address ? `${addr.name} <${addr.address}>` : addr.address ).join(', '); } /** * Format date for display */ export function formatEmailDate(date: Date | string | undefined): string { if (!date) return ''; try { const dateObj = typeof date === 'string' ? new Date(date) : date; return dateObj.toLocaleString('en-US', { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (e) { return typeof date === 'string' ? date : date.toString(); } } /** * Sanitize HTML content before processing or displaying * This ensures the content is properly formatted for LTR display * @param content HTML content to sanitize * @returns Sanitized HTML with LTR formatting */ export function sanitizeHtml(content: string): string { if (!content) return ''; // Sanitize the HTML using our configured DOMPurify with LTR enforced return DOMPurify.sanitize(content); } /** * Format an email for forwarding - CENTRAL IMPLEMENTATION * All other formatting functions should be deprecated in favor of this one */ export function formatForwardedEmail(email: EmailMessage): { subject: string; content: string; } { // Format subject with Fwd: prefix if needed const subjectBase = email.subject || '(No subject)'; const subject = subjectBase.match(/^(Fwd|FW|Forward):/i) ? subjectBase : `Fwd: ${subjectBase}`; // Get sender and recipient information const fromString = formatEmailAddresses(email.from || []); const toString = formatEmailAddresses(email.to || []); const dateString = formatEmailDate(email.date); // Get and sanitize original content (sanitization enforces LTR) const originalContent = sanitizeHtml(email.content || email.html || email.text || ''); // Check if the content already has a forwarded message header const hasExistingHeader = originalContent.includes('---------- Forwarded message ---------'); // If there's already a forwarded message header, don't add another one if (hasExistingHeader) { // Just wrap the content without additional formatting const content = `