← All @molecule/* packages · App templates

@molecule/api-sms-capture

Provider bond · sms · API (Node) · v1.0.2 · Apache-2.0

SMS capture provider for molecule.dev

npm install @molecule/api-sms-capture

npm · Source on GitHub · Implements @molecule/api-sms

How it works

@molecule/api-sms-capture is a provider bond on the API (Node) side: it implements the sms core interface (@molecule/api-sms) with a concrete vendor or library behind it.

Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.

import { setProvider } from '@molecule/api-sms'
import { provider } from '@molecule/api-sms-capture'

setProvider(provider) // intercept-only: nothing is actually sent

// Tee mode: really send AND record the real outcome
// import { createSMSCaptureProvider } from '@molecule/api-sms-capture'
// import { createProvider as twilio } from '@molecule/api-sms-twilio'
// setProvider(createSMSCaptureProvider(twilio()))

Works with: @molecule/api-activity, @molecule/api-sms

Reference

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

SMS capture provider for molecule.dev.

Records every send() / sendBulk() call as an activity event. Intercept-only by default; delegates + tees when wrapping a real provider.

Quick Start

import { setProvider } from '@molecule/api-sms'
import { provider } from '@molecule/api-sms-capture'

setProvider(provider) // intercept-only: nothing is actually sent

// Tee mode: really send AND record the real outcome
// import { createSMSCaptureProvider } from '@molecule/api-sms-capture'
// import { createProvider as twilio } from '@molecule/api-sms-twilio'
// setProvider(createSMSCaptureProvider(twilio()))

Type

provider

Installation

npm install @molecule/api-sms-capture @molecule/api-activity @molecule/api-sms

API

Functions

createSMSCaptureProvider(realProvider)

Creates an SMS capture provider.

When realProvider is provided, each message is delivered through it and the captured event records the real outcome (delegate + tee). When omitted (the dev default), messages are intercepted and a synthetic SMSResult is returned.

function createSMSCaptureProvider(realProvider?: SMSProvider): SMSProvider
  • realProvider — Optional real provider to delegate to and tee.

Returns: An {@link SMSProvider} that records activity for every send.

Constants

provider

Default SMS capture provider (intercept-only).

const provider: SMSProvider

Core Interface

Implements @molecule/api-sms interface.

Bond Wiring

Setup function to register this provider with the core interface:

import { setProvider } from '@molecule/api-sms'
import { provider } from '@molecule/api-sms-capture'

export function setupSmsCapture(): void {
  setProvider(provider)
}

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-activity ^1.0.1
  • @molecule/api-sms ^1.0.1

Runtime Dependencies

  • @molecule/api-activity

  • @molecule/api-sms

  • Two modes, and the choice decides whether the message is DELIVERED. INTERCEPT-ONLY (provider, or createSMSCaptureProvider() with no argument) records the message and returns a synthetic success — nothing reaches the handset. DELEGATE + TEE (createSMSCaptureProvider(real)) sends through the real provider AND records the real outcome. Anywhere real messages must go out (production), wrap the real provider — never bond the intercept-only provider.

  • Recording is best-effort: a bonded ActivitySink that throws NEVER changes the outcome of send() — a successful real send still resolves and a failed one still rejects with the REAL provider error.

  • Captured messages go to the bonded ACTIVITY SINK (@molecule/api-activity — e.g. the sandbox's sink read by the read_activity tool). Without a sink bonded, record() is a silent no-op: intercept-only send() still returns a synthetic success and the message is visible nowhere. Wire an activity sink before relying on captures (OTP flows, the E2E checklist).

  • Intercept-only mode returns synthetic results: send()status: 'sent' with id captured-<uuid>, and getStatus() ALWAYS reports 'sent'. In tee mode both reflect the wrapped real provider (whose getStatus() support varies — see @molecule/api-sms).

  • Events record status: 'captured' when intercepting, 'sent'/'failed' when teeing a real provider — so the activity feed never shows a delivery that didn't happen.

E2E Tests

Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:

  • Each SMS-triggering flow (phone verification, OTP login, alerts the app defines) confirms the send in the UI and a message actually reaches the transport. The sandbox CAPTURES outbound SMS instead of sending — read it with the read_activity tool (filter type 'sms'); the code/link is in its payload. Never mock the flow or modify production code to expose it.
  • The OTP round-trip completes: request a code → read the captured message's code → enter it in the UI → the flow advances; a wrong or expired code is rejected with a visible error.
  • Messages go only to the authenticated user's own verified number — no UI or endpoint lets a caller text an arbitrary number (spam/abuse vector).