Files
skills/docs/api-stack/api/api-plugin-notifications/src/channels/email-channel/email-channel.md
2026-05-29 08:20:56 +01:00

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 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

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 DeliveryChannel
    • name: "email"
    • canSend(n): boolean — true iff n.channel === "email"
    • send(n): Promise<DeliveryResult>
  • 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.