@warlock.js/notifications 5.34.1 → 5.34.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to `@warlock.js/notifications` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.34.3 - 2026-10-20
8
+
9
+ ### Changed
10
+
11
+ - Condensed the package agent skill description to at most 450 characters, with API names and package routing guidance.
12
+
13
+ ## 5.34.2 - 2026-10-19
14
+
15
+ ### Fixed
16
+
17
+ - Point the README at the package skill index, remove the outdated loose catalog, and include orientation in generated LLM projections.
18
+
7
19
  ## 5.32.0 - 2026-10-18
8
20
 
9
21
  ### Upgrading
package/README.md CHANGED
@@ -58,7 +58,7 @@ await inApp.markAsRead(user); // recipient-scoped — no IDOR
58
58
 
59
59
  ## Skills
60
60
 
61
- The package ships `skills/<task>/SKILL.md` files describing every public surface. Browse the index at [`skills/README.md`](./skills/README.md) or load them via `agent-kit`.
61
+ The package ships `skills/<task>/SKILL.md` files describing every public surface. Browse the index at [`skills/index.md`](./skills/index.md) or load them via `agent-kit`.
62
62
 
63
63
  ## License
64
64
 
package/llms-full.txt CHANGED
@@ -4,7 +4,35 @@
4
4
 
5
5
  > Generated artifact. Concatenates every SKILL.md and reference file under `@warlock.js/notifications/skills/`. Re-run `node scripts/generate-llms.mjs` after any change.
6
6
 
7
- ## configure-notifications `@warlock.js/notifications/configure-notifications/SKILL.md`
7
+ ## Package orientation
8
+
9
+ ---
10
+ description: 'Define once and notify through mail, in-app or custom channels: `defineNotification`, `notify`, `defineChannel`, `mailChannel`, `inApp`, `bullmqQueue` and `heraldQueue`. Use for "notify a user", "show unread notifications", "add a webhook channel" or "queue notification delivery". Not this package → @warlock.js/core for plain email and templates; @warlock.js/queue for jobs; @warlock.js/herald for broker messaging.'
11
+ ---
12
+
13
+ # @warlock.js/notifications
14
+
15
+ An orchestrator over transports that already exist: one event, many recipients, any mix of channels, one render per channel. It is not a queue, template engine, or transport. `defineNotification` builds a reusable notification (`send`, `queue`, `only`); `notify.<channel>` does ad-hoc single-channel sends; `inApp` is the read side of the database channel. Server-only.
16
+
17
+ ## The 80% path
18
+
19
+ 1. Orient with `notifications-basics.md`.
20
+ 2. Configure channels in `src/config/notifications.ts`: `configure-notifications.md`; create the table: `write-notification-migration.md`.
21
+ 3. Define the notification with `via` and a renderer per channel: `define-notification.md`; one-off sends: `send-ad-hoc.md`.
22
+ 4. Serve the inbox (list, mark read): `use-in-app.md`.
23
+ 5. Custom channel: `define-channel.md`; deferred delivery: `queue-notifications.md`; sent/failed/skipped hooks: `observe-notifications.md`.
24
+
25
+ ## Conventions and pitfalls
26
+
27
+ - The config is declarative; the connector registers its default export, so never call `setNotificationConfig` yourself.
28
+ - Channel names are typed through `NotificationChannels` declaration merging.
29
+ - `mailChannel` wraps core `sendMail`; use core directly for non-notification mail.
30
+ - Every send accepts `force` and `idempotencyKey`; preferences and rate limits are pluggable gates.
31
+ - `.queue()` needs a backend: `bullmqQueue` (with `@warlock.js/queue`) or `heraldQueue`.
32
+ - Do not value-import from client code.
33
+
34
+
35
+ ## configure-notifications `@warlock.js/notifications/skills/configure-notifications/SKILL.md`
8
36
 
9
37
  ---
10
38
  name: configure-notifications
