@maxnate/sms-core
v0.1.0
Published
Framework-agnostic SMS provider abstraction. Define adapters, register providers, send messages — zero runtime dependencies.
Downloads
108
Maintainers
Readme
@maxnate/sms-core
Zero-dependency TypeScript library for building SMS / messaging provider plugins. Define adapters, register providers, send messages — framework-agnostic, works in Node.js, Edge, Deno, and Bun.
npm install @maxnate/sms-coreWhy sms-core?
| Concern | sms-core handles | You handle |
|---|---|---|
| Provider interface | SmsProviderAdapter with lifecycle hooks | Implementing per provider |
| Registry | createSmsProviderRegistry() — register, configure, route | Wiring tenancy + persistence |
| Tenant isolation | Per-tenant config + idempotency | Providing ConfigStore + SmsIdempotencyStore |
| Crypto | HMAC-SHA256 / SHA1, timing-safe compare — Web Crypto API | Nothing |
| Idempotency | Pluggable store with TTL (SMS_IDEMPOTENCY_KEY_TTL_SECONDS) | Backing store (Redis/DB) |
| Errors | SmsProviderError, SmsIdempotencyKeyError, SmsWebhookVerificationError | Catching + reporting |
Available Providers
| Package | Provider | Channels | Webhook Signing |
|---|---|---|---|
| @maxnate/provider-twilio | Twilio | SMS, WhatsApp | HMAC-SHA1 (X-Twilio-Signature) |
Additional providers (Africa's Talking, MessageBird, Vonage, AWS SNS) can be added by implementing
SmsProviderAdapter.
Quick Start
import { createSmsProviderRegistry } from '@maxnate/sms-core'
import { TwilioSmsProvider } from '@maxnate/provider-twilio'
const registry = createSmsProviderRegistry({
configStore: myConfigStore, // ConfigStore<SmsProviderConfigAtRest>
secretsCodec: mySecretsCodec, // SecretsCodec — encrypt/decrypt credentials
idempotencyStore: myIdempotencyStore // optional, in-memory by default
})
registry.register(new TwilioSmsProvider())
await registry.configureProvider('tenant-1', {
id: 'twilio', name: 'Twilio', enabled: true,
credentials: { accountSid: '...', authToken: '...' },
fromNumber: '+15551234567',
channels: ['sms']
})
const result = await registry.send('tenant-1', 'twilio', {
to: '+15551112222',
body: 'Your code is 123456',
channel: 'sms'
})Architecture
@maxnate/sms-core ← This package (interface + registry + crypto + errors)
@maxnate/provider-twilio ← Adapter implementing SmsProviderAdapter
@maxnate/provider-* ← Community adaptersKey Concepts
SmsProviderAdapter
interface SmsProviderAdapter {
providerId: string
providerName: string
initialize(config: SmsProviderConfig): void | Promise<void>
send(message: SmsMessage): Promise<SmsResult>
verifyWebhook?(payload, signature, headers?): Promise<boolean>
handleWebhook?(payload, signature?, headers?): Promise<SmsWebhookEvent>
supportsFeature?(feature: keyof SmsProviderFeature): boolean
}Registry (tenant-scoped)
registry.register(provider) // Add a provider class
registry.unregister(providerId) // Remove
registry.get(providerId) // Get adapter
registry.getAll() // All registered adapters
registry.getEnabled(tenantId) // Enabled for a tenant
registry.getConfig(tenantId, providerId) // Read config
registry.configureProvider(tenantId, config) // Persist + initialize
registry.send(tenantId, providerId, message) // Send via tenant configCrypto Utilities
import {
hmacSha256Hex, hmacSha1Base64, timingSafeEqual,
hexToBytes, bufferToHex, bufferToBase64
} from '@maxnate/sms-core'All functions use the Web Crypto API — no Node.js dependencies.
Creating a Provider
import { BaseSmsProvider, SmsMessage, SmsResult } from '@maxnate/sms-core'
export class MyProvider extends BaseSmsProvider {
providerId = 'my-provider'
providerName = 'My Provider'
async send(message: SmsMessage): Promise<SmsResult> {
// ... implement
}
}BaseSmsProvider provides default initialize, supportsFeature, and capability flags.
Plugin Integration
For Maxnate-platform users, install @maxnate/plugin-sms which exposes the smsModule capability via ctx.extend('smsModule', module) and ships an /admin/sms UI.
Requirements
- Node.js >= 18 (or any runtime with Web Crypto API)
- TypeScript 5.x recommended
- Zero runtime dependencies
License
MIT
