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

5.2 KiB

Enterprise migration: @suaveplan/sms

  • Change ID: enterprise-sms
  • Status: PROPOSED
  • Capability: sms
  • Target package: @suaveplan/sms at 0.1.0
  • Target path: packages/communication/messaging/sms/
  • Design: ./design.md
  • Spec delta: ./specs/sms/spec.md
  • Source on main: /mnt/s/web/suaveplan/packages/server/sms (~1389 LOC)
  • Enterprise plan: /mnt/s/web/suaveplan/enterprise/sms.md

Why

@suaveplan/sms is the universal SMS core: a provider-agnostic sending pipeline with template rendering, opt-out management, rate limiting, scheduling, retry, and delivery tracking. Providers (Twilio, Vonage, MessageBird, AWS SNS, in-house gateways) plug into the abstract SmsProvider contract.

The upstream package (v1.0.2) is functionally narrow — provider seam, template render, opt-out list, four events — but is missing every enterprise lever the genesis tree commits to:

  • BaseError legacy hierarchy instead of defineCodes("SMS", [...]).
  • No Zod boundary on SmsMessage, SmsTemplate, SmsProvider inputs.
  • No OpenTelemetry counters or histograms.
  • No retry/backoff on transient provider failures.
  • No scheduling (sendAt) or batched dispatch.
  • No rate limiting per provider / per recipient.
  • No delivery-receipt ingest pipeline; status is fetched on demand.
  • No AbortSignal propagation on send / scheduler / status pulls.
  • No in-memory test provider; tests use ad-hoc anonymous mocks.
  • One global event-emitter shape; no typed EventBus bridge.
  • Tests pass with vi-mocks (@suaveplan/testing vi.fn()) — fine for unit harnesses, but the package never exercises a real provider round-trip end-to-end.

This change rewrites the package on genesis at enterprise quality while preserving every upstream export, then layers the SMS-core features (MessageScheduler, RateLimiter, DeliveryTracker, RetryPolicy, OTel instruments, Zod boundaries, abort signals, EventBus bridge, in-memory test provider).

Parity Checklist

Capability Upstream Migrated Notes
SmsService send / template render / opt-out ✅ ✅ Preserved verbatim, plus rate-limit + abort + retry on top.
SmsProvider abstract contract ✅ ✅ Preserved (name, send, getStatus).
SmsTemplate ({{var}} substitution) ✅ ✅ Preserved + defaults map + escape pass.
SmsMessage / SmsDeliveryReport / SmsDeliveryStatus ✅ ✅ Preserved + Zod schemas.
OptOutEntry registry ✅ ✅ Preserved + bulk import / export.
SmsEventMap (sms.sent/delivered/failed/opted_out/opted_in) ✅ ✅ Preserved + new sms.scheduled / sms.retry / sms.rate_limited.
Legacy EventEmitter surface (on/off/emit) ✅ ✅ Preserved (delegates to typed EventBus).
SmsError legacy class + factory methods ✅ ✅ Preserved as back-compat alias over genesis defineCodes family.
respondProblemJson Hono helper ✅ ✅ Preserved.
registerSmsErrorCodes / SMS_ERROR_METADATA ✅ ✅ Preserved via genesis ErrorCodes.registerMany.
Stable SMS.* defineCodes registry 🚫 (legacy BaseError) ⬆️ Genesis addition.
Zod boundary on every public input 🚫 ⬆️ Genesis addition.
OTel counters + histogram 🚫 ⬆️ Genesis addition (4 instruments).
MessageScheduler (sendAt + cancel) 🚫 ⬆️ Genesis addition.
RateLimiter (token bucket per recipient) 🚫 ⬆️ Genesis addition.
DeliveryTracker (receipt ingest + cache) 🚫 ⬆️ Genesis addition.
RetryPolicy (exponential backoff w/ jitter) 🚫 ⬆️ Genesis addition.
InMemorySmsProvider (test provider) 🚫 ⬆️ Genesis addition; replaces ad-hoc test mocks.
AbortSignal propagation 🚫 ⬆️ Genesis addition.
100/100/100/100 coverage 🚫 ⬆️ Genesis gate.

What

A net-new package at packages/communication/messaging/sms/ at 0.1.0, universal tier (no Node/Bun-only APIs), Zod-validated boundaries, OTel-instrumented, with the full upstream public surface plus the genesis enterprise additions.

This is the SMS core — not the providers package. The abstract SmsProvider interface ships with an in-memory test provider (InMemorySmsProvider) so consumers and integration tests can run without external services. Concrete provider integrations (Twilio, Vonage, AWS SNS) ship in the sibling @suaveplan/sms-providers package next wave.

Impact

Affected dependents

Run bunx turbo ls --filter=...@suaveplan/sms to enumerate.

No genesis package currently imports @suaveplan/sms:

  • @suaveplan/sms-providers — pending migration, will consume this as workspace:^0.1.0. [x] no action needed (reason: package not yet on genesis)
  • @suaveplan/api-plugin-messaging — pending migration. [x] no action needed (reason: package not yet on genesis)

Foundation deps (@suaveplan/error, @suaveplan/events, @suaveplan/types) are read-only consumers; no change required.

Out of scope

Nothing. Per feedback_no_deferrals_on_migration.md and AGENTS §2b, no "phase 2" is permitted. The whole upstream surface plus the enterprise additions land here, feature-complete, with 100/100/100/100 coverage.