-
Notifications
You must be signed in to change notification settings - Fork 0
Notify
One declaration, many channels: fan-out, a preference gate, a digest window, a delivery ledger
and an in-app inbox. Package @ultimat3/notify (tier 4) — the full reference is
packages/notify/README.md.
notifier() is a job factory: it returns a JobHandle, so a notification inherits retry, the
dead-letter path, cancellation, x jobs show and its manifest row — the same shape backfill()
and llm() take (The eight primitives). It is not a ninth primitive.
import { inAppChannel, mailChannel, notifier, t } from '@ultimat3/notify';
export const commentPosted = notifier({
name: 'post.commented',
input: t.object({ postId: t.uuid, orgId: t.uuid, author: t.string }),
tenant: (params) => params.orgId, // required, as on job()
key: (params) => `comment:${params.postId}`, // the queue's dedupe key AND the ledger's event key
recipients: ({ input, ctx }) => posts.subscribers(input.postId, ctx.signal),
deliver: [
{ channel: inAppChannel() }, // immediately
{
channel: mailChannel({ mailer }),
wait: '10m', // then re-read the condition: a mute in minute 3 wins
unless: ({ event }) => event.params.author === 'system',
digest: { window: '1h', group: (event) => event.params.postId },
},
],
});
await commentPosted.enqueue({ params: { postId, orgId, author } });setNotifyStores({ ledger, inbox, digest, preferences }) once at boot, whole-object replacement:
| Store | Default | Postgres |
|---|---|---|
ledger — the delivery claim |
in-memory (one process is genuinely deduped) |
createPgDeliveryLedger({ executor, windowMs }) — windowMs never shorter than your idempotency window |
preferences — the gate |
allow all | yours: the gate ships, what it reads never does — your taxonomy, your quiet hours |
inbox |
none — X_NOTIFY_STORE_MISSING
|
createPgInboxStore({ executor }) |
digest |
none — X_NOTIFY_STORE_MISSING
|
createMemoryDigestStore() |
The hourly x.purge job sweeps the Postgres ledger and inbox. The inbox is swept only when your
app.config.ts sets notify.inboxReadRetentionMs / notify.inboxUnreadRetentionMs — when an unread
message disappears is your decision (Configuration).
-
channel(name, fn)delivers per recipient;bulkChannel(name, fn)makes one call for the whole audience (a Slack post, a webhook) and cannot take a digest (X_NOTIFY_DIGEST_UNSUPPORTED).inAppChannel()andmailChannel({ mailer })ship;maileris structural, so Mail plugs in without an import. -
requireInbox(name)answerslist,unreadCount,markSeen,markRead.seenAtandreadAtare two facts, and the unread count is derived, never stored. - A replay does not send twice: the step checkpoint per channel and recipient, and the ledger's
atomic claim on
(notifier, key, channel, recipient)taken before the send. - Entries fire in
waitorder;if/unlessrun after the wait, on the attempt that delivers.
Codes: X_NOTIFY_CHANNELS_EMPTY, X_NOTIFY_CHANNEL_DUPLICATE, X_NOTIFY_DIGEST_UNSUPPORTED,
X_NOTIFY_FANOUT_TOO_WIDE (default cap 500 recipients), X_NOTIFY_STORE_MISSING,
X_NOTIFY_DELIVERY_FAILED — Error codes.
Ultimate — v22.1.0 As of 2026-09. Stable API, semver from here. MIT licensed. What npm serves is npm view @ultimat3/core version, never this line.
This footer is the only page that stamps a version. It renders under every wiki page, so one release bumps one line; a stamp on a second page is 46 hand-copies of one fact, and every one of them goes stale on the next tag.
Repository · Issues · Changelog · llms.txt
Edits to these pages are synced from wiki/ in the repository — change the file there, not the wiki, or the next sync overwrites it.
Start
Tutorials
- 1 · First app
- 2 · First feature
- 3 · Auth and admin
- 4 · Jobs and realtime
- 5 · Deploy free
- 6 · Growing up
Primitives
- The eight primitives
- Building your own base
- Actions
- Entities and migrations
- Policies and authz
- Queries and live queries
- Client data
- Jobs and workflows
- Scheduled tasks
- Routes and render modes
Capabilities
- Realtime
- Caching and invalidation
- Batching and preloading
- N+1 detection
- PWA and offline
- MCP and AI
- Agents
- Admin dashboard
- Scraping
- Auth
- Notify
- Storage and uploads
- Feature flags
- SEO
Cross-cutting
- I18n
- Theming
- UI components
- Interface rules
- Timezones and dates
- Money
- Resource management
- Migrations and backfills
- Testing
Reference