Repository navigation
Releases: onetodone/mailer
Release list
v0.3.1
Patch Changes
-
7e5a398: Run the main entry and the Resend transport on Bun, Deno and edge runtimes such as Cloudflare Workers.
- The main entry and
@onetodone/mailer/resendimport no Node.js built-in modules, so they load without Node.js compatibility flags.@onetodone/mailer/smtpstill needs Node.js. - A failing
onSentoronErrorhook is reported withprocess.emitWarningas aMailerWarningwhereprocess.emitWarningexists, and withconsole.warnelsewhere.
- The main entry and
v0.3.0
Minor Changes
-
5b43735: Give layouts the text direction of the email as
LayoutContext.dir.diris'rtl'for languages written right to left, such as Arabic and Hebrew, and'ltr'otherwise. Set it as thedirattribute of your layout's markup.diris required, so code that builds aLayoutContextby hand, such as a unit test of a custom layout, needs it too. Layouts passed tocreateMailerneed no change.- Migration: add
dirwhere you build aLayoutContextyourself, for exampledefaultLayout({ ...context, dir: 'ltr' })or{ branding, theme, locale: 'en', dir: 'ltr', subject, preheader, content, messages }.
-
f9a5407: Remove the
UiMessagestype from the public exports.- No exported function or type uses it, so it described nothing users can pass or receive.
- Migration: if you import
UiMessages, replace it with{ readonly linkFallback: string }.
-
f9a5407: Rename the
ctaUrlprop of thewelcometemplate tostartUrl.WelcomePropshasstartUrlinstead ofctaUrl: an absolutehttp:orhttps:link behind the "Get started" button,branding.appUrlby default. The name follows the other link props, which are named after their action (verifyUrl,resetUrl,signInUrl).sendandrenderrejectctaUrlwithINVALID_PROPS.- Migration: replace
props: { ctaUrl }withprops: { startUrl }inmailer.send('welcome', …)andmailer.render('welcome', …).
Patch Changes
-
f9a5407: Remove the unused
zodimport from the type declarations.- Type checking a project that uses the package does not load zod's declarations through it. zod stays a runtime dependency.
- No exported type changes.
-
f9a5407: Document the semver policy of the
MailerErrorCodetype: codes can be added in a minor release.- A
switchovererror.codeneeds adefaultbranch. Existing codes keep their meaning.
- A
-
f9a5407: Make
instanceof MailerErrorwork across the ESM and CommonJS builds.- When an app loads both builds, for example an ESM app with a CommonJS dependency that also uses the package,
error instanceof MailerErrorholds for errors from either build. instanceofagainst your own subclass ofMailerErrormatches only instances of that subclass.
- When an app loads both builds, for example an ESM app with a CommonJS dependency that also uses the package,
-
33827e1: Add a Resend transport:
resendTransportfrom@onetodone/mailer/resend.resendTransport({ apiKey, baseUrl?, fetch? })sends through the Resend API with the built-infetchand needs no extra package.- Every field of the email is sent, including cc, bcc, reply-to, headers and attachments. Attachments with a
cidgo out as inline images. sendresolves with the Resend email ID asmessageId.- A failed delivery rejects with
TRANSPORT_FAILED, with the HTTP status instatusand Resend's error response incause.retryableistruefor a rate limit or quota, a conflict, a server error or a network failure, andfalsefor any other 4xx. - Invalid settings throw
INVALID_CONFIG; the API key never appears in the message.
-
33827e1: Tell retryable delivery failures from rejected emails through
MailerError.retryableandMailerError.status.retryableistruewhen the same email may be delivered on a later attempt,falsewhen another attempt fails the same way, andundefinedwhen unknown.statusholds the SMTP reply code or the HTTP status of the provider's API when the provider answered.smtpTransportsets them onTRANSPORT_FAILED: a 4xx reply, a lost connection or a timeout is retryable, a 5xx reply is not.- Your own transport can set them by throwing
new MailerError('TRANSPORT_FAILED', message, { cause, retryable, status }); theMailerErrorOptionstype describes the options. - The meaning of
TRANSPORT_FAILEDis unchanged, and errors your transport throws without these fields keep themundefined.
-
5b43735: Render emails in right-to-left languages, such as Arabic and Hebrew, right to left.
- The direction follows the email's locale. For a right-to-left locale the built-in layout sets
dir="rtl"and aligns the content to the right, and Outlook on Windows aligns buttons, codes and images to the right too. - Codes from
ui.codeand the link fromui.linkFallbackstay left to right inside right-to-left text, so123 456keeps its order. - Templates receive the direction as
dir('ltr'or'rtl') next tolocale, for markup of their own. - Emails in left-to-right languages render exactly as before.
- The direction follows the email's locale. For a right-to-left locale the built-in layout sets
v0.2.6
Patch Changes
-
5d9000a: Add built-in texts in Chinese (Simplified).
Localegainszh. Thelocalesetting and thelocaleoption ofsendandrenderaccept it withoutmessages.- If you already send in Chinese (Simplified) through
messages, your overrides still win, and the keys you leave out come from the built-in texts instead of English. - Thanks @SuparvaCode for the translation!
v0.2.5
Patch Changes
-
05143f7: Document integrations and show every email in the README and docs.
- Screenshots of every built-in email, and a live preview of every email in every built-in language.
- Recipes for Better Auth, Auth.js, Express, Fastify, Next.js, NestJS and BullMQ.
- No changes to the API or to the rendered emails.
v0.2.4
Patch Changes
-
b762136: Add plural forms to the texts of custom templates.
- A text in a template's
messagesmay be plural forms instead of a string: one text perIntl.PluralRulescategory (zero,one,two,few,many,other), such asitems: { one: '{count} item', other: '{count} items' }.otheris required inen. t('<name>.<key>', { count })andt.htmlpick the form forcountthrough the plural rules of the email's locale;{count}is the number formatted for the locale. A category without a form usesother.countis required at compile time for texts with plural forms.- Other locales of the template and the mailer's
messagesgive plural forms for those keys category by category and may add categories the English texts leave out, such asfewandmany. Categories a locale leaves out fall back to English. createMailerthrowsINVALID_CONFIGfor plural forms withoutotherinen, an unknown plural category, and a locale text whose form differs fromen(a string whereenhas plural forms, or the other way round).TemplateTextsandTemplateMessagesaccept plural forms.TranslateandTemplateRenderContexttake an optional second type parameter with the keys of texts with plural forms.
- A text in a template's
v0.2.3
Patch Changes
-
f993009: Add built-in text in Belarusian (Latin).
Localegainsbe-Latn. Thelocalesetting and thelocaleoption ofsendandrenderaccept it withoutmessages.- If you already send in Belarusian (Latin) through
messages, your overrides still win, and the keys you leave out come from the built-in text instead of English.
v0.2.2
Patch Changes
-
51208d0: Add built-in texts in Czech, Estonian, French, Georgian, German, Italian, Japanese, Latvian, Lithuanian, Polish, Romanian, Thai and Ukrainian.
Localegainscs,de,et,fr,it,ja,ka,lt,lv,pl,ro,thanduk. Thelocalesetting and thelocaleoption ofsendandrenderaccept them withoutmessages.- The texts address the reader politely (
Sie,vous,vy,виand so on), except Polish and Italian, which use the informal form common in apps there. Reword any text throughmessages. - If you already send in one of these languages through
messages, your overrides still win, and the keys you leave out come from the built-in texts instead of English.
v0.2.1
Patch Changes
-
817dab4: Document the semver policy of the
Localetype: built-in locales can be added in any release.Localelists the built-in locales and gains values whenever a built-in locale is added, in any release, so code that requires everyLocalevalue (such asRecord<Locale, …>) is not covered by semver.- If you already use a locale through
messagesand it becomes built-in, your overrides still win; keys you did not override come from the built-in texts instead of English.
-
1f1d989: Add per-locale texts to custom templates.
defineTemplateacceptsmessages: the template's texts by locale.enis required and lists every text key; other locales may leave keys out, which fall back to English key by key. Plural forms are not supported in template texts.- In a template with
messages,tandt.htmltake the template's own keys under its name, such ast('invoice.subject', { number }), plus thecommonkeys, and reject any other key at compile time. A template withoutmessageskeepstfor every built-in key. - The
messagessetting ofcreateMaileroverrides the texts of registered custom templates under their names, with the same English fallback as the built-in texts.MessagesOverrides<typeof templates>andLocaleMessages<typeof templates>type such overrides outside thecreateMailercall. - A custom template with
messagesthat replaces a built-in template also replaces the built-in texts of that template in every locale. createMailerthrowsINVALID_CONFIGfor template texts that are not strings, a missingen, a key thatendoes not have, a locale that is neither built-in nor a key ofmessages, and a template withmessagesnamedcommonor containing a dot.- Types
TemplateMessagesandTemplateTexts.Template,TemplateRenderContext,Translate,MessagesOverridesandLocaleMessagestake optional type parameters, andTemplatePropsaccepts templates withmessages.
v0.2.0
Minor Changes
-
7bc8714: Add built-in emails that confirm an account deletion and tell the owner once the account is deleted.
confirmAccountDeletionasks the owner to confirm the deletion with a button toconfirmUrl, and warns that a deleted account and its data can't be restored. Props:confirmUrl,userName,expiresInMinutes(ConfirmAccountDeletionProps).accountDeletedtells the owner that the account was deleted, with a way to contact support if it wasn't them (supportUrl, elsebranding.supportEmail). Every prop is optional:userName,supportUrl(AccountDeletedProps).- Texts in English and Belarusian, overridable through
messagesin theconfirmAccountDeletionandaccountDeletedsections. Apps that keep deleted accounts for a grace period can rewordconfirmAccountDeletion.warningandaccountDeleted.farewell.
-
45611f1: Add built-in templates for changing the email address.
verifyEmailChangegoes to the new address and asks the user to confirm it. Props:verifyUrl,userName,expiresInMinutes(VerifyEmailChangeProps).emailChangeRequestedgoes to the current address when a change is requested. It namesnewEmailand offers a button that cancels the change (cancelUrl), or a way to contact support without it (supportUrl, elsebranding.supportEmail). Props:newEmail,userName,requestedAt,timeZone,ip,cancelUrl,supportUrl(EmailChangeRequestedProps).emailChangedgoes to the old address once the change is done, with a way to contact support. Every prop is optional:userName,newEmail,changedAt,timeZone,ip,supportUrl(EmailChangedProps).newEmailaccepts any non-empty text, so a masked address such asl***@example.comworks.- Texts in English and Belarusian, overridable through
messagesin theverifyEmailChange,emailChangeRequestedandemailChangedsections. Messagesdescribes the full built-in dictionary and gains keys whenever built-in templates are added, in any release, so a full dictionary typed asMessagesis not covered by semver. UseMessagesOverrides(orLocaleMessagesfor one locale) for your own texts; keys you leave out fall back to English.- Polish the Belarusian wording of the
resetPasswordintro: «Націсніце кнопку, каб задаць новы пароль.» - Migration: if you type a full dictionary as
Messages, add theverifyEmailChange,emailChangeRequestedandemailChangedsections, or check it withsatisfies MessagesOverridesinstead, so that missing keys fall back to English.
-
ffa8d14: Add built-in security notices for new sign-ins, two-factor authentication changes and locked accounts.
newSignIntells the owner about a sign-in, such as from a new device, and listssignedInAt,device,locationandipwhen you pass them. It offers a button to a page that secures the account (secureUrl), or a way to contact support without it (supportUrl, elsebranding.supportEmail). Every prop is optional:userName,signedInAt,timeZone,ip,device,location,secureUrl,supportUrl(NewSignInProps).twoFactorEnabledandtwoFactorDisabledtell the owner that two-factor authentication was turned on or off, with a way to contact support. Every prop is optional:userName,changedAt,timeZone,ip,supportUrl(TwoFactorEnabledProps,TwoFactorDisabledProps).accountLockedtells the owner that the account was locked after too many failed sign-in attempts, showslockedUntilandipwhen you pass them, and offers a button that unlocks the account (unlockUrl), or help from support without it (supportUrl, elsebranding.supportEmail). It ends with advice to choose a new password for owners who did not try to sign in. Every prop is optional:userName,lockedUntil,timeZone,ip,unlockUrl,supportUrl(AccountLockedProps).- Texts in English and Belarusian, overridable through
messagesin thenewSignIn,twoFactorEnabled,twoFactorDisabledandaccountLockedsections.
-
a32a33f: Add built-in templates for one-time codes, sign-in links and welcome emails.
otpCodesends a one-time code for sign-in, two-step verification or confirming an action. Props:code,userName,expiresInMinutes(OtpCodeProps). The subject and inbox preview leave out the code, because they show in notifications and on lock screens; both texts accept{code}, so amessagesoverride can add it.magicLinksends a link that signs the user in without a password and tells the reader not to share it. Props:signInUrl,userName,expiresInMinutes(MagicLinkProps).welcomegreets a new user with a button toctaUrl, or tobranding.appUrlwithout it. Every prop is optional:userName,ctaUrl(WelcomeProps).- Texts in English and Belarusian, overridable through
messagesin theotpCode,magicLinkandwelcomesections.
Patch Changes
-
99f9462: Add attachments and inline images.
mailer.sendacceptsattachments: files with afilename,content(aBuffer, aUint8Array, or text sent as UTF-8), an optionalcontentTypeand an optionalcid. WithoutcontentType, the type is guessed from the file name extension, withapplication/octet-streamas the fallback, and text content gets; charset=utf-8.- An attachment with a
cidis an inline image that the HTML shows throughcid:<cid>. After rendering,sendchecks the HTML, layout included, and rejects withINVALID_OPTIONSnaming everycid:reference without an attachment, before anything is sent. An attachment whosecidthe HTML never references is sent as a regular attachment. ui.image(src, { alt, width, height })shows an image from an absolutehttp:orhttps:URL, or an attachment through acid:reference. It renders email-safe markup with an explicit width (534 px, the content width, by default and at most), shrinks on narrow screens, and shows the alt text in the plain-text version. An invalid source throwsUNSAFE_URL.- File names and content types with line breaks or other control characters are rejected with
INVALID_OPTIONS, so attachments cannot inject MIME headers through any transport. Content types must be MIME types, and aciduses ASCII letters, digits,.,_,-and@and is unique within an email. smtpTransportdelivers attachments, with inline images next to the HTML.consoleTransportprints the name, type and size of each attachment, andmemoryTransportrecords them insent.OutgoingMessagehas an optionalattachmentslist, in whichcontentTypeis always set andcidis set only on referenced inline images. A custom transport must deliver every attachment or reject.- Hook events list the
filename,contentTypeandsizeof each attachment, never its content. - Types
Attachment,OutgoingAttachment,AttachmentInfoandImageOptions.
v0.1.1
Patch Changes
- d50257b: Remove source maps from the published package, which halves its unpacked size. The JavaScript and type declarations are unchanged apart from their
sourceMappingURLcomments. The JavaScript is not minified, so stack traces that point intodiststay readable.