Initial Commit
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# email-channel
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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: "<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.
|
||||
Reference in New Issue
Block a user