@wtfalch/mailer
v0.1.0
Published
One send(template, to, data) call for the estate's transactional email, over Resend, with a dev transport that writes to disk instead of sending.
Readme
@wtfalch/mailer
One send(template, to, data) call for the estate's transactional email,
over Resend, with a dev transport that writes to disk instead of sending.
Install
pnpm add @wtfalch/mailerUse
Call send() with a template name, the recipient, that template's data,
and who the message is from:
import { createDevTransport, send } from '@wtfalch/mailer';
await send({
template: 'invitation',
to: '[email protected]',
data: {
organisationName: 'Acme',
inviterName: 'Jordan',
acceptUrl: 'https://app.example.com/invite/abc123',
roleName: 'Editor', // optional
brand: { name: 'Acme' }, // optional
},
from: {
fromAddress: '[email protected]',
fromName: 'Acme', // optional
replyTo: '[email protected]', // optional
},
transport: createDevTransport(), // writes to .mailer/dev instead of sending
});transport is optional. Left out, send() uses the dev transport outside
NODE_ENV=production and throws MailerConfigError in production -- a
missing transport fails loudly instead of silently no-op'ing or making a
live call with no key.
Transports
createDevTransport({ dir })-- writes each message to<dir>/<timestamp>-<id>.json(defaultdir:.mailer/dev). Never opens a network connection.createResendTransport({ apiKey })-- sends over Resend. Needs eitherapiKey(a plain string -- resolve it yourself, e.g. from@wtfalch/keys) orclient, a minimal structuralResendLikefor injecting a fake in tests.
Templates
invitation is the only template shipped so far:
interface InvitationData {
organisationName: string;
inviterName: string;
acceptUrl: string;
roleName?: string;
brand?: { name: string; logoUrl?: string; primaryColor?: string };
}Tests
From packages/mailer:
pnpm testTests run against the dev transport and a fake ResendLike client --
never a live key, never the network.
