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

2.8 KiB

delivery-channel — Common transport contract

Purpose

delivery-channel.ts defines the structural contract every concrete delivery channel implements. The contract is intentionally tiny: a name, a synchronous canSend(notification) filter, and an async send(notification) that returns a DeliveryResult. Channels MUST swallow transport-level exceptions and surface failures via DeliveryResult { success: false, error } so the router can decide whether to retry, dead-letter, or emit notification.failed without try/catching every channel.

Features

  • Single DeliveryChannel interface for every transport.
  • DeliveryResult shape carries success flag, channel name, optional messageId / error, and a timestamp.
  • Synchronous canSend so routers can probe support without awaiting.
  • No coupling to a specific provider — the integration point is each channel's own sendFn/fetchFn callback.

Usage: Basic

import type {
    DeliveryChannel,
    DeliveryResult,
} from "@suaveplan/notification-channel";

const noop: DeliveryChannel = {
    name: "noop",
    canSend: () => true,
    async send(): Promise<DeliveryResult> {
        return {
            success: true,
            channel: "noop",
            messageId: "noop-0",
            timestamp: new Date().toISOString(),
        };
    },
};

Usage: Advanced

import type { DeliveryChannel } from "@suaveplan/notification-channel";

class LoggingChannel implements DeliveryChannel {
    public readonly name = "logging";
    constructor(private readonly inner: DeliveryChannel) {}
    canSend(n: import("@suaveplan/notification-channel").Notification) {
        return this.inner.canSend(n);
    }
    async send(n: import("@suaveplan/notification-channel").Notification) {
        const start = Date.now();
        const r = await this.inner.send(n);
        console.error(`[${this.inner.name}] ${r.success ? "ok" : "fail"} +${Date.now() - start}ms`);
        return r;
    }
}

API Reference

  • DeliveryChannel — interface.
    • name: string — unique transport identifier (matches the channel key the router routes to).
    • canSend(notification): boolean — synchronous probe.
    • send(notification): Promise<DeliveryResult> — main entry point; MUST NOT throw on transport failure.
  • DeliveryResult — { success, channel, messageId?, error?, timestamp }.

Implementation Notes

Both fields messageId and error are optional and mutually exclusive in practice — success: true should set messageId, success: false should set error. Tests cover both polarities. The interface deliberately uses string for channel (not NotificationChannel) so wrappers like DigestChannel (whose name is the literal "digest") and custom decorators can satisfy the contract without widening the package-wide channel enum.