@@ -294,6 +294,22 @@ function generateBoundary(): string {
294294 return `----=_Part_${ Date . now ( ) } _${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) } `
295295}
296296
297+ /**
298+ * Encode a header value using RFC 2047 Base64 encoding if it contains non-ASCII characters.
299+ * Email headers per RFC 2822 must be ASCII-only. Non-ASCII characters (emojis, accented
300+ * characters, etc.) must be encoded as =?UTF-8?B?<base64>?= to avoid mojibake.
301+ * @param value The header value to encode
302+ * @returns The encoded header value, or the original if it's already ASCII
303+ */
304+ export function encodeRfc2047 ( value : string ) : string {
305+ // eslint-disable-next-line no-control-regex
306+ if ( / ^ [ \x00 - \x7F ] * $ / . test ( value ) ) {
307+ return value
308+ }
309+ const encoded = Buffer . from ( value , 'utf-8' ) . toString ( 'base64' )
310+ return `=?UTF-8?B?${ encoded } ?=`
311+ }
312+
297313/**
298314 * Encode string or buffer to base64url format (URL-safe base64)
299315 * Gmail API requires base64url encoding for the raw message field
@@ -333,7 +349,7 @@ export function buildSimpleEmailMessage(params: {
333349 emailHeaders . push ( `Bcc: ${ bcc } ` )
334350 }
335351
336- emailHeaders . push ( `Subject: ${ subject || '' } ` )
352+ emailHeaders . push ( `Subject: ${ encodeRfc2047 ( subject || '' ) } ` )
337353
338354 if ( inReplyTo ) {
339355 emailHeaders . push ( `In-Reply-To: ${ inReplyTo } ` )
@@ -380,7 +396,7 @@ export function buildMimeMessage(params: BuildMimeMessageParams): string {
380396 if ( bcc ) {
381397 messageParts . push ( `Bcc: ${ bcc } ` )
382398 }
383- messageParts . push ( `Subject: ${ subject || '' } ` )
399+ messageParts . push ( `Subject: ${ encodeRfc2047 ( subject || '' ) } ` )
384400
385401 if ( inReplyTo ) {
386402 messageParts . push ( `In-Reply-To: ${ inReplyTo } ` )
0 commit comments