@krizaka/notifications 0.0.0-stage → 0.2.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/LICENSE +201 -0
- package/README.md +37 -2
- package/dist/chunk-2MH7PZDT.js +88 -0
- package/dist/chunk-2MH7PZDT.js.map +1 -0
- package/dist/chunk-FQYNUK64.js +29 -0
- package/dist/chunk-FQYNUK64.js.map +1 -0
- package/dist/index.d.ts +168 -0
- package/dist/index.js +296 -0
- package/dist/index.js.map +1 -0
- package/dist/port-DptCmYwy.d.ts +122 -0
- package/dist/settings.d.ts +30 -0
- package/dist/settings.js +52 -0
- package/dist/settings.js.map +1 -0
- package/dist/testkit.d.ts +10 -0
- package/dist/testkit.js +117 -0
- package/dist/testkit.js.map +1 -0
- package/package.json +84 -3
package/dist/testkit.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CHANNEL
|
|
3
|
+
} from "./chunk-FQYNUK64.js";
|
|
4
|
+
|
|
5
|
+
// src/testkit.ts
|
|
6
|
+
import { randomUUID } from "crypto";
|
|
7
|
+
import { expect, it } from "vitest";
|
|
8
|
+
var T0 = /* @__PURE__ */ new Date("2026-10-26T12:00:00.000Z");
|
|
9
|
+
var at = (seconds) => new Date(T0.valueOf() + seconds * 1e3);
|
|
10
|
+
var itemOf = (accountId, over = {}) => ({ id: randomUUID(), accountId, category: "messages", template: "new-message", payload: { variables: {} }, dedupeKey: null, inBell: true, createdAt: T0, readAt: null, ...over });
|
|
11
|
+
var BURST = { max: 3, since: at(-3600) };
|
|
12
|
+
function notificationStoreContract(name, make, accountId) {
|
|
13
|
+
it(`${name}: records an item, and says it is capped once the window already holds the cap`, async () => {
|
|
14
|
+
const store = await make();
|
|
15
|
+
const a = await accountId();
|
|
16
|
+
const results = [];
|
|
17
|
+
for (let i = 0; i < 5; i += 1) results.push(await store.record(itemOf(a, { createdAt: at(i) }), BURST));
|
|
18
|
+
expect(results.map((r) => r.capped)).toEqual([false, false, false, true, true]);
|
|
19
|
+
expect(results.every((r) => !r.duplicate)).toBe(true);
|
|
20
|
+
expect((await store.record(itemOf(a, { category: "follows" }), BURST)).capped).toBe(false);
|
|
21
|
+
expect((await store.record(itemOf(a), { max: 3, since: at(100) })).capped).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
it(`${name}: writes nothing for a second item with the same dedupe key, and lets another account use it`, async () => {
|
|
24
|
+
const store = await make();
|
|
25
|
+
const a = await accountId();
|
|
26
|
+
const b = await accountId();
|
|
27
|
+
expect(await store.record(itemOf(a, { dedupeKey: "bid:1" }), BURST)).toEqual({ duplicate: false, capped: false });
|
|
28
|
+
expect(await store.record(itemOf(a, { dedupeKey: "bid:1" }), BURST)).toEqual({ duplicate: true, capped: false });
|
|
29
|
+
expect(await store.record(itemOf(b, { dedupeKey: "bid:1" }), BURST)).toEqual({ duplicate: false, capped: false });
|
|
30
|
+
expect(await store.list(a, { unreadOnly: false, limit: 10 })).toHaveLength(1);
|
|
31
|
+
});
|
|
32
|
+
it(`${name}: lists the bell newest first with a cursor, hides what was not shown, and counts the unread`, async () => {
|
|
33
|
+
const store = await make();
|
|
34
|
+
const a = await accountId();
|
|
35
|
+
const ids = [];
|
|
36
|
+
for (let i = 0; i < 5; i += 1) {
|
|
37
|
+
const item = itemOf(a, { createdAt: at(i) });
|
|
38
|
+
ids.push(item.id);
|
|
39
|
+
await store.record(item, { max: 100, since: at(-3600) });
|
|
40
|
+
}
|
|
41
|
+
await store.record(itemOf(a, { createdAt: at(9), inBell: false }), { max: 100, since: at(-3600) });
|
|
42
|
+
const first = await store.list(a, { unreadOnly: false, limit: 2 });
|
|
43
|
+
expect(first.map((i) => i.id)).toEqual([ids[4], ids[3]]);
|
|
44
|
+
const next = await store.list(a, { unreadOnly: false, limit: 10, before: { createdAt: first[1].createdAt, id: first[1].id } });
|
|
45
|
+
expect(next.map((i) => i.id)).toEqual([ids[2], ids[1], ids[0]]);
|
|
46
|
+
expect(await store.unreadCount(a)).toBe(5);
|
|
47
|
+
});
|
|
48
|
+
it(`${name}: marks read only the account's own unread items, keeps the first read_at`, async () => {
|
|
49
|
+
const store = await make();
|
|
50
|
+
const a = await accountId();
|
|
51
|
+
const b = await accountId();
|
|
52
|
+
const mine = itemOf(a);
|
|
53
|
+
const theirs = itemOf(b);
|
|
54
|
+
const other = itemOf(a, { createdAt: at(1) });
|
|
55
|
+
for (const i of [mine, theirs, other]) await store.record(i, BURST);
|
|
56
|
+
expect(await store.markRead(a, [mine.id, theirs.id], at(10))).toBe(1);
|
|
57
|
+
expect(await store.markRead(a, [mine.id], at(20))).toBe(0);
|
|
58
|
+
expect((await store.list(a, { unreadOnly: false, limit: 10 })).find((i) => i.id === mine.id)?.readAt?.toISOString()).toBe(at(10).toISOString());
|
|
59
|
+
expect(await store.unreadCount(b)).toBe(1);
|
|
60
|
+
expect((await store.list(a, { unreadOnly: true, limit: 10 })).map((i) => i.id)).toEqual([other.id]);
|
|
61
|
+
expect(await store.markRead(a, null, at(30))).toBe(1);
|
|
62
|
+
expect(await store.unreadCount(a)).toBe(0);
|
|
63
|
+
});
|
|
64
|
+
it(`${name}: keeps an account's choices, one per category and channel, the last one winning`, async () => {
|
|
65
|
+
const store = await make();
|
|
66
|
+
const a = await accountId();
|
|
67
|
+
await store.setPreference(a, "messages", CHANNEL.email, false, T0);
|
|
68
|
+
await store.setPreference(a, "messages", CHANNEL.push, true, T0);
|
|
69
|
+
await store.setPreference(a, "messages", CHANNEL.email, true, at(5));
|
|
70
|
+
const rows = (await store.preferences(a)).sort((x, y) => x.channel.localeCompare(y.channel));
|
|
71
|
+
expect(rows).toEqual([
|
|
72
|
+
{ category: "messages", channel: "email", enabled: true },
|
|
73
|
+
{ category: "messages", channel: "push", enabled: true }
|
|
74
|
+
]);
|
|
75
|
+
expect(await store.preferences(await accountId())).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
it(`${name}: registers a device once per token, moves it to a new account, revokes it and brings it back`, async () => {
|
|
78
|
+
const store = await make();
|
|
79
|
+
const a = await accountId();
|
|
80
|
+
const b = await accountId();
|
|
81
|
+
const device = (account, token, seconds) => ({ id: randomUUID(), accountId: account, platform: "ios", token, createdAt: at(seconds), lastSeenAt: at(seconds) });
|
|
82
|
+
await store.upsertDevice(device(a, "tok-1", 0));
|
|
83
|
+
await store.upsertDevice(device(a, "tok-1", 5));
|
|
84
|
+
await store.upsertDevice(device(a, "tok-2", 6));
|
|
85
|
+
expect((await store.devices(a)).map((d) => d.token)).toEqual(["tok-1", "tok-2"]);
|
|
86
|
+
expect((await store.devices(a))[0].createdAt.toISOString()).toBe(at(0).toISOString());
|
|
87
|
+
expect(await store.revokeDevice("tok-1", at(10))).toBe(true);
|
|
88
|
+
expect(await store.revokeDevice("tok-1", at(11))).toBe(false);
|
|
89
|
+
expect((await store.devices(a)).map((d) => d.token)).toEqual(["tok-2"]);
|
|
90
|
+
await store.upsertDevice(device(b, "tok-1", 20));
|
|
91
|
+
expect((await store.devices(b)).map((d) => d.token)).toEqual(["tok-1"]);
|
|
92
|
+
expect((await store.devices(a)).map((d) => d.token)).toEqual(["tok-2"]);
|
|
93
|
+
});
|
|
94
|
+
it(`${name}: purges old items and erases everything about an account`, async () => {
|
|
95
|
+
const store = await make();
|
|
96
|
+
const a = await accountId();
|
|
97
|
+
const b = await accountId();
|
|
98
|
+
await store.record(itemOf(a, { createdAt: at(-86400 * 10) }), { max: 100, since: at(-86400 * 20) });
|
|
99
|
+
await store.record(itemOf(a), { max: 100, since: at(-3600) });
|
|
100
|
+
expect(await store.purgeBefore(at(-86400))).toBe(1);
|
|
101
|
+
await store.record(itemOf(b), BURST);
|
|
102
|
+
await store.setPreference(a, "messages", CHANNEL.app, false, T0);
|
|
103
|
+
await store.upsertDevice({ id: randomUUID(), accountId: a, platform: "web", token: "tok-erase", createdAt: T0, lastSeenAt: T0 });
|
|
104
|
+
expect(await store.erase(a)).toBe(3);
|
|
105
|
+
expect(await store.unreadCount(a)).toBe(0);
|
|
106
|
+
expect(await store.preferences(a)).toEqual([]);
|
|
107
|
+
expect(await store.devices(a)).toEqual([]);
|
|
108
|
+
expect(await store.unreadCount(b)).toBe(1);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
T0,
|
|
113
|
+
at,
|
|
114
|
+
itemOf,
|
|
115
|
+
notificationStoreContract
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=testkit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testkit.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nimport { expect, it } from \"vitest\";\n\nimport { CHANNEL, type Item, type NotificationStore } from \"./port\";\n\nexport const T0 = new Date(\"2026-10-26T12:00:00.000Z\");\nexport const at = (seconds: number) => new Date(T0.valueOf() + seconds * 1000);\n\n/** An item for tests; the account ids are UUIDs because the database's are. */\nexport const itemOf = (accountId: string, over: Partial<Item> = {}): Item => ({ id: randomUUID(), accountId, category: \"messages\", template: \"new-message\", payload: { variables: {} }, dedupeKey: null, inBell: true, createdAt: T0, readAt: null, ...over });\nconst BURST = { max: 3, since: at(-3600) };\n\n/** What every `NotificationStore` adapter must do: the same behaviour behind the same port. `make` gives an empty store whose accounts exist. */\nexport function notificationStoreContract(name: string, make: () => NotificationStore | Promise<NotificationStore>, accountId: () => string | Promise<string>) {\n it(`${name}: records an item, and says it is capped once the window already holds the cap`, async () => {\n const store = await make();\n const a = await accountId();\n const results = [];\n for (let i = 0; i < 5; i += 1) results.push(await store.record(itemOf(a, { createdAt: at(i) }), BURST));\n expect(results.map((r) => r.capped)).toEqual([false, false, false, true, true]);\n expect(results.every((r) => !r.duplicate)).toBe(true);\n // another category and another window are counted apart\n expect((await store.record(itemOf(a, { category: \"follows\" }), BURST)).capped).toBe(false);\n expect((await store.record(itemOf(a), { max: 3, since: at(100) })).capped).toBe(false);\n });\n\n it(`${name}: writes nothing for a second item with the same dedupe key, and lets another account use it`, async () => {\n const store = await make();\n const a = await accountId();\n const b = await accountId();\n expect(await store.record(itemOf(a, { dedupeKey: \"bid:1\" }), BURST)).toEqual({ duplicate: false, capped: false });\n expect(await store.record(itemOf(a, { dedupeKey: \"bid:1\" }), BURST)).toEqual({ duplicate: true, capped: false });\n expect(await store.record(itemOf(b, { dedupeKey: \"bid:1\" }), BURST)).toEqual({ duplicate: false, capped: false });\n expect(await store.list(a, { unreadOnly: false, limit: 10 })).toHaveLength(1);\n });\n\n it(`${name}: lists the bell newest first with a cursor, hides what was not shown, and counts the unread`, async () => {\n const store = await make();\n const a = await accountId();\n const ids: string[] = [];\n for (let i = 0; i < 5; i += 1) {\n const item = itemOf(a, { createdAt: at(i) });\n ids.push(item.id);\n await store.record(item, { max: 100, since: at(-3600) });\n }\n await store.record(itemOf(a, { createdAt: at(9), inBell: false }), { max: 100, since: at(-3600) });\n const first = await store.list(a, { unreadOnly: false, limit: 2 });\n expect(first.map((i) => i.id)).toEqual([ids[4], ids[3]]);\n const next = await store.list(a, { unreadOnly: false, limit: 10, before: { createdAt: first[1]!.createdAt, id: first[1]!.id } });\n expect(next.map((i) => i.id)).toEqual([ids[2], ids[1], ids[0]]);\n expect(await store.unreadCount(a)).toBe(5);\n });\n\n it(`${name}: marks read only the account's own unread items, keeps the first read_at`, async () => {\n const store = await make();\n const a = await accountId();\n const b = await accountId();\n const mine = itemOf(a);\n const theirs = itemOf(b);\n const other = itemOf(a, { createdAt: at(1) });\n for (const i of [mine, theirs, other]) await store.record(i, BURST);\n expect(await store.markRead(a, [mine.id, theirs.id], at(10))).toBe(1);\n expect(await store.markRead(a, [mine.id], at(20))).toBe(0);\n expect((await store.list(a, { unreadOnly: false, limit: 10 })).find((i) => i.id === mine.id)?.readAt?.toISOString()).toBe(at(10).toISOString());\n expect(await store.unreadCount(b)).toBe(1);\n expect((await store.list(a, { unreadOnly: true, limit: 10 })).map((i) => i.id)).toEqual([other.id]);\n expect(await store.markRead(a, null, at(30))).toBe(1);\n expect(await store.unreadCount(a)).toBe(0);\n });\n\n it(`${name}: keeps an account's choices, one per category and channel, the last one winning`, async () => {\n const store = await make();\n const a = await accountId();\n await store.setPreference(a, \"messages\", CHANNEL.email, false, T0);\n await store.setPreference(a, \"messages\", CHANNEL.push, true, T0);\n await store.setPreference(a, \"messages\", CHANNEL.email, true, at(5));\n const rows = (await store.preferences(a)).sort((x, y) => x.channel.localeCompare(y.channel));\n expect(rows).toEqual([\n { category: \"messages\", channel: \"email\", enabled: true },\n { category: \"messages\", channel: \"push\", enabled: true },\n ]);\n expect(await store.preferences(await accountId())).toEqual([]);\n });\n\n it(`${name}: registers a device once per token, moves it to a new account, revokes it and brings it back`, async () => {\n const store = await make();\n const a = await accountId();\n const b = await accountId();\n const device = (account: string, token: string, seconds: number) => ({ id: randomUUID(), accountId: account, platform: \"ios\" as const, token, createdAt: at(seconds), lastSeenAt: at(seconds) });\n await store.upsertDevice(device(a, \"tok-1\", 0));\n await store.upsertDevice(device(a, \"tok-1\", 5));\n await store.upsertDevice(device(a, \"tok-2\", 6));\n expect((await store.devices(a)).map((d) => d.token)).toEqual([\"tok-1\", \"tok-2\"]);\n expect((await store.devices(a))[0]!.createdAt.toISOString()).toBe(at(0).toISOString());\n expect(await store.revokeDevice(\"tok-1\", at(10))).toBe(true);\n expect(await store.revokeDevice(\"tok-1\", at(11))).toBe(false);\n expect((await store.devices(a)).map((d) => d.token)).toEqual([\"tok-2\"]);\n await store.upsertDevice(device(b, \"tok-1\", 20));\n expect((await store.devices(b)).map((d) => d.token)).toEqual([\"tok-1\"]);\n expect((await store.devices(a)).map((d) => d.token)).toEqual([\"tok-2\"]);\n });\n\n it(`${name}: purges old items and erases everything about an account`, async () => {\n const store = await make();\n const a = await accountId();\n const b = await accountId();\n await store.record(itemOf(a, { createdAt: at(-86_400 * 10) }), { max: 100, since: at(-86_400 * 20) });\n await store.record(itemOf(a), { max: 100, since: at(-3600) });\n expect(await store.purgeBefore(at(-86_400))).toBe(1);\n await store.record(itemOf(b), BURST);\n await store.setPreference(a, \"messages\", CHANNEL.app, false, T0);\n await store.upsertDevice({ id: randomUUID(), accountId: a, platform: \"web\", token: \"tok-erase\", createdAt: T0, lastSeenAt: T0 });\n expect(await store.erase(a)).toBe(3);\n expect(await store.unreadCount(a)).toBe(0);\n expect(await store.preferences(a)).toEqual([]);\n expect(await store.devices(a)).toEqual([]);\n expect(await store.unreadCount(b)).toBe(1);\n });\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,87 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/notifications",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "The bell, channels and preferences for every Krizaka product: one notify() writes the in-app item and appends one outbox message per enabled channel (e-mail, push per device, live), respecting the account's matrix of categories by channels and per-category burst caps that hold under concurrency. The product declares its categories; the admin tunes defaults and caps. No push or realtime import, no wall clock.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"krizaka",
|
|
7
|
+
"notifications",
|
|
8
|
+
"bell",
|
|
9
|
+
"preferences",
|
|
10
|
+
"push",
|
|
11
|
+
"email",
|
|
12
|
+
"outbox"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/krizaka/krizaka-ts/tree/main/packages/notifications",
|
|
15
|
+
"bugs": "https://github.com/krizaka/krizaka-ts/issues",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/krizaka/krizaka-ts.git",
|
|
19
|
+
"directory": "packages/notifications"
|
|
20
|
+
},
|
|
21
|
+
"license": "Apache-2.0",
|
|
22
|
+
"author": "Krizaka (https://www.krizaka.com)",
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"module": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./settings": {
|
|
33
|
+
"types": "./dist/settings.d.ts",
|
|
34
|
+
"import": "./dist/settings.js"
|
|
35
|
+
},
|
|
36
|
+
"./testkit": {
|
|
37
|
+
"types": "./dist/testkit.d.ts",
|
|
38
|
+
"import": "./dist/testkit.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
46
|
+
],
|
|
47
|
+
"sideEffects": false,
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=22"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^22.10.0",
|
|
53
|
+
"@types/pg": "^8.23.1",
|
|
54
|
+
"eslint": "^9.39.0",
|
|
55
|
+
"pg": "^8.23.1",
|
|
56
|
+
"tsup": "^8.5.0",
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"vitest": "^4.1.11",
|
|
59
|
+
"zod": "^4.6.7",
|
|
60
|
+
"@krizaka/config": "^0.8.1",
|
|
61
|
+
"@krizaka/outbox": "^0.1.1"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@krizaka/db": "^0.2.0",
|
|
65
|
+
"@krizaka/settings": "^0.5.0",
|
|
66
|
+
"@krizaka/time": "^0.1.0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"vitest": ">=4",
|
|
70
|
+
"zod": ">=3.23"
|
|
71
|
+
},
|
|
72
|
+
"peerDependenciesMeta": {
|
|
73
|
+
"vitest": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"zod": {
|
|
77
|
+
"optional": true
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"scripts": {
|
|
81
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsup",
|
|
82
|
+
"lint": "eslint . --max-warnings 0 && tsc --noEmit",
|
|
83
|
+
"typecheck": "tsc --noEmit",
|
|
84
|
+
"test": "vitest run",
|
|
85
|
+
"check": "pnpm run lint && pnpm run test && pnpm run build"
|
|
86
|
+
}
|
|
6
87
|
}
|