Files
skills/docs/communication/messaging/notification-channel/src/channels/email-channel.md
2026-05-29 08:20:56 +01:00

60 lines
2.6 KiB
Markdown

# `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`.
- `canSend` accepts `email` channel + `@`-bearing recipient — keeps malformed addresses from reaching upstream providers.
- Falls back to `"(No subject)"` when the notification carries no subject.
- Propagates `metadata` to the provider message — useful for tracking IDs.
- Injectable `Clock` for deterministic `timestamp` fields in tests.
## Usage: Basic
```ts
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
```ts
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 to `sendFn`.
- `canSend(n): boolean`, `send(n): Promise<DeliveryResult>` — `DeliveryChannel` impl.
## 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.