Skip to content
LogoLogo

@portable/mail

Functions

Mail.envelope

Returns the SMTP envelope for a message, the addresses a transport passes to MAIL FROM and RCPT TO. Bcc recipients are included, although encodeStream leaves the Bcc header out. Addresses are normalized as in the message headers, and each recipient is listed once. The result can be spread into Smtp.send.

function envelope(message: {
  readonly from?: Addresses | undefined;
  readonly sender?: Addresses | undefined;
  readonly to?: Addresses | undefined;
  readonly cc?: Addresses | undefined;
  readonly bcc?: Addresses | undefined;
}): Envelope;

Throws

  • InvalidInputError When an address isn't one addr-spec, or the message has neither a From nor a Sender address.
import * as Mail from '@portable/mail';
 
Mail.envelope({
  from: 'Mari <mari@example.com>',
  to: 'a@example.com, B <b@example.com>',
  bcc: ['a@example.com', 'c@example.com'],
});
// { from: 'mari@example.com', to: ['a@example.com', 'b@example.com', 'c@example.com'] }

Mail.encodeStream

Encodes a message as a raw RFC 5322 message, ready for an SMTP DATA command, an .eml file or an API that takes raw MIME. Text and HTML become multipart/alternative, attachments with a cid go into multipart/related beside the HTML, and other attachments into multipart/mixed. Blob and stream content is read as the result is read.

encodeStream throws on mistakes in the message before it reads any content. A content stream that fails errors the result, and cancelling the result cancels every content stream not yet read to the end.

function encodeStream(
  message: Message,
  options: encodeStream.Options = {},
): ReadableStream<Uint8Array>;
OptionTypeDefaultDescription
boundarystring'=_' and 16 random hex digits and '_', which no base64 or quoted-printable line can containThe prefix of every multipart boundary, up to 60 characters from RFC 2046's bchars. Each boundary is this prefix followed by the position of its multipart among all parts, counted from 1 in the order the parts are written.
charsetanyUTF-8Charset for encoded words in headers and for text bodies given as strings, for mail that has to use one other than UTF-8. @portable/charset provides encoders.
keepBccbooleanfalseWrite the Bcc header, for a transport such as sendmail that reads recipients from it.
signalAbortSignalErrors the result with signal.reason and cancels every content stream not yet read.

Throws

  • InvalidInputError When an address, header, content type, file name or date can't be written.
  • TypeError When content isn't a string, Uint8Array, Blob or ReadableStream, or a stream is locked or given twice.
import * as Mail from '@portable/mail';
 
const raw = Mail.encodeStream({
  from: 'Mari <mari@example.com>',
  to: 'jaan@example.com',
  subject: 'Tere',
  text: 'Tere, Jaan!',
  html: '<p>Tere, Jaan! <img src="cid:logo"></p>',
  attachments: [
    { cid: 'logo', content: logoBytes, contentType: 'image/png' },
    {
      content: await (await fetch(reportUrl)).blob(),
      filename: 'report.pdf',
    },
  ],
});
await new Response(raw).text();

Errors

Mail.InvalidInputError

Thrown by encodeStream and envelope when the message contains something that a mail header can't hold as given: an address that isn't one addr-spec, a header name outside printable ASCII, a header the package writes itself, a content type that isn't type/subtype or is multipart/*, or an invalid Date. envelope also throws it for a message without a From or Sender address.

class InvalidInputError extends Errors.InvalidInputError {
  readonly kind: string;
}
import * as Mail from '@portable/mail';
 
try {
  Mail.encodeStream({ to: 'not an address', text: 'Hello' });
} catch (error) {
  if (error instanceof Mail.InvalidInputError) {
    console.log(error.message);
  }
}

Types

Mail.Addresses

The recipients or senders of one header: an address list as text, a Mailbox, or a list of either.

type Addresses = string | Mailbox | readonly (string | Mailbox)[];

Mail.Envelope

The SMTP envelope of a message: the MAIL FROM address and every RCPT TO address.

type Envelope = {
  from: string;
  to: readonly string[];
};

Mail.Mailbox

One mailbox. Without a name, only the address is written.

type Mailbox = {
  readonly name?: string | undefined;
  readonly address: string;
};

Mail.Content

A body or attachment: text (written as UTF-8), bytes, a Blob or File, or a stream of bytes such as response.body. Blobs and streams are read only while the message is read, so a large attachment is never held in memory.

type Content = string | Uint8Array | Blob | ReadableStream<Uint8Array>;

Mail.TransferEncoding

How a part's bytes are written. 7bit and 8bit write them as they are, with line breaks turned into CRLF.

type TransferEncoding =  |  |  | ;

Mail.Attachment

A file attached to the message, or an inline image the HTML refers to as cid:<cid>.

type Attachment = {
    readonly content: Content;
    readonly filename?: string | undefined;
    readonly contentType?: string | undefined;
    readonly cid?: string | undefined;
    readonly disposition?:  |  | undefined;
    readonly headers?: HeaderFields | undefined;
    readonly transferEncoding?: TransferEncoding | undefined;
};

Mail.HeaderFields

Extra header fields, by name. A list writes one field per value, in order.

type HeaderFields = Readonly<
  Record<string, string | readonly string[]>
>;

Mail.Message

A message to encode. Every field is optional, and a message with no body gets an empty text body.

type Message = {
  readonly from?: Addresses | undefined;
  readonly sender?: Addresses | undefined;
  readonly to?: Addresses | undefined;
  readonly cc?: Addresses | undefined;
  readonly bcc?: Addresses | undefined;
  readonly replyTo?: Addresses | undefined;
  readonly inReplyTo?: string | undefined;
  readonly references?: string | readonly string[] | undefined;
  readonly subject?: string | undefined;
  readonly messageId?: string | undefined;
  readonly date?: Instant | string | undefined;
  readonly text?: Content | undefined;
  readonly amp?: Content | undefined;
  readonly html?: Content | undefined;
  readonly alternatives?: readonly Part[] | undefined;
  readonly attachments?: readonly Attachment[] | undefined;
  readonly headers?: HeaderFields | undefined;
};

Mail.Part

An alternative body, such as a calendar invitation next to the text and HTML.

type Part = {
  readonly content: Content;
  readonly contentType: string;
  readonly headers?: HeaderFields | undefined;
  readonly transferEncoding?: TransferEncoding | undefined;
};