# 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 on `notification.channel === "email"`. - Maps `Notification.recipient` → `EmailMessage.to` (the emailer's `AddressBag` accepts a single string), `Notification.subject` → `EmailMessage.subject` (with `defaultSubject` fallback when the notification carries no subject), `Notification.body` → `EmailMessage.text` (or `html` when `metadata.html === true`). - Folds `EmailService.send`'s `{ success: false, error }` outcomes into `DeliveryResult.error`. No throws for transport failures. - Refuses to construct with `emailService: null` — raises `NotificationsPluginError(EMAIL_SERVICE_UNAVAILABLE)` so the dispatcher fails fast at boot when an operator forgets to inject the service. - Surfaces the emailer's `messageId` when the transport returns one; falls back to the upstream notification id otherwise. ## Usage: Basic ```ts 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: ```ts 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: "

Welcome

Glad you're here.

", metadata: { html: true }, }); ``` ## API Reference - `class EmailDeliveryChannel implements DeliveryChannel` - `name: "email"` - `canSend(n): boolean` — true iff `n.channel === "email"` - `send(n): Promise` - `interface EmailDeliveryChannelOptions` - `emailService: EmailService | null` — null raises `EMAIL_SERVICE_UNAVAILABLE` at construction - `defaultSubject?: string` — default `"Notification"` - `clock?: Clock` ## Implementation Notes - The emailer's `EmailMessage` requires `subject` to be a non-empty string; the channel always supplies one (via `defaultSubject` if the notification omits it). - `metadata.html === true` flips body delivery from `text` to `html`. Both formats are mutually exclusive — choose one per notification. - The channel exposes the emailer's transport-reported `messageId` on the `DeliveryResult` so callers can correlate delivery-tracking events with the underlying email message. - `EmailService.send` never throws (it folds errors into the result), so the channel doesn't need a try/catch around it.