Files
2026-05-27 23:29:53 +01:00

9.9 KiB

@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.

Documentation tree

  • delivery-tracker — delivery-tracker is the bounded LRU cache of SmsDeliveryReport records that SmsService consults for delivery stat…
    • delivery-tracker — delivery-tracker is the bounded LRU cache of SmsDeliveryReport records that SmsService consults for delivery stat…
  • error-codes — The error-codes module owns the canonical SMS.* registry for @suaveplan/sms. Every error surfaced by the package —…
    • error-codes — The error-codes module owns the canonical SMS.* registry for @suaveplan/sms. Every error surfaced by the package —…
  • in-memory-provider — InMemorySmsProvider is the production-quality in-process SmsProvider implementation that ships with `@suaveplan/sms…
    • in-memory-provider — InMemorySmsProvider is the production-quality in-process SmsProvider implementation that ships with `@suaveplan/sms…
  • legacy-errors — legacy-errors preserves the upstream @suaveplan/sms v1 public surface — the SmsError class, the SmsErrorCode st…
    • legacy-errors — legacy-errors preserves the upstream @suaveplan/sms v1 public surface — the SmsError class, the SmsErrorCode st…
  • legacy-metadata — legacy-metadata preserves the upstream SMS_ERROR_METADATA table — the per-code mapping of httpStatus, severity,…
    • legacy-metadata — legacy-metadata preserves the upstream SMS_ERROR_METADATA table — the per-code mapping of httpStatus, severity,…
  • opt-out — opt-out is the in-memory opt-out registry that SmsService consults before every outbound message. Recipients on the…
    • opt-out — opt-out is the in-memory opt-out registry that SmsService consults before every outbound message. Recipients on the…
  • otel — otel/metrics is the OpenTelemetry instrumentation layer for @suaveplan/sms. It owns four meter instruments — three…
    • metrics — otel/metrics is the OpenTelemetry instrumentation layer for @suaveplan/sms. It owns four meter instruments — three…
  • rate-limiter — rate-limiter is the token-bucket throttle that SmsService consults before every outbound send. The limiter is keyed…
    • rate-limiter — rate-limiter is the token-bucket throttle that SmsService consults before every outbound send. The limiter is keyed…
  • retry — retry is the exponential-backoff retry policy used by SmsService.send to absorb transient provider failures. The po…
    • retry — retry is the exponential-backoff retry policy used by SmsService.send to absorb transient provider failures. The po…
  • routes — routes/problem-json is the RFC 7807 application/problem+json response helper for hosts wiring @suaveplan/sms erro…
    • problem-json — routes/problem-json is the RFC 7807 application/problem+json response helper for hosts wiring @suaveplan/sms erro…
  • scheduler — scheduler provides MessageScheduler — the minimal timer seam that SmsService.scheduleAt uses to fire a delayed de…
    • scheduler — scheduler provides MessageScheduler — the minimal timer seam that SmsService.scheduleAt uses to fire a delayed de…
  • schemas — schemas is the Zod boundary for every public input that @suaveplan/sms exposes. The schemas mirror the public type…
    • schemas — schemas is the Zod boundary for every public input that @suaveplan/sms exposes. The schemas mirror the public type…
  • bus-map — bus-map declares SmsBusMap — the single-payload topic contract used by SmsService.bus (an EventBus<SmsBusMap>).…
  • legacy-emitter — LegacyEmitter is the back-compat shim that preserves the upstream @suaveplan/sms v1 on/off/emit listener API on t…
  • sms-service — SmsService is the provider-agnostic orchestrator that ties together every primitive in @suaveplan/sms. A single `se…
  • templates — templates is the in-process template registry and renderer used by SmsService to expand {{variable}} placeholders…
    • templates — templates is the in-process template registry and renderer used by SmsService to expand {{variable}} placeholders…
  • validation — validation/phone is the strict-E.164 phone number validator used by every public input boundary in @suaveplan/sms.…
    • phone — validation/phone is the strict-E.164 phone number validator used by every public input boundary in @suaveplan/sms.…

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

bun add @suaveplan/sms

Quick start

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