@@ -146,7 +174,7 @@ afterEach(() => resetNotificationConfig());
146
174
  - The `define-channel` topic — custom channels.
147
175
 
148
176
 
149
- ## define-channel `@warlock.js/notifications/define-channel/SKILL.md`
177
+ ## define-channel `@warlock.js/notifications/skills/define-channel/SKILL.md`
150
178
 
151
179
  ---
152
180
  name: define-channel
@@ -259,7 +287,7 @@ export const slackChannel = (config: { token: string }) =>
259
287
  - The `develop-warlock.js-feature` workspace skill — lazy-import pattern for SDK-backed channels.
260
288
 
261
289
 
262
- ## define-notification `@warlock.js/notifications/define-notification/SKILL.md`
290
+ ## define-notification `@warlock.js/notifications/skills/define-notification/SKILL.md`
263
291
 
264
292
  ---
265
293
  name: define-notification
@@ -393,7 +421,7 @@ Per-recipient: each recipient gets an independent `Promise.allSettled`. Per-chan
393
421
  - The `observe-notifications` topic — `notifications.on("sending" | "sent" | "failed" | "skipped", …)`.
394
422
 
395
423
 
396
- ## notifications-basics `@warlock.js/notifications/notifications-basics/SKILL.md`
424
+ ## notifications-basics `@warlock.js/notifications/skills/notifications-basics/SKILL.md`
397
425
 
398
426
  ---
399
427
  name: notifications-basics
@@ -495,7 +523,7 @@ await inApp.markAsRead(user, "ntf_123");
495
523
  - The `observe-notifications` topic — events + metrics.
496
524
 
497
525
 
498
- ## observe-notifications `@warlock.js/notifications/observe-notifications/SKILL.md`
526
+ ## observe-notifications `@warlock.js/notifications/skills/observe-notifications/SKILL.md`
499
527
 
500
528
  ---
501
529
  name: observe-notifications
@@ -569,7 +597,7 @@ A handler that throws is logged and swallowed — the other handlers for the sam
569
597
  - The `send-ad-hoc` topic — when `notifiable` is undefined in events.
570
598
 
571
599
 
572
- ## queue-notifications `@warlock.js/notifications/queue-notifications/SKILL.md`
600
+ ## queue-notifications `@warlock.js/notifications/skills/queue-notifications/SKILL.md`
573
601
 
574
602
  ---
575
603
  name: queue-notifications
