first commit
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# `sms-channel` — SMS delivery channel
|
||||
|
||||
## Purpose
|
||||
|
||||
`sms-channel.ts` ships the `SmsChannel` `DeliveryChannel` implementation. Like the email channel, it is provider-neutral: a caller-supplied `sendFn(message)` performs the actual API call into Twilio, MessageBird, Vonage, Sinch, or any in-house SMSC. The channel keeps the responsibility surface tiny — recipient validation (E.164 `+` prefix), payload shaping, and error swallowing — so the router can stay decoupled from any specific carrier.
|
||||
|
||||
## Features
|
||||
|
||||
- Provider-neutral via injectable `sendFn`.
|
||||
- `canSend` rejects recipients that do not start with `+` (E.164 prefix); avoids hitting upstream providers with formats they will reject.
|
||||
- Propagates `metadata` to the provider message for correlation IDs and segmentation.
|
||||
- Injectable `Clock` for deterministic `timestamp` fields in tests.
|
||||
- Returns `DeliveryResult` even on provider failure — never throws to the router.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { SmsChannel } from "@suaveplan/notification-channel";
|
||||
|
||||
const channel = new SmsChannel({
|
||||
from: "+15559999999",
|
||||
sendFn: async (msg) => ({
|
||||
messageId: await twilio.messages.create({
|
||||
from: "+15559999999",
|
||||
to: msg.to,
|
||||
body: msg.body,
|
||||
}).sid,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { SmsChannel } from "@suaveplan/notification-channel";
|
||||
|
||||
const channel = new SmsChannel({
|
||||
from: "+15559999999",
|
||||
sendFn: async (msg) => {
|
||||
const { sid } = await provider.send({
|
||||
from: "+15559999999",
|
||||
to: msg.to,
|
||||
body: msg.body,
|
||||
...(msg.metadata?.["statusCallback"] !== undefined
|
||||
? { statusCallback: msg.metadata["statusCallback"] }
|
||||
: {}),
|
||||
});
|
||||
return { messageId: sid };
|
||||
},
|
||||
clock: () => testNow,
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `SmsChannel(config: SmsChannelConfig)` — constructor.
|
||||
- `SmsChannelConfig` — `{ from, sendFn, clock? }`.
|
||||
- `SmsMessage` — `{ to, body, metadata? }` passed to `sendFn`.
|
||||
- `canSend(n): boolean`, `send(n): Promise<DeliveryResult>` — `DeliveryChannel` impl.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `+` prefix check is intentionally conservative — it does not validate the full E.164 country/area format because providers do that anyway and over-validating client-side breaks edge cases like short-code dispatch. Tests cover both `Error` and non-`Error` throws inside `sendFn` to lock in the `String(err)` coercion. The injected `clock` only affects the `DeliveryResult.timestamp` field; provider-recorded send time remains authoritative.
|
||||
Reference in New Issue
Block a user