first commit
This commit is contained in:
87
docs/communication/messaging/sms/src/opt-out/opt-out.md
Normal file
87
docs/communication/messaging/sms/src/opt-out/opt-out.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# `opt-out`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`opt-out` is the in-memory opt-out registry that `SmsService`
|
||||
consults before every outbound message. Recipients on the
|
||||
registry receive no further messages — `service.send` throws
|
||||
`SmsRecipientOptedOutError` (code `SMS.RECIPIENT_OPTED_OUT`).
|
||||
Entries can be scoped to a channel so transactional messages
|
||||
remain deliverable when only marketing has been opted out by the
|
||||
host application.
|
||||
|
||||
The registry is intentionally pluggable but ships as an in-process
|
||||
`Map` so unit tests and small deployments need no external state;
|
||||
production hosts that share opt-out across processes can wrap
|
||||
`OptOutRegistry` in their own persistence layer.
|
||||
|
||||
## Features
|
||||
|
||||
- `add(phoneNumber, channel?)` — records the opt-out with a
|
||||
timestamp from an injectable `Clock`.
|
||||
- `remove(phoneNumber)` — opt-in helper.
|
||||
- `has(phoneNumber)` and `get(phoneNumber)` — predicate +
|
||||
detail accessors.
|
||||
- `list()` snapshot, `size()`, and `clear()` for bulk operations.
|
||||
- `importEntries(entries)` — bulk import with overwrite-by-id
|
||||
semantics for syncing from an external source.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { OptOutRegistry } from "@suaveplan/sms/opt-out";
|
||||
|
||||
const reg = new OptOutRegistry();
|
||||
reg.add("+15551234567", "marketing");
|
||||
console.log(reg.has("+15551234567")); // true
|
||||
reg.remove("+15551234567");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { OptOutRegistry } from "@suaveplan/sms/opt-out";
|
||||
|
||||
// Inject a deterministic clock for tests; importEntries
|
||||
// reseeds the registry from a downstream cache.
|
||||
const reg = new OptOutRegistry({ clock: () => 1_700_000_000_000 });
|
||||
reg.importEntries([
|
||||
{
|
||||
phoneNumber: "+15550000001",
|
||||
optedOutAt: new Date(1_700_000_000_000),
|
||||
channel: "marketing",
|
||||
},
|
||||
{
|
||||
phoneNumber: "+15550000002",
|
||||
optedOutAt: new Date(1_700_000_000_000),
|
||||
},
|
||||
]);
|
||||
console.log(reg.size()); // 2
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `class OptOutRegistry`
|
||||
- `constructor(options?: { clock?: Clock })`
|
||||
- `add(phoneNumber: string, channel?: string): OptOutEntry`
|
||||
- `remove(phoneNumber: string): boolean`
|
||||
- `has(phoneNumber: string): boolean`
|
||||
- `get(phoneNumber: string): OptOutEntry | undefined`
|
||||
- `list(): ReadonlyArray<OptOutEntry>`
|
||||
- `importEntries(entries: ReadonlyArray<OptOutEntry>): void`
|
||||
- `clear(): void`
|
||||
- `size(): number`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The registry stores `OptOutEntry` records keyed by the verbatim
|
||||
phone number — no normalisation. Callers SHOULD pass E.164
|
||||
strings; mismatched formats will fail to match. `add` overwrites
|
||||
existing entries by `phoneNumber`, so a caller switching the
|
||||
`channel` on an existing opt-out simply re-adds. The `clock`
|
||||
defaults to `Date.now`, deterministic only when explicitly
|
||||
injected.
|
||||
Reference in New Issue
Block a user