@12-apps/notifications 3.0.0 → 4.0.0
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/ADOPTING.md
CHANGED
|
@@ -20,7 +20,7 @@ sweep — endpoints and screens included.
|
|
|
20
20
|
| **Core** | `@12-apps/notifications` | Nothing to wire — the framework-free, storage-free vocabulary both halves share: types, the generator registry, the preference policy, the phone rules, the copy table, the inbox wire shape. Safe in a browser. |
|
|
21
21
|
| **Server** | `@12-apps/notifications/server` | Call `createApiNotifications({ db, contacts, transports })` and mount the `routes` it returns — the nine inbox / preferences / push-subscription endpoints, with parsing, statuses and the `{ data }` envelope inside. It also returns `notify` (the emit front door), `notifyByPermission`, `dispatchDeliveries`, `drainPending` and the three stores. |
|
|
22
22
|
| **Hono** | `@12-apps/notifications/hono` | `const notifications = notificationsRouter({ ...serverConfig, resolveActor }); app.route('/api/account', notifications.router)`. A one-call mount; `hono` is an OPTIONAL peer, so importing the root, `/server` or `/react` never resolves it. |
|
|
23
|
-
| **React** | `@12-apps/notifications/react` | Call `createWebNotifications({ apiBase })`. `BellWithPanel` is the whole feature as one element; `BellButton` + `Panel` are the pair for a host with its own chrome; `page` is the preferences screen you route to. pt-BR product copy and the
|
|
23
|
+
| **React** | `@12-apps/notifications/react` | Call `createWebNotifications({ apiBase })`. `BellWithPanel` is the whole feature as one element; `BellButton` + `Panel` are the pair for a host with its own chrome; `page` is the preferences screen you route to. pt-BR product copy and the origin host's test ids ship inside. |
|
|
24
24
|
| **Web Push** | `@12-apps/notifications/web-push` | `sender: vapidPushSender({ subject, publicKey, privateKey })` on the `WEB_PUSH` declaration. Its own subpath because it is the only piece that needs `web-push` — an OPTIONAL peer a host that never enables the channel never installs. |
|
|
25
25
|
| **Prisma** | `prisma/notifications.prisma` + `prisma/migrations/*` | Run `pnpm --filter @12-apps/notifications prisma:sync -- <host schema dir>`: the partial is **COPIED** into the host's multi-file schema folder — never symlinked (a symlinked migration is silently skipped by Prisma; a symlinked partial dangles under `turbo prune`). Migrations are discovered structurally from the installed package's `prisma/migrations` by the host's plugin-migration sync. |
|
|
26
26
|
|
|
@@ -35,7 +35,7 @@ sweep — endpoints and screens included.
|
|
|
35
35
|
someone else's inbox would be a different surface with a different actor.
|
|
36
36
|
|
|
37
37
|
2. **`contacts` is not optional, and it is not the `users` table.** A transport
|
|
38
|
-
needs an address: `getContact(userId) → { email, phone } | null`.
|
|
38
|
+
needs an address: `getContact(userId) → { email, phone } | null`. The origin host's
|
|
39
39
|
router read `users.email` / `users.phone` directly, which is the one thing in
|
|
40
40
|
the pipeline that was always the host's — a package cannot know the shape of
|
|
41
41
|
an identity table, and a host with phone VERIFICATION wants to answer the
|
|
@@ -105,8 +105,8 @@ sweep — endpoints and screens included.
|
|
|
105
105
|
it with hand-written SQL. The argument shapes are CLOSED (documented in
|
|
106
106
|
`src/server/db.ts`), so a non-Prisma host has a finite surface to fill.
|
|
107
107
|
|
|
108
|
-
5. **`categories` is product vocabulary.**
|
|
109
|
-
|
|
108
|
+
5. **`categories` is product vocabulary, and REQUIRED.** There is no default:
|
|
109
|
+
the host declares its own taxonomy, and the preferences screen
|
|
110
110
|
renders whatever the api half was given — the taxonomy travels on the
|
|
111
111
|
`GET /notification-preferences` payload, so the two halves cannot disagree
|
|
112
112
|
about it. The packaged migration deliberately puts **no CHECK** on
|
|
@@ -115,7 +115,7 @@ sweep — endpoints and screens included.
|
|
|
115
115
|
6. **`generators` are registered from the OUTSIDE, and stay the host's.** A
|
|
116
116
|
generator maps a domain event to `{ title, body, link, data }`, and the
|
|
117
117
|
events are exactly what does not port: `order.paid`, `stock.low` and
|
|
118
|
-
`short-payment` are
|
|
118
|
+
`short-payment` are the origin host's, not any host's. Pass them in `generators`,
|
|
119
119
|
or call `registerGenerator` for a module imported later. The generator's
|
|
120
120
|
`category` is what the router gates fan-out on.
|
|
121
121
|
|
|
@@ -184,7 +184,7 @@ sweep — endpoints and screens included.
|
|
|
184
184
|
notified — and the log line that tells "nobody holds it" apart from "every
|
|
185
185
|
dispatch failed". Two rules for your implementation:
|
|
186
186
|
|
|
187
|
-
- `listCandidates` must be **bounded to people who hold a role**.
|
|
187
|
+
- `listCandidates` must be **bounded to people who hold a role**. The origin host's
|
|
188
188
|
requires a role grant, which keeps a store's storefront BUYERS out of a loop
|
|
189
189
|
that resolves permissions one user at a time.
|
|
190
190
|
- `getPermissions` must be **scoped to the tenant**. Unioning a user's grants
|
|
@@ -222,7 +222,7 @@ sweep — endpoints and screens included.
|
|
|
222
222
|
|
|
223
223
|
13. **These endpoints are cookie-authenticated WRITES. CSRF is yours.** Eight of
|
|
224
224
|
the nine change state, and `resolveActor` typically reads a session cookie —
|
|
225
|
-
so the host, not this package, owns the cross-site question. In
|
|
225
|
+
so the host, not this package, owns the cross-site question. In the origin host it
|
|
226
226
|
is fully mitigated by the Auth.js cookie's `SameSite=Lax`; a host
|
|
227
227
|
authenticating with `SameSite=None`, or one whose `resolveActor` trusts a
|
|
228
228
|
header a proxy sets, inherits an unguarded write surface. The one that matters
|
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/notifications",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Plug-and-play notification system (12-15): an always-on in-app inbox, per-user × per-category channel preferences, and email / SMS / WhatsApp / web-push transports behind vendor DRIVERS so a second provider is a config entry. Framework-free core (.), host-mounted backend surface (./server: inbox / preferences / push-subscription endpoints, the channel router with delivery records + retry sweep, the permission fan-out, duck-typed Prisma seam), Hono adapter (./hono), React surface (./react: bell + badge, inbox drawer, preferences screen), VAPID sender (./web-push) and the package-owned Prisma partial + migrations. Standardized adoption contract in ADOPTING.md.",
|
|
6
6
|
"exports": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"prisma:sync:check": "node scripts/sync-notifications-schema.mjs --check"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@12-apps/ui": "^
|
|
25
|
+
"@12-apps/ui": "^5.0.0"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"hono": ">=4.0.0",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
--
|
|
25
25
|
-- ============================ REPLAY SAFETY ================================
|
|
26
26
|
-- Every statement is guarded, because the first adopters ALREADY HAVE these
|
|
27
|
-
-- tables:
|
|
27
|
+
-- tables: the origin host created them by hand before the package existed, so this
|
|
28
28
|
-- migration must be a no-op there and correct on an empty database.
|
|
29
29
|
--
|
|
30
30
|
-- The guards are per COLUMN, not per table. `CREATE TABLE IF NOT EXISTS` alone
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
// and for a stronger reason: the category set is HOST vocabulary (`categories`
|
|
23
23
|
// on the server config), so a closed set in the schema would be wrong for every
|
|
24
24
|
// adopter but the first. A host that wants its own taxonomy enforced adds the
|
|
25
|
-
// CHECK in a migration of its own —
|
|
25
|
+
// CHECK in a migration of its own — the origin host does. Only `channel` and `status`
|
|
26
26
|
// on the delivery row are closed here, because those two are the LIBRARY's.
|
|
27
27
|
model Notification {
|
|
28
28
|
id String @id @default(uuid())
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* The host package that owns the schema folder MUST also declare this package
|
|
21
21
|
* as a dependency, so the source of the copy is present in every build context.
|
|
22
22
|
*
|
|
23
|
-
* Default host path follows the
|
|
23
|
+
* Default host path follows the origin host layout
|
|
24
24
|
* (`packages/prisma/prisma/schema/`); another repo passes its own schema folder
|
|
25
25
|
* as the positional argument, or sets NOTIFICATIONS_HOST_SCHEMA_DIR.
|
|
26
26
|
*/
|