3.7 KiB
3.7 KiB
email-channel
(no submodules)
Purpose
Adapts @suaveplan/emailer's EmailService to the
@suaveplan/notification-channel DeliveryChannel interface so
NotificationDispatcher can route email notifications through any of
the emailer's three transports (Dummy / SMTP / Gmail REST). The
channel is registered under the name email and canSend-gates on
notification.channel === "email". This module is the single place
where the dispatcher's generic Notification shape is translated
into an EmailMessage — keeping that translation out of the router
and out of EmailService.
Features
- Channel name
email; canSend gates onnotification.channel === "email". - Maps
Notification.recipient→EmailMessage.to(the emailer'sAddressBagaccepts a single string),Notification.subject→EmailMessage.subject(withdefaultSubjectfallback when the notification carries no subject),Notification.body→EmailMessage.text(orhtmlwhenmetadata.html === true). - Folds
EmailService.send's{ success: false, error }outcomes intoDeliveryResult.error. No throws for transport failures. - Refuses to construct with
emailService: null— raisesNotificationsPluginError(EMAIL_SERVICE_UNAVAILABLE)so the dispatcher fails fast at boot when an operator forgets to inject the service. - Surfaces the emailer's
messageIdwhen the transport returns one; falls back to the upstream notification id otherwise.
Usage: Basic
import { EmailService, createDummyTransport } from "@suaveplan/emailer";
import { EmailDeliveryChannel } from "@suaveplan/api-plugin-notifications";
const email = new EmailService({
transport: createDummyTransport({ logSends: true }),
});
await email.initialize();
const channel = new EmailDeliveryChannel({ emailService: email });
const result = await channel.send({
id: "notif_1",
channel: "email",
recipient: "user@example.com",
subject: "Welcome",
body: "Hello!",
priority: "normal",
status: "sent",
createdAt: new Date().toISOString(),
});
Usage: Advanced
Render an HTML body, configure a default subject, and wire into the router:
import { NotificationRouter } from "@suaveplan/notification-channel";
const channel = new EmailDeliveryChannel({
emailService: email,
defaultSubject: "[YourApp] Notification",
});
const router = new NotificationRouter({
channels: [{ channel: "email", enabled: true }],
defaultChannel: "email",
deliveryChannels: [channel],
});
await router.send({
recipient: "user@example.com",
channel: "email",
body: "<h1>Welcome</h1><p>Glad you're here.</p>",
metadata: { html: true },
});
API Reference
class EmailDeliveryChannel implements DeliveryChannelname: "email"canSend(n): boolean— true iffn.channel === "email"send(n): Promise<DeliveryResult>
interface EmailDeliveryChannelOptionsemailService: EmailService | null— null raisesEMAIL_SERVICE_UNAVAILABLEat constructiondefaultSubject?: string— default"Notification"clock?: Clock
Implementation Notes
- The emailer's
EmailMessagerequiressubjectto be a non-empty string; the channel always supplies one (viadefaultSubjectif the notification omits it). metadata.html === trueflips body delivery fromtexttohtml. Both formats are mutually exclusive — choose one per notification.- The channel exposes the emailer's transport-reported
messageIdon theDeliveryResultso callers can correlate delivery-tracking events with the underlying email message. EmailService.sendnever throws (it folds errors into the result), so the channel doesn't need a try/catch around it.