Move docs/ package mirror into document-navigator plugin

Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:10:00 +01:00
parent fe3fedf42e
commit 9bf56a806b
6022 changed files with 12437 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
# Vonage — `@suaveplan/sms-providers`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Real Vonage (formerly Nexmo) SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/sms/json` with `api_key` + `api_secret` carried in the form body (the documented default authentication mode), auto-selecting `type=unicode` when the body contains a non-ASCII codepoint and `type=text` otherwise. Sender (`from`) supports alphanumeric IDs (up to 11 chars), short codes, and long codes — Vonage accepts the raw string and we pass it through verbatim. Status is fetched via `GET /search/message`. Webhook signature verification follows Vonage's documented HMAC-SHA256 scheme over the sorted form fields excluding the `sig` parameter.
The class holds no in-flight state and routes every HTTP call through the injectable `FetchLike` seam. Tests stand up a `node:http` server on a random port and inject a `fetch` adapter that points at it.
## Features
- Form-body auth (`api_key`, `api_secret`) — matches Vonage's documented default.
- Auto `type=unicode` vs `type=text` based on whether the body contains a non-ASCII codepoint.
- Sender field forwarded verbatim (alpha / short code / long code all supported).
- `getStatus(id)` round-trips `GET /search/message?id=…` with the same auth pattern.
- `mapStatus` covers `submitted` / `buffered` / `accepted` / `delivered` / `expired` / `failed` / `rejected`.
- `verifyWebhookSignature(params, signature, signatureSecret)` — HMAC-SHA256 over `&key=value&…` for sorted keys (excluding `sig`); lowercase-hex comparison in constant time.
- `parseInbound(params)` discriminates between inbound messages and DLR (delivery-receipt) payloads.
- OTel — counter + histogram for every send + verify.
## Usage: Basic
```ts
import { VonageProvider } from "@suaveplan/sms-providers";
const vonage = new VonageProvider({
apiKey: "vonage_api_key",
apiSecret: "vonage_api_secret",
});
const r = await vonage.send({
to: "447700900000",
from: "Suaveplan",
body: "Hello",
});
```
## Usage: Advanced
```ts
import { VonageProvider } from "@suaveplan/sms-providers";
const vonage = new VonageProvider({
apiKey: "k",
apiSecret: "s",
timeoutMs: 5_000,
});
const ok = await vonage.verifyWebhookSignature(
{ messageId: "0A0000001", text: "hi", sig: "<hex>" },
"<hex>",
"signatureSecretSharedWithVonage"
);
```
## API Reference
| Symbol | Kind | Notes |
|---|---|---|
| `VonageProvider` | class | Implements `SmsProvider`. |
| `send(message, options?)` | method | POST `/sms/json`, returns `SmsDeliveryReport`. |
| `getStatus(id)` | method | `GET /search/message?id=…`. |
| `mapStatus(raw)` | method | Canonical mapping. |
| `verifyWebhookSignature(params, sig, signatureSecret)` | method | HMAC-SHA256 hex verifier; throws `SIGNATURE_INVALID` on mismatch. |
| `parseInbound(params)` | method | Discriminates into `InboundSmsEvent`. |
## Implementation Notes
Vonage strips the leading `+` from E.164 `to` numbers on submission, so the implementation `replace(/^\+/, "")` once before form-encoding. The signature payload is the documented `&{k}={v}&…` shape (note the leading `&`), built from a stable lexicographic key sort with `sig` excluded.