first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
# `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
```ts
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
```ts
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.