@@ -683,7 +711,7 @@ await orderShipped.queue(user, { order }, { idempotencyKey: `ship:${order.id}` }
683
711
  - The `herald-basics` topic of the `warlock-js-herald` skill - connecting the broker.
684
712
 
685
713
 
686
- ## send-ad-hoc `@warlock.js/notifications/send-ad-hoc/SKILL.md`
714
+ ## send-ad-hoc `@warlock.js/notifications/skills/send-ad-hoc/SKILL.md`
687
715
 
688
716
  ---
689
717
  name: send-ad-hoc
@@ -782,7 +810,7 @@ await notify.slack(staff, { text: "Deploy starting" });
782
810
  - The `observe-notifications` topic — `sending` / `sent` / `failed` / `skipped` events.
783
811
 
784
812
 
785
- ## use-in-app `@warlock.js/notifications/use-in-app/SKILL.md`
813
+ ## use-in-app `@warlock.js/notifications/skills/use-in-app/SKILL.md`
786
814
 
787
815
  ---
788
816
  name: use-in-app
@@ -960,7 +988,7 @@ database: inApp.configure({ repository: notificationsRepository }),
960
988
  - The `define-notification` topic — create rows via the database channel.
961
989
 
962
990
 
963
- ## write-notification-migration `@warlock.js/notifications/write-notification-migration/SKILL.md`
991
+ ## write-notification-migration `@warlock.js/notifications/skills/write-notification-migration/SKILL.md`
964
992
 
965
993
  ---
966
994
  name: write-notification-migration
package/llms.txt CHANGED
@@ -6,12 +6,13 @@
6
6
 
7
7
  ## Skills
8
8
 
9
- - [configure-notifications](@warlock.js/notifications/configure-notifications/SKILL.md): Wire `@warlock.js/notifications` via a declarative `src/config/notifications.ts` that exports a `NotificationConfig` default — the notifications connector registers it at boot, so app code never calls `setNotificationConfig`. `channels`: registry mapping name → factory result (`mailChannel`, `inApp.configure`, custom `defineChannel`); `queue`: optional `QueueDispatcher` (`.queue()` throws without it); `preferences`: optional `PreferenceProvider` (drops channels the recipient muted); `rateLimit`: optional `RateLimiter` (drops channels exceeding per-recipient budgets). Triggers: `config/notifications.ts`, `NotificationConfig`, `setNotificationConfig`, `getNotificationConfig`, `mailChannel`, `inApp.configure`, "configure notifications", "wire notifications at boot", "set preferences provider", "set rate limit"; typical import `import { type NotificationConfig, mailChannel, inApp } from "@warlock.js/notifications"`. Skip: building a reusable notification — the `define-notification` topic; the in-app read API — the `use-in-app` topic; adding a custom channel — the `define-channel` topic.
10
- - [define-channel](@warlock.js/notifications/define-channel/SKILL.md): Add a custom notification channel — `defineChannel<P>({ name, route?, send })` returns a `Channel<P>` you register in the `channels` map of `setNotificationConfig`. `name` matches the key in `NotificationChannels` (extend via `declare module` for typing); `route(notifiable)` resolves the recipient's address (defaults to `{ id }` when omitted); `send({ payload, route, notifiable, options })` does the actual transport. Use for Slack/Discord/internal-webhook/anything else — `fetch`-based with NO SDK is the simplest variant. Channels that load a heavy SDK should follow the lazy-import pattern from the develop-feature playbook. Triggers: `defineChannel`, `Channel<P>`, "custom notification channel", "add discord channel", "slack channel", "webhook channel", "new channel type", `declare module "@warlock.js/notifications"`; typical import `import { defineChannel } from "@warlock.js/notifications"`. Skip: lazy-loading an optional SDK behind the channel — the `develop-warlock.js-feature` workspace skill; using built-in channels — the `configure-notifications` topic.
11
- - [define-notification](@warlock.js/notifications/define-notification/SKILL.md): Build a reusable multi-channel notification with `defineNotification<Data>({ type, via, ...renderers })`. `type` is the stable identifier preference/rate-limit gates use AND defaults the database channel's `type`. `via` is `ChannelName[]` OR `(data, to) => ChannelName[]` for per-recipient channel selection. Renderers are `(data, to, ctx) => NotificationChannels[C]`; `ctx` carries `locale` + `meta` from `SendOptions`. The returned object exposes `.send(to|to[], data, options?)`, `.queue(to|to[], data, options?)`, and `.only(...channels)`. Per-recipient + per-channel `Promise.allSettled` isolates failures; the database channel's `type` is auto-defaulted from `def.type`; `SendOptions.idempotencyKey` is injected into the database payload. Triggers: `defineNotification`, `via`, "reusable notification", "multi-channel notification", "send through several channels at once", "render per channel", `RenderContext`, `def.type`; "force on a notification", "queue with delay"; typical import `import { defineNotification } from "@warlock.js/notifications"`. Skip: single-channel ad-hoc sends — the `send-ad-hoc` topic; adding a brand-new channel — the `define-channel` topic; observing dispatch outcomes — the `observe-notifications` topic.
12
- - [notifications-basics](@warlock.js/notifications/notifications-basics/SKILL.md): Start here for @warlock.js/notifications channels, notification definitions, delivery, and application setup.
13
- - [observe-notifications](@warlock.js/notifications/observe-notifications/SKILL.md): Wire metrics, logging, tracing, and audits via `notifications.on(event, handler)` — four events fire per (channel, recipient): `sending` (gates passed, about to hit the transport), `sent` (succeeded), `failed` (channel threw — carries the Error), `skipped` (a pre-send gate dropped it — carries `reason: "preference" | "rate-limit"`). Every event carries the recipient as `notifiable?` (undefined only for raw-target ad-hoc sends) and a `dispatchId` — `sending` and its terminal `sent`/`failed` share it, so observers can pair them (spans, latency, hung-send detection). `sent`/`failed` also carry `durationMs`. Handler exceptions are logged-and-swallowed so one bad listener can not break the dispatcher. `on(...)` returns an unsubscribe function; `off(event, handler)` exists for symmetry. Triggers: `notifications.on`, `notifications.off`, `"sending"` event, `"sent"` event, `"failed"` event, `"skipped"` event, `dispatchId`, `durationMs`, `NotificationEvents`, "notification metrics", "notification latency", "trace notifications", "notification audit log", "track notification drops"; typical import `import { notifications } from "@warlock.js/notifications"`. Skip: building notifications — the `define-notification` topic; configuring the preference/rate-limit gates that produce `skipped` — the `configure-notifications` topic.
14
- - [queue-notifications](@warlock.js/notifications/queue-notifications/SKILL.md): Send notifications asynchronously through the herald backend or the BullMQ driver. Configure a QueueDispatcher in config/notifications.ts, then use notif.queue(to, data, options?) to enqueue rendered jobs. Herald uses heraldQueue() and startNotificationsWorker(); bullmqQueue({ attempts, backoff }) from @warlock.js/notifications uses BullMQ (lazy-loading the optional @warlock.js/queue peer), supports retries, and honours SendOptions.delay. Without a queue configured, .queue() rejects NoQueueDispatcherError. Triggers: heraldQueue, bullmqQueue, startNotificationsWorker, QueueDispatcher, .queue(, async notifications, queue notifications, notification worker, background notifications, notifications.dispatch. Skip: synchronous sends - the `define-notification` topic; herald itself - @warlock.js/herald/*.
15
- - [send-ad-hoc](@warlock.js/notifications/send-ad-hoc/SKILL.md): Ad-hoc per-channel send via the `notify.<channel>(to, payload, options?)` Proxy facade. Works for any channel registered in `NotificationChannels` (built-in or custom). `to` accepts a `Notifiable` model OR a raw route string (`"x@y.com"`); `payload` is typed per channel via the registry; `options` is the standard `SendOptions`. `notify.channel(name).send(...)` is the runtime escape when the channel name is not a compile-time literal. Type-based preference gating applies when a type is known — explicit via `options.type` OR auto-detected from a database payload's `type` field. For multi-channel sends use `defineNotification` instead — there is no inline `notify(to, { via, ... })` form on purpose. Triggers: `notify.<channel>`, `notify.channel(name)`, `notify.mail`, `notify.database`, "send a one-off notification", "ad-hoc notification", "send an email without defining a notification", "raw email target", "dynamic channel name"; typical import `import { notify } from "@warlock.js/notifications"`. Skip: reusable multi-channel notifications — the `define-notification` topic; configuring channels — the `configure-notifications` topic.
16
- - [use-in-app](@warlock.js/notifications/use-in-app/SKILL.md): Use the `inApp` facade for the in-app/database channel's READ side: `inApp.configure({ model: Notification })` or `{ repository: myRepo }` binds the store AND returns the channel; `inApp.list / listUnread / countUnread / markAsRead / markAsUnread` are RECIPIENT-SCOPED by construction (no IDOR — `markAsRead(user, id)` cannot flip another user's row even with a foreign id). `countUnread` uses `RepositoryManager.countCached` for the badge fast-path. Triggers: `inApp.configure`, `inApp.list`, `inApp.listUnread`, `inApp.countUnread`, `inApp.markAsRead`, `inApp.markAsUnread`, `BaseNotificationsRepository`, `DatabaseNotification`, `NotificationsFilter`, "in-app notifications", "mark notification as read", "unread count", "notification badge", "IDOR-safe markAsRead"; typical import `import { inApp } from "@warlock.js/notifications"`. Skip: writing notifications (use `defineNotification` or `notify.database`) — the `define-notification` topic; the migration columns — the `write-notification-migration` topic; wiring it into config — the `configure-notifications` topic.
17
- - [write-notification-migration](@warlock.js/notifications/write-notification-migration/SKILL.md): Create the notifications table with the `notificationColumns(Notification)` factory + cascade `Migration.create`. Column NAMES come from the model's `columnMap` (recipient / tenant / readAt / isRead); the fixed columns are `type` / `title` / `body` / `payload` / `idempotency_key`. Set `{ idType: "bigint" }` for integer-key apps; UUID remains the default. Read-state follows columnMap presence: `readAt` → nullable timestamp, `isRead` → indexed boolean, both → both. Defaults to `user_id` + `read_at` when no columnMap. Spread + extend for extras (FK references, composite indexes). Triggers: `notificationColumns`, `Migration.create` for notifications, "create the notifications table", "notification migration", "add organization_id to notifications", `is_read` column, `read_at` column, `idempotency_key` column; typical import `import { Migration } from "@warlock.js/cascade"; import { notificationColumns } from "@warlock.js/notifications"`. Skip: generic cascade migration writing — the `write-migration` topic of the `warlock-js-cascade` skill; the model itself — the `use-in-app` topic.
9
+ - [Package orientation](@warlock.js/notifications/skills/index.md): Define once and notify through mail, in-app or custom channels: `defineNotification`, `notify`, `defineChannel`, `mailChannel`, `inApp`, `bullmqQueue` and `heraldQueue`. Use for "notify a user", "show unread notifications", "add a webhook channel" or "queue notification delivery". Not this package → @warlock.js/core for plain email and templates; @warlock.js/queue for jobs; @warlock.js/herald for broker messaging.
10
+ - [configure-notifications](@warlock.js/notifications/skills/configure-notifications/SKILL.md): Wire `@warlock.js/notifications` via a declarative `src/config/notifications.ts` that exports a `NotificationConfig` default — the notifications connector registers it at boot, so app code never calls `setNotificationConfig`. `channels`: registry mapping name → factory result (`mailChannel`, `inApp.configure`, custom `defineChannel`); `queue`: optional `QueueDispatcher` (`.queue()` throws without it); `preferences`: optional `PreferenceProvider` (drops channels the recipient muted); `rateLimit`: optional `RateLimiter` (drops channels exceeding per-recipient budgets). Triggers: `config/notifications.ts`, `NotificationConfig`, `setNotificationConfig`, `getNotificationConfig`, `mailChannel`, `inApp.configure`, "configure notifications", "wire notifications at boot", "set preferences provider", "set rate limit"; typical import `import { type NotificationConfig, mailChannel, inApp } from "@warlock.js/notifications"`. Skip: building a reusable notification — the `define-notification` topic; the in-app read API — the `use-in-app` topic; adding a custom channel — the `define-channel` topic.
11
+ - [define-channel](@warlock.js/notifications/skills/define-channel/SKILL.md): Add a custom notification channel — `defineChannel<P>({ name, route?, send })` returns a `Channel<P>` you register in the `channels` map of `setNotificationConfig`. `name` matches the key in `NotificationChannels` (extend via `declare module` for typing); `route(notifiable)` resolves the recipient's address (defaults to `{ id }` when omitted); `send({ payload, route, notifiable, options })` does the actual transport. Use for Slack/Discord/internal-webhook/anything else — `fetch`-based with NO SDK is the simplest variant. Channels that load a heavy SDK should follow the lazy-import pattern from the develop-feature playbook. Triggers: `defineChannel`, `Channel<P>`, "custom notification channel", "add discord channel", "slack channel", "webhook channel", "new channel type", `declare module "@warlock.js/notifications"`; typical import `import { defineChannel } from "@warlock.js/notifications"`. Skip: lazy-loading an optional SDK behind the channel — the `develop-warlock.js-feature` workspace skill; using built-in channels — the `configure-notifications` topic.
12
+ - [define-notification](@warlock.js/notifications/skills/define-notification/SKILL.md): Build a reusable multi-channel notification with `defineNotification<Data>({ type, via, ...renderers })`. `type` is the stable identifier preference/rate-limit gates use AND defaults the database channel's `type`. `via` is `ChannelName[]` OR `(data, to) => ChannelName[]` for per-recipient channel selection. Renderers are `(data, to, ctx) => NotificationChannels[C]`; `ctx` carries `locale` + `meta` from `SendOptions`. The returned object exposes `.send(to|to[], data, options?)`, `.queue(to|to[], data, options?)`, and `.only(...channels)`. Per-recipient + per-channel `Promise.allSettled` isolates failures; the database channel's `type` is auto-defaulted from `def.type`; `SendOptions.idempotencyKey` is injected into the database payload. Triggers: `defineNotification`, `via`, "reusable notification", "multi-channel notification", "send through several channels at once", "render per channel", `RenderContext`, `def.type`; "force on a notification", "queue with delay"; typical import `import { defineNotification } from "@warlock.js/notifications"`. Skip: single-channel ad-hoc sends — the `send-ad-hoc` topic; adding a brand-new channel — the `define-channel` topic; observing dispatch outcomes — the `observe-notifications` topic.
13
+ - [notifications-basics](@warlock.js/notifications/skills/notifications-basics/SKILL.md): Start here for @warlock.js/notifications channels, notification definitions, delivery, and application setup.
14
+ - [observe-notifications](@warlock.js/notifications/skills/observe-notifications/SKILL.md): Wire metrics, logging, tracing, and audits via `notifications.on(event, handler)` — four events fire per (channel, recipient): `sending` (gates passed, about to hit the transport), `sent` (succeeded), `failed` (channel threw — carries the Error), `skipped` (a pre-send gate dropped it — carries `reason: "preference" | "rate-limit"`). Every event carries the recipient as `notifiable?` (undefined only for raw-target ad-hoc sends) and a `dispatchId` — `sending` and its terminal `sent`/`failed` share it, so observers can pair them (spans, latency, hung-send detection). `sent`/`failed` also carry `durationMs`. Handler exceptions are logged-and-swallowed so one bad listener can not break the dispatcher. `on(...)` returns an unsubscribe function; `off(event, handler)` exists for symmetry. Triggers: `notifications.on`, `notifications.off`, `"sending"` event, `"sent"` event, `"failed"` event, `"skipped"` event, `dispatchId`, `durationMs`, `NotificationEvents`, "notification metrics", "notification latency", "trace notifications", "notification audit log", "track notification drops"; typical import `import { notifications } from "@warlock.js/notifications"`. Skip: building notifications — the `define-notification` topic; configuring the preference/rate-limit gates that produce `skipped` — the `configure-notifications` topic.
15
+ - [queue-notifications](@warlock.js/notifications/skills/queue-notifications/SKILL.md): Send notifications asynchronously through the herald backend or the BullMQ driver. Configure a QueueDispatcher in config/notifications.ts, then use notif.queue(to, data, options?) to enqueue rendered jobs. Herald uses heraldQueue() and startNotificationsWorker(); bullmqQueue({ attempts, backoff }) from @warlock.js/notifications uses BullMQ (lazy-loading the optional @warlock.js/queue peer), supports retries, and honours SendOptions.delay. Without a queue configured, .queue() rejects NoQueueDispatcherError. Triggers: heraldQueue, bullmqQueue, startNotificationsWorker, QueueDispatcher, .queue(, async notifications, queue notifications, notification worker, background notifications, notifications.dispatch. Skip: synchronous sends - the `define-notification` topic; herald itself - @warlock.js/herald/*.
16
+ - [send-ad-hoc](@warlock.js/notifications/skills/send-ad-hoc/SKILL.md): Ad-hoc per-channel send via the `notify.<channel>(to, payload, options?)` Proxy facade. Works for any channel registered in `NotificationChannels` (built-in or custom). `to` accepts a `Notifiable` model OR a raw route string (`"x@y.com"`); `payload` is typed per channel via the registry; `options` is the standard `SendOptions`. `notify.channel(name).send(...)` is the runtime escape when the channel name is not a compile-time literal. Type-based preference gating applies when a type is known — explicit via `options.type` OR auto-detected from a database payload's `type` field. For multi-channel sends use `defineNotification` instead — there is no inline `notify(to, { via, ... })` form on purpose. Triggers: `notify.<channel>`, `notify.channel(name)`, `notify.mail`, `notify.database`, "send a one-off notification", "ad-hoc notification", "send an email without defining a notification", "raw email target", "dynamic channel name"; typical import `import { notify } from "@warlock.js/notifications"`. Skip: reusable multi-channel notifications — the `define-notification` topic; configuring channels — the `configure-notifications` topic.
17
+ - [use-in-app](@warlock.js/notifications/skills/use-in-app/SKILL.md): Use the `inApp` facade for the in-app/database channel's READ side: `inApp.configure({ model: Notification })` or `{ repository: myRepo }` binds the store AND returns the channel; `inApp.list / listUnread / countUnread / markAsRead / markAsUnread` are RECIPIENT-SCOPED by construction (no IDOR — `markAsRead(user, id)` cannot flip another user's row even with a foreign id). `countUnread` uses `RepositoryManager.countCached` for the badge fast-path. Triggers: `inApp.configure`, `inApp.list`, `inApp.listUnread`, `inApp.countUnread`, `inApp.markAsRead`, `inApp.markAsUnread`, `BaseNotificationsRepository`, `DatabaseNotification`, `NotificationsFilter`, "in-app notifications", "mark notification as read", "unread count", "notification badge", "IDOR-safe markAsRead"; typical import `import { inApp } from "@warlock.js/notifications"`. Skip: writing notifications (use `defineNotification` or `notify.database`) — the `define-notification` topic; the migration columns — the `write-notification-migration` topic; wiring it into config — the `configure-notifications` topic.
18
+ - [write-notification-migration](@warlock.js/notifications/skills/write-notification-migration/SKILL.md): Create the notifications table with the `notificationColumns(Notification)` factory + cascade `Migration.create`. Column NAMES come from the model's `columnMap` (recipient / tenant / readAt / isRead); the fixed columns are `type` / `title` / `body` / `payload` / `idempotency_key`. Set `{ idType: "bigint" }` for integer-key apps; UUID remains the default. Read-state follows columnMap presence: `readAt` → nullable timestamp, `isRead` → indexed boolean, both → both. Defaults to `user_id` + `read_at` when no columnMap. Spread + extend for extras (FK references, composite indexes). Triggers: `notificationColumns`, `Migration.create` for notifications, "create the notifications table", "notification migration", "add organization_id to notifications", `is_read` column, `read_at` column, `idempotency_key` column; typical import `import { Migration } from "@warlock.js/cascade"; import { notificationColumns } from "@warlock.js/notifications"`. Skip: generic cascade migration writing — the `write-migration` topic of the `warlock-js-cascade` skill; the model itself — the `use-in-app` topic.
package/package.json CHANGED
@@ -5,14 +5,14 @@
5
5
  "environment": "server"
6
6
  },
7
7
  "dependencies": {
8
- "@warlock.js/cache": "5.34.1",
9
- "@warlock.js/cascade": "5.34.1",
10
- "@warlock.js/core": "5.34.1",
11
- "@warlock.js/logger": "5.34.1"
8
+ "@warlock.js/cache": "5.34.3",
9
+ "@warlock.js/cascade": "5.34.3",
10
+ "@warlock.js/core": "5.34.3",
11
+ "@warlock.js/logger": "5.34.3"
12
12
  },
13
13
  "peerDependencies": {
14
- "@warlock.js/herald": "5.34.1",
15
- "@warlock.js/queue": "5.34.1"
14
+ "@warlock.js/herald": "5.34.3",
15
+ "@warlock.js/queue": "5.34.3"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "@warlock.js/herald": {
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "author": "hassanzohdy",
39
39
  "license": "MIT",
40
- "version": "5.34.1",
40
+ "version": "5.34.3",
41
41
  "main": "./cjs/index.cjs",
42
42
  "module": "./esm/index.mjs",
43
43
  "types": "./esm/index.d.mts",
package/skills/index.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: 'Multi-channel notifications for Warlock: define once, send by mail, in-app database, or custom channels. Exports `defineNotification`, `notify`, `defineChannel`, `mailChannel`, `inApp`, `notificationColumns`, `notifications`, `bullmqQueue`, `heraldQueue`, `NotificationConfig`. Use for: "notify a user", "send order shipped by email and in-app", "unread notifications", "add a Slack or webhook channel", "queue notification delivery". Not this package: sending a plain email or mail templates → @warlock.js/core sendMail; background jobs → @warlock.js/queue.'
2
+ description: 'Define once and notify through mail, in-app or custom channels: `defineNotification`, `notify`, `defineChannel`, `mailChannel`, `inApp`, `bullmqQueue` and `heraldQueue`. Use for "notify a user", "show unread notifications", "add a webhook channel" or "queue notification delivery". Not this package → @warlock.js/core for plain email and templates; @warlock.js/queue for jobs; @warlock.js/herald for broker messaging.'
3
3
  ---
4
4
 
5
5
  # @warlock.js/notifications
package/skills/README.md DELETED
@@ -1,23 +0,0 @@
1
- # @warlock.js/notifications — skills
2
-
3
- All cross-references use the form `@warlock.js/notifications/<skill>/SKILL.md`.
4
-
5
- Skills are added as the package's public surface lands. Phase 0 (core dispatch) ships:
6
-
7
- - (incoming) `notifications-basics/` — what the package is, when to reach for it, the mental model.
8
- - (incoming) `define-notification/` — reusable multi-channel definitions; `via`, renderers, `(data, to, ctx)`.
9
- - (incoming) `send-ad-hoc/` — `notify.<channel>` proxy + `notify.channel(name)` runtime form.
10
- - (incoming) `configure-notifications/` — `config/notifications.ts`, `NotificationConfig`, `SendOptions`.
11
- - (incoming) `define-channel/` — custom channels via `defineChannel` + `declare module`.
12
- - (incoming) `use-in-app/` — `inApp.configure({model})`, `listUnread` / `markAsRead` / `markAsUnread`.
13
- - (incoming) `write-notification-migration/` — `notificationColumns(Notification)`, named from the model's `columnMap`.
14
- - (incoming) `observe-notifications/` — `notifications.on("sending" | "sent" | "failed" | "skipped", …)`.
15
- - `queue-notifications/` — async `.queue()` via the herald-backed dispatcher + worker.
16
- - (deferred — Phase 2, bridges) `gate-with-preferences/` — already covered in `configure-notifications`; promote if it grows.
17
- - (deferred — Phase 2, bridges) `send-via-whatsapp/`, `send-via-telegram/`, `send-via-push/`, `send-via-slack/`.
18
-
19
- Regenerate the LLM index after any change:
20
-
21
- ```bash
22
- node scripts/generate-llms.mjs
23
- ```