# `digest-channel` — Digest delivery channel _(no submodules)_ ## 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.