Notifications
Built on
@warlock.js/core(mail) and@warlock.js/cascade(in-app store). Not standalone — it orchestrates over the transports the framework already gives you.
@warlock.js/notifications answers one question: something happened — how do I tell the right people, through the right channels, without rewriting the plumbing every time? You describe a notification once; it renders per channel and fans out to every recipient.
Define a notification, then fire it anywhere — sync or queued:
import { defineNotification } from "@warlock.js/notifications";
export const orderShipped = defineNotification<{ order: Order }>({ type: "order.shipped", via: ["database", "mail"], database: ({ order }) => ({ title: `Order #${order.number} shipped`, payload: { id: order.id } }), mail: ({ order }, to) => ({ subject: `Order #${order.number} shipped`, html: `<p>Hi ${to.get("name")}, it's on the way.</p>`, }),});
// anywhere — one recipient, a list, or queuedawait orderShipped.send(user, { order });await orderShipped.send([buyer, salesRep], { order });await orderShipped.queue(user, { order }, { delay: "10m" });Need a one-off? Skip the definition and use the per-channel proxy:
import { notify } from "@warlock.js/notifications";
await notify.mail(user, { subject: "Welcome", html: "<p>Thanks for joining</p>" });await notify.database(user, { type: "welcome", title: "Welcome!" });What it gives you
Section titled “What it gives you”- Define once, fire anywhere —
defineNotificationcarries the channels + a renderer per channel;.send/.queue/.onlydispatch it. - A typed channel registry —
notify.mail,notify.database, … are payload-typed; custom channels extend the registry with a 3-linedeclare module. - An in-app store with a clean read side —
inApp.listUnread/countUnread/markAsRead/dismiss, recipient-scoped by construction (no IDOR). - Pre-send gates — plug in a
PreferenceProvider(mute lists, quiet hours) and aRateLimiter(per-user budgets). Both optional. - Idempotency —
idempotencyKeymakes a retried send a no-op instead of a duplicate row. - Observability — subscribe to
sending/sent/failed/skipped(shareddispatchId+durationMs) for metrics, logs, tracing, and drop accounting.
Read by intent
Section titled “Read by intent”- New here? getting-started / introduction, then your first notification end to end.
- Mental model — channels, the registry, and how recipients are addressed: essentials / channels and the registry.
- The in-app store — model, repository, read API, IDOR-safety: essentials / the in-app store.
- Task guides — define a notification, ad-hoc sends, custom channels, preferences & rate limits, observability.
- Recipes — transactional, security & marketing, end to end.
- Reference — every public export in the API reference.
WhatsApp, Telegram, Slack, and push channels arrive with
@warlock.js/bridges. Until then, any HTTP channel is a 30-linedefineChannel— see custom channels.
Source: @warlock.js/notifications/.