134 lines
9.9 KiB
Markdown
134 lines
9.9 KiB
Markdown
# @suaveplan/sms - Universal SMS core: provider-agnostic send pipeline with E.164 validation, Zod boundaries, template rendering, opt-out registry, token-bucket rate limiter, exponential-backoff retry, message scheduler, bounded delivery-tracker LRU cache, typed EventBus, legacy on/off mirror, in-memory test provider, four OTel instruments, thirteen SMS.* error codes via defineCodes, and back-compat for upstream SmsError + SMS_ERROR_METADATA.
|
|
|
|
Universal SMS core: provider-agnostic send pipeline with strict
|
|
E.164 validation, Zod boundaries, template rendering, opt-out
|
|
registry, token-bucket rate limiter, exponential-backoff retry with
|
|
abort propagation, message scheduling, bounded LRU delivery tracker,
|
|
typed `EventBus` + legacy `on/off/emit` mirror, an in-memory test
|
|
provider, four OpenTelemetry instruments, thirteen `SMS.*` error
|
|
codes via `defineCodes`, and full back-compat for the upstream
|
|
`SmsError` class and `SMS_ERROR_METADATA` table.
|
|
|
|
This package is the SMS core. Concrete provider integrations
|
|
(Twilio, Vonage, AWS SNS, etc.) ship in `@suaveplan/sms-providers`.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## Documentation tree
|
|
|
|
- [delivery-tracker](./src/delivery-tracker/delivery-tracker.md) — `delivery-tracker` is the bounded LRU cache of `SmsDeliveryReport` records that `SmsService` consults for delivery stat…
|
|
- [delivery-tracker](./src/delivery-tracker/delivery-tracker.md) — `delivery-tracker` is the bounded LRU cache of `SmsDeliveryReport` records that `SmsService` consults for delivery stat…
|
|
- [error-codes](./src/error-codes/error-codes.md) — The error-codes module owns the canonical `SMS.*` registry for `@suaveplan/sms`. Every error surfaced by the package —…
|
|
- [error-codes](./src/error-codes/error-codes.md) — The error-codes module owns the canonical `SMS.*` registry for `@suaveplan/sms`. Every error surfaced by the package —…
|
|
- [in-memory-provider](./src/in-memory-provider/in-memory-provider.md) — `InMemorySmsProvider` is the production-quality in-process `SmsProvider` implementation that ships with `@suaveplan/sms…
|
|
- [in-memory-provider](./src/in-memory-provider/in-memory-provider.md) — `InMemorySmsProvider` is the production-quality in-process `SmsProvider` implementation that ships with `@suaveplan/sms…
|
|
- [legacy-errors](./src/legacy-errors/legacy-errors.md) — `legacy-errors` preserves the upstream `@suaveplan/sms` v1 public surface — the `SmsError` class, the `SmsErrorCode` st…
|
|
- [legacy-errors](./src/legacy-errors/legacy-errors.md) — `legacy-errors` preserves the upstream `@suaveplan/sms` v1 public surface — the `SmsError` class, the `SmsErrorCode` st…
|
|
- [legacy-metadata](./src/legacy-metadata/legacy-metadata.md) — `legacy-metadata` preserves the upstream `SMS_ERROR_METADATA` table — the per-code mapping of `httpStatus`, `severity`,…
|
|
- [legacy-metadata](./src/legacy-metadata/legacy-metadata.md) — `legacy-metadata` preserves the upstream `SMS_ERROR_METADATA` table — the per-code mapping of `httpStatus`, `severity`,…
|
|
- [opt-out](./src/opt-out/opt-out.md) — `opt-out` is the in-memory opt-out registry that `SmsService` consults before every outbound message. Recipients on the…
|
|
- [opt-out](./src/opt-out/opt-out.md) — `opt-out` is the in-memory opt-out registry that `SmsService` consults before every outbound message. Recipients on the…
|
|
- [otel](./src/otel/metrics.md) — `otel/metrics` is the OpenTelemetry instrumentation layer for `@suaveplan/sms`. It owns four meter instruments — three…
|
|
- [metrics](./src/otel/metrics.md) — `otel/metrics` is the OpenTelemetry instrumentation layer for `@suaveplan/sms`. It owns four meter instruments — three…
|
|
- [rate-limiter](./src/rate-limiter/rate-limiter.md) — `rate-limiter` is the token-bucket throttle that `SmsService` consults before every outbound send. The limiter is keyed…
|
|
- [rate-limiter](./src/rate-limiter/rate-limiter.md) — `rate-limiter` is the token-bucket throttle that `SmsService` consults before every outbound send. The limiter is keyed…
|
|
- [retry](./src/retry/retry.md) — `retry` is the exponential-backoff retry policy used by `SmsService.send` to absorb transient provider failures. The po…
|
|
- [retry](./src/retry/retry.md) — `retry` is the exponential-backoff retry policy used by `SmsService.send` to absorb transient provider failures. The po…
|
|
- [routes](./src/routes/problem-json.md) — `routes/problem-json` is the RFC 7807 `application/problem+json` response helper for hosts wiring `@suaveplan/sms` erro…
|
|
- [problem-json](./src/routes/problem-json.md) — `routes/problem-json` is the RFC 7807 `application/problem+json` response helper for hosts wiring `@suaveplan/sms` erro…
|
|
- [scheduler](./src/scheduler/scheduler.md) — `scheduler` provides `MessageScheduler` — the minimal timer seam that `SmsService.scheduleAt` uses to fire a delayed de…
|
|
- [scheduler](./src/scheduler/scheduler.md) — `scheduler` provides `MessageScheduler` — the minimal timer seam that `SmsService.scheduleAt` uses to fire a delayed de…
|
|
- [schemas](./src/schemas/schemas.md) — `schemas` is the Zod boundary for every public input that `@suaveplan/sms` exposes. The schemas mirror the public type…
|
|
- [schemas](./src/schemas/schemas.md) — `schemas` is the Zod boundary for every public input that `@suaveplan/sms` exposes. The schemas mirror the public type…
|
|
- [bus-map](./src/service/bus-map.md) — `bus-map` declares `SmsBusMap` — the single-payload topic contract used by `SmsService.bus` (an `EventBus<SmsBusMap>`).…
|
|
- [legacy-emitter](./src/service/legacy-emitter.md) — `LegacyEmitter` is the back-compat shim that preserves the upstream `@suaveplan/sms` v1 `on/off/emit` listener API on t…
|
|
- [sms-service](./src/service/sms-service.md) — `SmsService` is the provider-agnostic orchestrator that ties together every primitive in `@suaveplan/sms`. A single `se…
|
|
- [templates](./src/templates/templates.md) — `templates` is the in-process template registry and renderer used by `SmsService` to expand `{{variable}}` placeholders…
|
|
- [templates](./src/templates/templates.md) — `templates` is the in-process template registry and renderer used by `SmsService` to expand `{{variable}}` placeholders…
|
|
- [validation](./src/validation/phone.md) — `validation/phone` is the strict-E.164 phone number validator used by every public input boundary in `@suaveplan/sms`.…
|
|
- [phone](./src/validation/phone.md) — `validation/phone` is the strict-E.164 phone number validator used by every public input boundary in `@suaveplan/sms`.…
|
|
<!-- end:auto-docs -->
|
|
|
|
## Highlights
|
|
|
|
- **`SmsService`** — orchestrates the full send pipeline: abort
|
|
check → provider check → E.164 validation → opt-out check →
|
|
rate-limit → template render → retry → provider send → delivery
|
|
tracker ingest → OTel + bus emit.
|
|
- **`InMemorySmsProvider`** — production-quality in-process
|
|
`SmsProvider` for tests and demos; records every sent message,
|
|
supports configurable failure predicates and status transitions.
|
|
- **`TemplateRegistry`** + `renderTemplate` — `{{var}}` substitution
|
|
with template-level and call-level `defaults` maps.
|
|
- **`OptOutRegistry`** — channel-scoped opt-out / opt-in with bulk
|
|
import/export.
|
|
- **`RateLimiter`** — token bucket per `(channel, recipient)` key
|
|
with injectable `Clock`.
|
|
- **`RetryPolicy`** — exponential backoff with full jitter,
|
|
AbortSignal-aware, injectable `Timers` seam.
|
|
- **`MessageScheduler`** — `scheduleAt(date, fn)` with cancel
|
|
handle, injectable timers + clock.
|
|
- **`DeliveryTracker`** — bounded LRU receipt cache with provider
|
|
fall-back via `lookupOrFetch`.
|
|
- **Typed `EventBus`** on `service.bus` plus the upstream
|
|
`on/off/emit` API mirroring against it.
|
|
- **Four OTel instruments**: `sms.sent_total`, `sms.failed_total`,
|
|
`sms.retry_total`, `sms.send_duration_ms`.
|
|
- **Thirteen `SMS.*` error codes** via `defineCodes("SMS", [...])`
|
|
plus the upstream `SmsError` legacy class preserved verbatim for
|
|
back-compat.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
bun add @suaveplan/sms
|
|
```
|
|
|
|
## Quick start
|
|
|
|
```ts
|
|
import {
|
|
InMemorySmsProvider,
|
|
SmsService,
|
|
} from "@suaveplan/sms";
|
|
|
|
const provider = new InMemorySmsProvider();
|
|
const service = new SmsService({ provider });
|
|
|
|
service.registerTemplate({
|
|
id: "welcome",
|
|
body: "Hi {{name}}!",
|
|
variables: ["name"],
|
|
});
|
|
|
|
service.bus.on("sms.sent", (report) => {
|
|
console.log("delivered", report.messageId, "→", report.to);
|
|
});
|
|
|
|
await service.send({
|
|
to: "+15551234567",
|
|
body: "ignored when templateId is set",
|
|
templateId: "welcome",
|
|
templateVars: { name: "Alice" },
|
|
});
|
|
```
|
|
|
|
## Modules
|
|
|
|
- [`error-codes/error-codes.md`](./src/error-codes/error-codes.md) — `defineCodes("SMS", [...])`, leaf classes
|
|
- [`legacy-errors/legacy-errors.md`](./src/legacy-errors/legacy-errors.md) — upstream `SmsError` back-compat
|
|
- [`legacy-metadata/legacy-metadata.md`](./src/legacy-metadata/legacy-metadata.md) — `SMS_ERROR_METADATA` table
|
|
- [`schemas/schemas.md`](./src/schemas/schemas.md) — Zod boundary parsers
|
|
- [`validation/phone.md`](./src/validation/phone.md) — strict E.164 validator
|
|
- [`templates/templates.md`](./src/templates/templates.md) — `TemplateRegistry`, `renderTemplate`
|
|
- [`opt-out/opt-out.md`](./src/opt-out/opt-out.md) — `OptOutRegistry`
|
|
- [`rate-limiter/rate-limiter.md`](./src/rate-limiter/rate-limiter.md) — token-bucket limiter
|
|
- [`retry/retry.md`](./src/retry/retry.md) — `RetryPolicy`
|
|
- [`scheduler/scheduler.md`](./src/scheduler/scheduler.md) — `MessageScheduler`
|
|
- [`delivery-tracker/delivery-tracker.md`](./src/delivery-tracker/delivery-tracker.md) — bounded LRU receipt cache
|
|
- [`in-memory-provider/in-memory-provider.md`](./src/in-memory-provider/in-memory-provider.md) — `InMemorySmsProvider`
|
|
- [`service/sms-service.md`](./src/service/sms-service.md) — orchestrator
|
|
- [`service/legacy-emitter.md`](./src/service/legacy-emitter.md) — legacy `on/off/emit` mirror
|
|
- [`routes/problem-json.md`](./src/routes/problem-json.md) — RFC 7807 Hono helper
|
|
- [`otel/metrics.md`](./src/otel/metrics.md) — OpenTelemetry instruments
|
|
|