Files
suaveplan/packages/communication/messaging/notification-channel/src/digest/digest-channel.md
2026-05-27 23:29:53 +01:00

2.6 KiB

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

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

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.