118 lines
8.6 KiB
Markdown
118 lines
8.6 KiB
Markdown
# @suaveplan/sms-providers - Universal SMS provider implementations for @suaveplan/sms. Real HTTP clients for Twilio (Basic auth + form-encoded + X-Twilio-Signature HMAC-SHA1), Vonage (HMAC-SHA256 webhook), MessageBird (AccessKey + JWT HS256 webhook), Plivo (Basic auth + X-Plivo-Signature-V3 HMAC-SHA256), and AWS SNS (inline SigV4 + RSA-SHA1 SNS notification verifier). Ships ProviderRegistry, httpRequest over injectable FetchLike, composeAbortSignal, SMS_PROVIDERS_CODES + SmsProvidersError, per-provider mapStatus, parseInbound, parseOptOutKeyword, and five OTel instruments. No provider SDK deps; every HTTP call flows through an injectable FetchLike seam.
|
|
|
|
> Real provider clients for `@suaveplan/sms` — Twilio, Vonage, MessageBird, Plivo, AWS SNS. Universal (Bun, Node, browsers, Workers, Deno) over an injectable `FetchLike` seam; no provider SDKs in the dep tree.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## Documentation tree
|
|
|
|
- [aws-sns](./src/aws-sns/aws-sns.md) — Real AWS SNS SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs `Action=Publish` form bodies to the r…
|
|
- [aws-sns](./src/aws-sns/aws-sns.md) — Real AWS SNS SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs `Action=Publish` form bodies to the r…
|
|
- [sigv4](./src/aws-sns/sigv4.md) — Inline AWS Signature V4 signer used by `AwsSnsProvider`. Replaces the `@aws-sdk/signature-v4` dependency tree (15+ MB)…
|
|
- [crypto](./src/crypto/default-hmac.md) — Owns the package's default `HmacSign` implementation (routed through `globalThis.crypto.subtle`) and the small set of u…
|
|
- [default-hmac](./src/crypto/default-hmac.md) — Owns the package's default `HmacSign` implementation (routed through `globalThis.crypto.subtle`) and the small set of u…
|
|
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable, namespaced error-code registry `SMS_PROVIDERS_CODES` and the `SmsProvidersError` discriminated subc…
|
|
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable, namespaced error-code registry `SMS_PROVIDERS_CODES` and the `SmsProvidersError` discriminated subc…
|
|
- [http](./src/http/http.md) — Universal HTTP helper that every concrete provider client routes through. Wraps an injectable `FetchLike` seam, se…
|
|
- [http](./src/http/http.md) — Universal HTTP helper that every concrete provider client routes through. Wraps an injectable `FetchLike` seam, se…
|
|
- [keywords](./src/keywords/keywords.md) — Universal classifier mapping an inbound SMS body to the canonical `OptOutAction` enum used by `@suaveplan/sms`'s `OptOu…
|
|
- [keywords](./src/keywords/keywords.md) — Universal classifier mapping an inbound SMS body to the canonical `OptOutAction` enum used by `@suaveplan/sms`'s `OptOu…
|
|
- [messagebird](./src/messagebird/messagebird.md) — Real MessageBird SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/messages` with `Authorizatio…
|
|
- [messagebird](./src/messagebird/messagebird.md) — Real MessageBird SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/messages` with `Authorizatio…
|
|
- [otel](./src/otel/otel.md) — Owns the package's OpenTelemetry instrument set. Five instruments cover the public-surface operations every provider cl…
|
|
- [otel](./src/otel/otel.md) — Owns the package's OpenTelemetry instrument set. Five instruments cover the public-surface operations every provider cl…
|
|
- [plivo](./src/plivo/plivo.md) — Real Plivo SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/v1/Account/{AuthId}/Message/` auth…
|
|
- [plivo](./src/plivo/plivo.md) — Real Plivo SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/v1/Account/{AuthId}/Message/` auth…
|
|
- [registry](./src/registry/registry.md) — `ProviderRegistry` is a process-local, synchronous index of `SmsProvider` instances keyed by `provider.name`. Sibling p…
|
|
- [registry](./src/registry/registry.md) — `ProviderRegistry` is a process-local, synchronous index of `SmsProvider` instances keyed by `provider.name`. Sibling p…
|
|
- [twilio](./src/twilio/twilio.md) — Real Twilio SMS provider implementing the `SmsProvider` contract from `@suaveplan/sms`. Issues authenticated, form-enco…
|
|
- [twilio](./src/twilio/twilio.md) — Real Twilio SMS provider implementing the `SmsProvider` contract from `@suaveplan/sms`. Issues authenticated, form-enco…
|
|
- [types](./src/types/provider.md) — Defines the shared structural types every concrete provider class consumes: `FetchLike` (the injectable HTTP seam), `Hm…
|
|
- [provider](./src/types/provider.md) — Defines the shared structural types every concrete provider class consumes: `FetchLike` (the injectable HTTP seam), `Hm…
|
|
- [vonage](./src/vonage/vonage.md) — Real Vonage (formerly Nexmo) SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/sms/json` with `…
|
|
- [vonage](./src/vonage/vonage.md) — Real Vonage (formerly Nexmo) SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/sms/json` with `…
|
|
<!-- end:auto-docs -->
|
|
|
|
## Highlights
|
|
|
|
- **Five real provider clients**, every one implementing the `SmsProvider` contract from `@suaveplan/sms`:
|
|
- `TwilioProvider` — `POST /Messages.json`, HTTP Basic auth, MMS `MediaUrl[]`, X-Twilio-Signature HMAC-SHA1 webhook verifier.
|
|
- `VonageProvider` — `POST /sms/json`, body auth, auto `type=unicode`, HMAC-SHA256 webhook verifier.
|
|
- `MessageBirdProvider` — `AccessKey` auth, JSON body, JWT HS256 webhook verifier with `iat`/`exp`/`nbf` enforcement + optional `payload_hash` binding.
|
|
- `PlivoProvider` — Basic auth, JSON body, X-Plivo-Signature-V3 HMAC-SHA256 verifier.
|
|
- `AwsSnsProvider` — inline AWS SigV4-signed `Action=Publish`, RSA-SHA1 SNS notification verifier with injectable callback.
|
|
- **`ProviderRegistry`** indexes provider instances by name; strict register/lookup, frozen `list()` snapshot.
|
|
- **`SmsProvidersError`** discriminated subclass of `SuaveplanError` with eight namespaced codes (`SMS_PROVIDERS.HTTP_ERROR`, `TIMEOUT`, `PARSE_ERROR`, `CONFIG_MISSING`, `SIGNATURE_INVALID`, `INVALID_NUMBER`, `PROVIDER_NOT_REGISTERED`, `PROVIDER_ALREADY_REGISTERED`).
|
|
- **`parseOptOutKeyword`** universal CTIA/TCPA opt-in/opt-out/help keyword classifier.
|
|
- **Five OTel instruments** — `send_total`, `webhook_total`, `signature_failures_total`, `send_duration_ms`, `webhook_verify_duration_ms`.
|
|
- **Real `node:http` round-trip tests** per provider — no mocks; tests bind a server on a random port and inject a `FetchLike` pointing at it.
|
|
|
|
## Quick start
|
|
|
|
```ts
|
|
import {
|
|
AwsSnsProvider,
|
|
MessageBirdProvider,
|
|
PlivoProvider,
|
|
ProviderRegistry,
|
|
TwilioProvider,
|
|
VonageProvider,
|
|
} from "@suaveplan/sms-providers";
|
|
|
|
const registry = new ProviderRegistry();
|
|
registry.register(new TwilioProvider({ accountSid: "AC…", authToken: "…" }));
|
|
registry.register(new VonageProvider({ apiKey: "k", apiSecret: "s" }));
|
|
registry.register(new MessageBirdProvider({ accessKey: "K" }));
|
|
registry.register(new PlivoProvider({ authId: "A", authToken: "T" }));
|
|
registry.register(
|
|
new AwsSnsProvider({
|
|
region: "us-east-1",
|
|
accessKeyId: "AKIA…",
|
|
secretAccessKey: "…",
|
|
})
|
|
);
|
|
|
|
const twilio = registry.get("twilio");
|
|
const report = await twilio.send({
|
|
to: "+15551234567",
|
|
from: "+15550000000",
|
|
body: "Hello",
|
|
});
|
|
console.log(report.messageId, report.status);
|
|
```
|
|
|
|
## Webhook verification
|
|
|
|
Every provider exposes a signature verifier matching its documented scheme:
|
|
|
|
```ts
|
|
// Twilio HMAC-SHA1 over url + sorted form
|
|
await twilio.verifyWebhookSignature(callbackUrl, params, signature);
|
|
|
|
// Vonage HMAC-SHA256 hex over sorted &key=value (excluding sig)
|
|
await vonage.verifyWebhookSignature(params, sigHex, signatureSecret);
|
|
|
|
// MessageBird JWT HS256 with iat/exp/nbf window + payload_hash binding
|
|
await mb.verifyWebhookSignature(jwt, signingKey, { nowMs, bodyHashHex });
|
|
|
|
// Plivo HMAC-SHA256 base64 over url + nonce + sorted key=value,...
|
|
await plivo.verifyWebhookSignature(url, nonce, params, signature);
|
|
|
|
// AWS SNS RSA-SHA1 — pass an injectable verifier callback
|
|
await sns.verifyNotification(payload, async (pem, msg, sigB64) => {
|
|
/* createVerify("RSA-SHA1")…verify(...) */
|
|
return true;
|
|
});
|
|
```
|
|
|
|
## Tier
|
|
|
|
`src/` ships zero `node:*` / `bun:*` / platform imports. Every HTTP call routes through an injectable `FetchLike`; every HMAC routes through an injectable `HmacSign` (defaulting to `globalThis.crypto.subtle`). Tests use real `node:http` + `node:crypto` from under `tests/setup/` and inside `*.test.ts` files only.
|
|
|
|
## Pre-publish gate
|
|
|
|
```bash
|
|
bunx turbo lint typecheck test build --filter=@suaveplan/sms-providers
|
|
bunx turbo test:coverage --filter=@suaveplan/sms-providers # 100/100/100/100
|
|
```
|
|
|