2.6 KiB
email-channel — Email delivery channel
Purpose
email-channel.ts ships the EmailChannel DeliveryChannel implementation. The class is provider-neutral: the integration seam is a caller-supplied sendFn(message) callback that performs the actual SMTP / API call. Wiring a real provider (Amazon SES, SendGrid, Postmark, Mailgun, or an internal MTA) is a matter of supplying a function with the documented signature. The channel swallows transport errors and returns them as DeliveryResult { success: false, error }, never propagating exceptions to the router.
Features
- Provider-neutral via injectable
sendFn. canSendacceptsemailchannel +@-bearing recipient — keeps malformed addresses from reaching upstream providers.- Falls back to
"(No subject)"when the notification carries no subject. - Propagates
metadatato the provider message — useful for tracking IDs. - Injectable
Clockfor deterministictimestampfields in tests.
Usage: Basic
import { EmailChannel } from "@suaveplan/notification-channel";
const channel = new EmailChannel({
from: "noreply@example.com",
sendFn: async (msg) => ({ messageId: await sesClient.send(msg) }),
});
const result = await channel.send(notification);
if (!result.success) reportError(result.error);
Usage: Advanced
import { EmailChannel } from "@suaveplan/notification-channel";
const channel = new EmailChannel({
from: "alerts@example.com",
sendFn: async (msg) => {
const messageId = await sendgrid.send({
to: msg.to,
from: "alerts@example.com",
subject: msg.subject,
content: [{ type: "text/plain", value: msg.body }],
customArgs: msg.metadata as Record<string, string> | undefined,
});
return { messageId };
},
clock: () => testNow,
});
API Reference
EmailChannel(config: EmailChannelConfig)— constructor.EmailChannelConfig—{ from, sendFn, clock? }.EmailMessage—{ to, subject, body, metadata? }passed tosendFn.canSend(n): boolean,send(n): Promise<DeliveryResult>—DeliveryChannelimpl.
Implementation Notes
The default subject "(No subject)" matches the upstream @suaveplan/notification-channel@1.2.6 behaviour so consumer dashboards keep showing a non-empty subject column. Non-Error throws inside sendFn are coerced via String(err) so the DeliveryResult.error is always a string. The injected clock only affects the timestamp field — the upstream provider's own timestamps remain authoritative for SLA tracking.