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

6.1 KiB
Raw Permalink Blame History

Enterprise migration — @suaveplan/sms-providers

Why

@suaveplan/sms shipped a clean send/receive/template/scheduler/opt-out core with InMemorySmsProvider as the canonical test double. Production deployments need real provider clients — Twilio, Vonage, MessageBird, Plivo, AWS SNS — each speaking its own REST dialect, authenticating with its own scheme, returning its own status vocabulary, and signing its own webhooks. Upstream packages/universal/sms-providers shipped simulated providers (random IDs, no network IO, no signature verification) — not enterprise grade.

This change ports the package fresh under packages/communication/messaging/sms-providers/ (per assignment.yml), replacing every simulated implementation with real HTTP clients over an injectable FetchLike seam, real HMAC/signature verifiers for each provider's inbound webhook contract, and a typed SMS_PROVIDERS.* error registry. Tests bind a real node:http server on a random port for end-to-end flows; no mocks.

What changes

  • ADDED Package @suaveplan/sms-providers at packages/communication/messaging/sms-providers/, version 0.1.0, universal-tier src/ (zero hard browser/node deps; injectable fetch + injectable HMAC seam).
  • ADDED SMS_PROVIDERS.* namespaced error code registry via defineCodes("SMS_PROVIDERS", […]) with eight codes: HTTP_ERROR, TIMEOUT, PARSE_ERROR, CONFIG_MISSING, SIGNATURE_INVALID, INVALID_NUMBER, PROVIDER_NOT_REGISTERED, PROVIDER_ALREADY_REGISTERED.
  • ADDED SmsProvidersError discriminated subclass of SuaveplanError carrying provider, httpStatus, url, bodyExcerpt discriminants; factory helpers per code.
  • ADDED Slim foundation: httpRequest over FetchLike with composed AbortSignal + per-call timeout + JSON parse + typed error translation; composeAbortSignal helper.
  • ADDED ProviderRegistry indexing SmsProvider instances by name (insertion-ordered, strict register/lookup, frozen list()).
  • ADDED Five real provider clients implementing SmsProvider from @suaveplan/sms:
    1. TwilioProvider — POST /2010-04-01/Accounts/{AccountSid}/Messages.json, HTTP Basic auth (AccountSid:AuthToken), form-encoded body, MMS MediaUrl pass-through, status URL → polled, X-Twilio-Signature HMAC-SHA1 webhook verifier.
    2. VonageProvider — POST /sms/json, api_key+api_secret body params, alphanumeric / short-code / long-code from, HMAC-SHA256 webhook signature verifier honouring sig/timestamp.
    3. MessageBirdProvider — POST /messages, AccessKey <key> auth header, JSON body, Messagebird-Signature-Jwt JWT (HS256) webhook verifier with timestamp freshness window.
    4. PlivoProvider — POST /v1/Account/{AuthId}/Message/, HTTP Basic auth (AuthId:AuthToken), JSON body, X-Plivo-Signature-V3 HMAC-SHA256 webhook verifier (canonical-string of URL+nonce+body).
    5. AwsSnsProvider — POST / to sns.{region}.amazonaws.com with AWS SigV4 signing (computed inline; no aws-sdk dep), Action=Publish form body, message-attribute mapped sender + SMSType (Transactional/Promotional); SNS webhook verifier accepts subscription confirmation, notification, and unsubscribe-confirmation payloads with PEM-cert injectable resolver.
  • ADDED Per-provider raw-status → SmsDeliveryStatus map; injectable Clock + IdGenerator per provider for deterministic message-id fallbacks (when the provider does not return one synchronously).
  • ADDED Inbound webhook router pipeline per provider: signature verify → header timestamp freshness check → discriminated InboundSmsEvent (message_received | delivery_receipt | opt_out | opt_in) — opt-in/opt-out keywords (STOP/UNSTOP/HELP family) recognised from inbound body.
  • ADDED OTel instruments (5):
    • counter sms_providers.send_total{provider,outcome}
    • counter sms_providers.webhook_total{provider,event,outcome}
    • counter sms_providers.signature_failures_total{provider}
    • histogram sms_providers.send_duration_ms{provider}
    • histogram sms_providers.webhook_verify_duration_ms{provider}
  • ADDED Real node:http round-trip integration tests per provider (test binds an HTTP server on a random port, captures the request, returns a canned response, asserts URL/method/headers/body) and real-node:crypto-driven webhook signature suites (accept + reject paths).
  • ADDED Co-located .md per .ts (six required sections, ≥200 words excl. code) and a root README.md.
  • MODIFIED STATUS.md row for @suaveplan/sms-providers flipped – → ✅.

Parity Checklist

Capability Upstream Migrated Notes
TwilioProvider simulated send ✅ ⬆️ Replaced: real HTTP Basic + form-encoded POST + MMS MediaUrl + status poll + X-Twilio-Signature HMAC-SHA1 verifier.
VonageProvider simulated send ✅ ⬆️ Replaced: real POST + alpha/short/long sender parsing + HMAC-SHA256 webhook verifier.
MessageBirdProvider simulated send ✅ ⬆️ Replaced: real AccessKey POST + JWT (HS256) webhook verifier with timestamp freshness.
AwsSnsProvider simulated send ✅ ⬆️ Replaced: real SigV4-signed POST to sns.<region>.amazonaws.com + PEM-cert injectable SNS verifier.
PlivoProvider 🚫 ✅ ADDED — orchestrator scope explicitly names Plivo.
httpRequest helper ✅ ⬆️ Re-implemented over injectable FetchLike + composed AbortSignal + SMS_PROVIDERS.* codes.
SMS_PROVIDER_* codes on shared ErrorCodes ✅ ⬆️ Migrated to genesis-native defineCodes("SMS_PROVIDERS", […]).
mapStatus(raw) per provider ✅ ✅ Preserved on every provider class.
Webhook signature verification 🚫 ✅ ADDED — production requirement.
Provider registry 🚫 ✅ ADDED — multiplexing by name (mirrors @suaveplan/payment-providers).
OTel instrumentation 🚫 ✅ ADDED — production requirement.
node:http round-trip tests 🚫 ✅ ADDED — orchestrator requirement; real in-process server, no mocks.
Inbound opt-in/opt-out keyword detection 🚫 ✅ ADDED — required for compliance integration with @suaveplan/sms's OptOutRegistry.