@samveys/notifications

Mail and database channels, optional queueing, channel registry

Version
1.0.2Nov 5, 2026
Install
pnpm add @samveys/notifications
Import
import { createDatabaseInbox, createNotifications, defineNotification, inboxTableSql, notifications } from '@samveys/notifications'

Function (5)

createDatabaseInbox Function

The database inbox: `samveys_notifications(id, notifiable_id, type, data, read_at, created_at)`.

function createDatabaseInbox(options: InboxOptions): DatabaseInbox;

Exported from packages/notifications/src/inbox.ts

createNotifications Function

Builds the notification service with the `mail` and `database` channels registered.

function createNotifications(options: NotificationsOptions): Notifications & {

Exported from packages/notifications/src/manager.ts

defineNotification Function

Defines a notification: where it goes (`via`) and what each channel receives.

function defineNotification<U extends Notifiable = Notifiable>(input: NotificationInput<U>): Notification<U>;

Exported from packages/notifications/src/notification.ts

inboxTableSql Function

DDL of the inbox table on the database's dialect.

function inboxTableSql(db: Pick<Database, 'dialect'>, table?: string): readonly string[];

Exported from packages/notifications/src/inbox.ts

notifications Function

The `@samveys/notifications` plugin: binds `'notifications'` with `mail` and `database` channels.

function notifications(config?: NotificationsConfig): import("@samveys/core").Plugin<NotificationsConfig>;

Exported from packages/notifications/src/plugin.ts

Interface (11)

DatabaseInbox Interface

No documentation comment.

interface DatabaseInbox extends Inbox {
    /** Stores one entry for `user`. */
    add(user: Notifiable, type: string, data: Readonly<Record<string, unknown>>): Promise<DatabaseNotification>;
}

Exported from packages/notifications/src/inbox.ts

DatabaseNotification Interface

A stored inbox entry.

interface DatabaseNotification {
    readonly id: string;
    readonly notifiableId: string;
    readonly type: string;
    readonly data: Readonly<Record<string, unknown>>;
    readonly readAt: Temporal.Instant | null;
    readonly createdAt: Temporal.Instant;
}

Exported from packages/notifications/src/contracts.ts

Inbox Interface

No documentation comment.

interface Inbox {
    /** All entries of a user, newest first. */
    all(user: Notifiable): Promise<readonly DatabaseNotification[]>;
    unread(user: Notifiable): Promise<readonly DatabaseNotification[]>;
    /** Marks one entry read. Returns false when the id is unknown or already read. */
    markAsRead(id: string): Promise<boolean>;
    /** Marks all unread entries of a user read; returns how many. */
    markAllAsRead(user: Notifiable): Promise<number>;
}

Exported from packages/notifications/src/contracts.ts

InboxOptions Interface

No documentation comment.

interface InboxOptions {
    /** Resolves the database lazily. */
    readonly database: () => Database;

Exported from packages/notifications/src/inbox.ts

Notifiable Interface

Whoever receives notifications. Must be JSON-serialisable when a notification is queued.

interface Notifiable {
    readonly id: string | number;
    readonly email?: string;
}

Exported from packages/notifications/src/contracts.ts

Notification Interface

No documentation comment.

interface Notification<U extends Notifiable = Notifiable> extends NotificationInput<U> {
    readonly kind: 'notification';
    readonly queue: boolean;
    payloadFor(channel: string, user: U): unknown;
}

Exported from packages/notifications/src/contracts.ts

NotificationChannel Interface

One delivery route. Built-ins: `mail`, `database`. Add more with `extend`.

interface NotificationChannel {
    send(user: Notifiable, notification: Notification): Promise<void>;
}

Exported from packages/notifications/src/contracts.ts

NotificationInput Interface

No documentation comment.

interface NotificationInput<U extends Notifiable = Notifiable> {
    /** Stable name, stored with database rows and used to resolve queued notifications. */
    readonly type: string;
    /** Channel names this notification goes to for `user`: `'mail'`, `'database'`, or any registered channel. */
    via(user: U): readonly string[];
    toMail?(user: U): MailDraft;
    /** JSON-serialisable data stored in the user's inbox. */
    toDatabase?(user: U): Readonly<Record<string, unknown>>;
    /**
     * Payload builders for custom channels, by channel name (e.g. `broadcast`). Read by the channel
  …
Show the whole declaration (13 lines)
interface NotificationInput<U extends Notifiable = Notifiable> {
    /** Stable name, stored with database rows and used to resolve queued notifications. */
    readonly type: string;
    /** Channel names this notification goes to for `user`: `'mail'`, `'database'`, or any registered channel. */
    via(user: U): readonly string[];
    toMail?(user: U): MailDraft;
    /** JSON-serialisable data stored in the user's inbox. */
    toDatabase?(user: U): Readonly<Record<string, unknown>>;
    /**
     * Payload builders for custom channels, by channel name (e.g. `broadcast`). Read by the channel
     * through {@link Notification.payloadFor}.
     */
    readonly toChannel?: Readonly<Record<string, (user: U) => unknown>>;

Exported from packages/notifications/src/contracts.ts

Notifications Interface

No documentation comment.

interface Notifications extends Inbox {
    /** Sends `notification` to `user` on each channel of `via`, inline or queued (`queue: true`). */
    notify<U extends Notifiable>(user: U, notification: Notification<U>): Promise<void>;
    /** Registers a channel under `name` (the extension point other packages, e.g. broadcast, use). */
    extend(name: string, channel: NotificationChannel): void;
    /** Registers notifications so a queue worker can resolve them by `type`. */
    register(...notifications: readonly Notification<never>[]): void;
    readonly channels: readonly string[];
}

Exported from packages/notifications/src/contracts.ts

NotificationsConfig Interface

Configuration of `notifications()`.

interface NotificationsConfig {
    /** Notifications that queue workers must be able to resolve by `type`. */
    readonly notifications?: readonly Notification<never>[];
    /** Database connection name for the inbox (default connection when omitted). */
    readonly connection?: string;
    readonly table?: string;
}

Exported from packages/notifications/src/plugin.ts

NotificationsOptions Interface

No documentation comment.

interface NotificationsOptions {
    readonly inbox: DatabaseInbox;
    readonly mail: () => Mailer;

Exported from packages/notifications/src/manager.ts

Type (1)

MailDraft Type

A mail draft: the recipient defaults to the notifiable's `email`.

type MailDraft = Omit<MailMessage, 'to'> & {
    readonly to?: MailMessage['to'];
};

Exported from packages/notifications/src/contracts.ts