Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
# `digest-channel` — Digest delivery channel
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`digest-channel.ts` ships the `DigestChannel` implementation that batches notifications into periodic summaries delivered through a delegate `DeliveryChannel`. It is the channel-shape adapter for "weekly summary email" / "hourly Slack roll-up" / "daily push digest" use-cases — it implements `DeliveryChannel` so it slots into the router exactly like a direct channel, and it owns its own per-recipient queue.
## Features
- Wraps any `DeliveryChannel` as `delegate`.
- Per-recipient FIFO queue with configurable `maxPerDigest` cap.
- `intervalMs` gates the next flush — `send` opportunistically flushes when the interval has elapsed since the last flush.
- Customisable `formatDigest(notifications)` formatter.
- `flush(recipient)`, `flushAll()`, `queueSize`, `pendingRecipients` for ops/cron integrations.
- Injectable `Clock` so tests advance the interval without sleeping.
## Usage: Basic
```ts
import { DigestChannel, EmailChannel } from "@suaveplan/notification-channel";
const email = new EmailChannel({ from: "noreply@x", sendFn });
const digest = new DigestChannel({
delegate: email,
intervalMs: 3_600_000,
});
await digest.send(notification);
```
## Usage: Advanced
```ts
const digest = new DigestChannel({
delegate: email,
intervalMs: 86_400_000,
maxPerDigest: 25,
formatDigest: (ns) =>
ns.map((n, i) => `${i + 1}. [${n.priority}] ${n.subject}: ${n.body}`).join("\n"),
clock: () => Date.now(),
});
// In a cron handler:
const results = await digest.flushAll();
```
## API Reference
- `DigestChannel(config: DigestChannelConfig)` — constructor.
- `DigestChannelConfig` — `{ delegate, intervalMs?, maxPerDigest?, formatDigest?, clock? }`.
- `enqueue(n)`, `send(n)`, `flush(recipient)`, `flushAll()`, `queueSize(recipient)`, `pendingRecipients()`, `canSend(n)`.
## Implementation Notes
`enqueue` silently drops the overflow when the queue reaches `maxPerDigest`. This is a deliberate denial-of-service shield — a runaway pipeline cannot blow memory by enqueuing forever. The default formatter numbers entries and labels them by subject; consumers wanting Markdown / HTML output supply their own `formatDigest`. `canSend` proxies to the delegate, so the digest channel inherits the delegate's recipient validation — useful when the delegate is an `EmailChannel` that wants `@`-only recipients. `lastFlush` is tracked per-recipient and consulted from `send`/`flushAll`; a recipient flushed seconds ago will not flush again until the interval has elapsed.