@weifuwujs/messages 0.0.0-stage → 1.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/README.md +259 -2
- package/dist/bus.d.ts +7 -0
- package/dist/bus.d.ts.map +1 -0
- package/dist/bus.js +30 -0
- package/dist/bus.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +82 -0
- package/dist/cli.js.map +1 -0
- package/dist/cursor.d.ts +20 -0
- package/dist/cursor.d.ts.map +1 -0
- package/dist/cursor.js +49 -0
- package/dist/cursor.js.map +1 -0
- package/dist/hub.d.ts +15 -0
- package/dist/hub.d.ts.map +1 -0
- package/dist/hub.js +51 -0
- package/dist/hub.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/messages.d.ts +10 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +272 -0
- package/dist/messages.js.map +1 -0
- package/dist/postgres.d.ts +3 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +2 -0
- package/dist/postgres.js.map +1 -0
- package/dist/redis.d.ts +24 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +126 -0
- package/dist/redis.js.map +1 -0
- package/dist/sqlite.d.ts +3 -0
- package/dist/sqlite.d.ts.map +1 -0
- package/dist/sqlite.js +2 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/stores/memory.d.ts +6 -0
- package/dist/stores/memory.d.ts.map +1 -0
- package/dist/stores/memory.js +115 -0
- package/dist/stores/memory.js.map +1 -0
- package/dist/stores/postgres.d.ts +22 -0
- package/dist/stores/postgres.d.ts.map +1 -0
- package/dist/stores/postgres.js +192 -0
- package/dist/stores/postgres.js.map +1 -0
- package/dist/stores/sqlite.d.ts +13 -0
- package/dist/stores/sqlite.d.ts.map +1 -0
- package/dist/stores/sqlite.js +180 -0
- package/dist/stores/sqlite.js.map +1 -0
- package/dist/testing.d.ts +24 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +248 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +171 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -4
- package/templates/react-router-messages/app/messages.ts +13 -0
- package/templates/react-router-messages/app/routes/home.tsx +51 -0
- package/templates/react-router-messages/app/routes/notifications.tsx +91 -0
- package/templates/react-router-messages/app/routes.ts +10 -0
- package/templates/react-router-messages/app/server.ts +41 -0
package/dist/messages.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { json } from '@weifuwujs/weifuwu';
|
|
2
|
+
import { localMessageBus } from "./bus.js";
|
|
3
|
+
import { decodeCursor } from "./cursor.js";
|
|
4
|
+
import { localMessageHub } from "./hub.js";
|
|
5
|
+
const MAX_PAGE_SIZE = 100;
|
|
6
|
+
const DEFAULT_PAGE_SIZE = 20;
|
|
7
|
+
/** 从 ctx.auth.user.id 取当前用户(与 auth 模块结构耦合,不 import) */
|
|
8
|
+
function userIdOf(ctx) {
|
|
9
|
+
const id = ctx.auth?.user?.id;
|
|
10
|
+
return typeof id === 'string' && id.length > 0 ? id : null;
|
|
11
|
+
}
|
|
12
|
+
function unauthorized() {
|
|
13
|
+
return json({ error: 'unauthorized' }, { status: 401 });
|
|
14
|
+
}
|
|
15
|
+
const badRequest = (error) => json({ error }, { status: 400 });
|
|
16
|
+
/**
|
|
17
|
+
* 站内信/通知中心:持久化 + 未读计数 + 本人可见的只读 REST。
|
|
18
|
+
*
|
|
19
|
+
* - `send` 是服务端发送入口(HTTP 不暴露写消息,防越权群发);
|
|
20
|
+
* - 至少一次 + 按 id 去重由客户端/实时层承担,幂等键由 store 唯一约束承担;
|
|
21
|
+
* - 实时推送(WebSocket)见后续阶段,`messages.routes()` 先提供轮询兜底。
|
|
22
|
+
*/
|
|
23
|
+
export function createMessages(options) {
|
|
24
|
+
const store = options.store;
|
|
25
|
+
const now = options.now ?? (() => new Date());
|
|
26
|
+
const logger = options.logger ?? console;
|
|
27
|
+
const prefix = options.routePrefix ?? '/messages';
|
|
28
|
+
const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, options.pageSize ?? DEFAULT_PAGE_SIZE));
|
|
29
|
+
const syncLimit = Math.max(1, options.syncLimit ?? 100);
|
|
30
|
+
const hub = localMessageHub();
|
|
31
|
+
const bus = options.bus ?? localMessageBus();
|
|
32
|
+
const ownsBus = options.bus === undefined;
|
|
33
|
+
/** 每个 userId 的 bus 订阅停止函数(首个连接 watch,末个连接 unwatch) */
|
|
34
|
+
const watchers = new Map();
|
|
35
|
+
async function publishEvent(userId, event) {
|
|
36
|
+
try {
|
|
37
|
+
await bus.ready?.();
|
|
38
|
+
await bus.publish(userId, event);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
logger.error(`[messages] 推送失败(消息已持久化):${error instanceof Error ? error.message : String(error)}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function publishUnread(userId) {
|
|
45
|
+
await publishEvent(userId, {
|
|
46
|
+
type: 'unread',
|
|
47
|
+
unread: await store.unreadCount(userId),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function addConnection(userId, socket) {
|
|
51
|
+
const remove = hub.add(userId, socket);
|
|
52
|
+
if (hub.connections(userId) === 1) {
|
|
53
|
+
watchers.set(userId, bus.watch(userId, (event) => hub.publish(userId, event)));
|
|
54
|
+
}
|
|
55
|
+
return () => {
|
|
56
|
+
remove();
|
|
57
|
+
if (hub.connections(userId) === 0) {
|
|
58
|
+
watchers.get(userId)?.();
|
|
59
|
+
watchers.delete(userId);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function close() {
|
|
64
|
+
for (const stop of watchers.values())
|
|
65
|
+
stop();
|
|
66
|
+
watchers.clear();
|
|
67
|
+
hub.closeAll();
|
|
68
|
+
if (ownsBus)
|
|
69
|
+
await bus.close?.();
|
|
70
|
+
}
|
|
71
|
+
async function markRead(userId, ids) {
|
|
72
|
+
const updated = await store.markRead(userId, ids, now());
|
|
73
|
+
if (updated > 0)
|
|
74
|
+
await publishUnread(userId);
|
|
75
|
+
return updated;
|
|
76
|
+
}
|
|
77
|
+
async function markAllRead(userId) {
|
|
78
|
+
const updated = await store.markAllRead(userId, now());
|
|
79
|
+
if (updated > 0)
|
|
80
|
+
await publishUnread(userId);
|
|
81
|
+
return updated;
|
|
82
|
+
}
|
|
83
|
+
async function send(input) {
|
|
84
|
+
if (typeof input.title !== 'string' || input.title.trim() === '') {
|
|
85
|
+
throw new TypeError('messages.send: title is required');
|
|
86
|
+
}
|
|
87
|
+
if (input.data !== undefined && input.data !== null) {
|
|
88
|
+
try {
|
|
89
|
+
JSON.stringify(input.data);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
throw new TypeError('messages.send: data must be JSON-serializable');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const result = await store.insert({
|
|
96
|
+
userId: input.userId,
|
|
97
|
+
kind: input.kind ?? 'default',
|
|
98
|
+
title: input.title,
|
|
99
|
+
body: input.body ?? null,
|
|
100
|
+
data: input.data ?? null,
|
|
101
|
+
idempotencyKey: input.idempotencyKey ?? null,
|
|
102
|
+
createdAt: now(),
|
|
103
|
+
});
|
|
104
|
+
if (result.created && options.onSend) {
|
|
105
|
+
try {
|
|
106
|
+
await options.onSend(result.message);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
logger.error(`[messages] onSend hook 失败(消息已持久化):${error instanceof Error ? error.message : String(error)}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (result.created) {
|
|
113
|
+
await publishEvent(result.message.userId, {
|
|
114
|
+
type: 'message',
|
|
115
|
+
message: result.message,
|
|
116
|
+
});
|
|
117
|
+
await publishUnread(result.message.userId);
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
async function readJsonBody(req) {
|
|
122
|
+
const text = await req.text();
|
|
123
|
+
if (text.trim() === '')
|
|
124
|
+
return { ok: true, value: null };
|
|
125
|
+
try {
|
|
126
|
+
return { ok: true, value: JSON.parse(text) };
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return { ok: false, response: badRequest('invalid_json') };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function parseLimit(raw) {
|
|
133
|
+
if (raw === undefined)
|
|
134
|
+
return pageSize;
|
|
135
|
+
const parsed = Number(raw);
|
|
136
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_PAGE_SIZE)
|
|
137
|
+
return null;
|
|
138
|
+
return parsed;
|
|
139
|
+
}
|
|
140
|
+
const routes = () => [
|
|
141
|
+
{
|
|
142
|
+
method: 'GET',
|
|
143
|
+
path: prefix,
|
|
144
|
+
handler: async (req, ctx) => {
|
|
145
|
+
const userId = userIdOf(ctx);
|
|
146
|
+
if (!userId)
|
|
147
|
+
return unauthorized();
|
|
148
|
+
const limit = parseLimit(ctx.query.limit);
|
|
149
|
+
if (limit === null)
|
|
150
|
+
return badRequest('invalid_limit');
|
|
151
|
+
const cursor = ctx.query.cursor ?? null;
|
|
152
|
+
if (cursor !== null && decodeCursor(cursor) === null) {
|
|
153
|
+
return badRequest('invalid_cursor');
|
|
154
|
+
}
|
|
155
|
+
const page = await store.list({
|
|
156
|
+
userId,
|
|
157
|
+
limit,
|
|
158
|
+
cursor,
|
|
159
|
+
unreadOnly: ctx.query.unread === '1' || ctx.query.unread === 'true',
|
|
160
|
+
kind: ctx.query.kind || undefined,
|
|
161
|
+
});
|
|
162
|
+
return json({
|
|
163
|
+
items: page.items,
|
|
164
|
+
nextCursor: page.nextCursor,
|
|
165
|
+
unread: await store.unreadCount(userId),
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
method: 'GET',
|
|
171
|
+
path: `${prefix}/unread`,
|
|
172
|
+
handler: async (req, ctx) => {
|
|
173
|
+
const userId = userIdOf(ctx);
|
|
174
|
+
if (!userId)
|
|
175
|
+
return unauthorized();
|
|
176
|
+
return json({ unread: await store.unreadCount(userId) });
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
method: 'POST',
|
|
181
|
+
path: `${prefix}/read`,
|
|
182
|
+
handler: async (req, ctx) => {
|
|
183
|
+
const userId = userIdOf(ctx);
|
|
184
|
+
if (!userId)
|
|
185
|
+
return unauthorized();
|
|
186
|
+
const body = await readJsonBody(req);
|
|
187
|
+
if (!body.ok)
|
|
188
|
+
return body.response;
|
|
189
|
+
const ids = body.value?.ids;
|
|
190
|
+
if (!Array.isArray(ids) || ids.some((id) => typeof id !== 'string')) {
|
|
191
|
+
return badRequest('invalid_ids');
|
|
192
|
+
}
|
|
193
|
+
return json({ updated: await markRead(userId, ids) });
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
method: 'POST',
|
|
198
|
+
path: `${prefix}/read-all`,
|
|
199
|
+
handler: async (req, ctx) => {
|
|
200
|
+
const userId = userIdOf(ctx);
|
|
201
|
+
if (!userId)
|
|
202
|
+
return unauthorized();
|
|
203
|
+
return json({ updated: await markAllRead(userId) });
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
const api = {
|
|
208
|
+
store,
|
|
209
|
+
send,
|
|
210
|
+
list: (query) => store.list(query),
|
|
211
|
+
unread: (userId) => store.unreadCount(userId),
|
|
212
|
+
markRead,
|
|
213
|
+
markAllRead,
|
|
214
|
+
routes,
|
|
215
|
+
install(app, installOptions = {}) {
|
|
216
|
+
if (installOptions.rest !== false)
|
|
217
|
+
app.mount(api);
|
|
218
|
+
const wsPath = installOptions.wsPath ?? `${prefix}/ws`;
|
|
219
|
+
const guard = async (req, ctx, next) => {
|
|
220
|
+
if (!userIdOf(ctx))
|
|
221
|
+
return new Response('Unauthorized', { status: 401 });
|
|
222
|
+
return next(req, ctx);
|
|
223
|
+
};
|
|
224
|
+
app.ws(wsPath, guard, async (socket, ctx) => {
|
|
225
|
+
const userId = userIdOf(ctx);
|
|
226
|
+
if (!userId) {
|
|
227
|
+
socket.close(1008, 'unauthorized');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const sinceParam = ctx.query.since;
|
|
231
|
+
if (sinceParam !== undefined &&
|
|
232
|
+
sinceParam !== '' &&
|
|
233
|
+
decodeCursor(sinceParam) === null) {
|
|
234
|
+
socket.close(1008, 'invalid cursor');
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// subscribe-first:竞态下宁可重复(由 id 去重)也不丢失
|
|
238
|
+
const unsubscribe = addConnection(userId, socket);
|
|
239
|
+
if (sinceParam !== undefined) {
|
|
240
|
+
const page = await store.listAfter({
|
|
241
|
+
userId,
|
|
242
|
+
cursor: sinceParam === '' ? null : sinceParam,
|
|
243
|
+
limit: syncLimit,
|
|
244
|
+
});
|
|
245
|
+
socket.send(JSON.stringify({
|
|
246
|
+
type: 'sync',
|
|
247
|
+
items: page.items,
|
|
248
|
+
hasMore: page.hasMore,
|
|
249
|
+
unread: await store.unreadCount(userId),
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
socket.send(JSON.stringify({ type: 'unread', unread: await store.unreadCount(userId) }));
|
|
254
|
+
}
|
|
255
|
+
return { onClose: () => unsubscribe() };
|
|
256
|
+
});
|
|
257
|
+
app.onClose(() => close());
|
|
258
|
+
return api;
|
|
259
|
+
},
|
|
260
|
+
connections: (userId) => hub.connections(userId),
|
|
261
|
+
publish: (userId, event) => publishEvent(userId, event),
|
|
262
|
+
async ready() {
|
|
263
|
+
await bus.ready?.();
|
|
264
|
+
},
|
|
265
|
+
close,
|
|
266
|
+
async migrate() {
|
|
267
|
+
await store.migrate?.();
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
return api;
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3D,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/postgres.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/redis.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Redis, RedisOptions } from 'ioredis';
|
|
2
|
+
import type { MessageBus } from './types.ts';
|
|
3
|
+
export interface RedisMessageBusOptions extends RedisOptions {
|
|
4
|
+
/** 频道前缀(默认 `ww:messages`);测试/多租户用独立前缀隔离 */
|
|
5
|
+
prefix?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface RedisMessageBus extends MessageBus {
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* redis pub/sub 总线:多副本部署时让 `send` 到达持有连接的其他实例。
|
|
12
|
+
*
|
|
13
|
+
* - **按需订阅**:本节点有某用户的连接时才 SUBSCRIBE `<prefix>:<userId>`,最后一个连接
|
|
14
|
+
* 断开后自动退订(不把全量事件广播给所有节点);
|
|
15
|
+
* - 复用 `./ioredis` 子路径的同一客户端(`ctx.redis`):注入时 `duplicate()` 出订阅连接,
|
|
16
|
+
* 不负责关闭注入客户端;
|
|
17
|
+
* - 至少一次、可乱序:`unread` 是快照值,`message` 由客户端按 id 去重。
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* redisMessageBus(process.env.REDIS_URL!, { prefix: 'ww:messages' })
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function redisMessageBus(urlOrRedis?: string | Redis, options?: RedisMessageBusOptions): RedisMessageBus;
|
|
24
|
+
//# sourceMappingURL=redis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../src/redis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,YAAY,CAAA;AAE3D,MAAM,WAAW,sBAAuB,SAAQ,YAAY;IAC1D,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,EAC3B,OAAO,GAAE,sBAA2B,GACnC,eAAe,CA2HjB"}
|
package/dist/redis.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* redis pub/sub 总线:多副本部署时让 `send` 到达持有连接的其他实例。
|
|
3
|
+
*
|
|
4
|
+
* - **按需订阅**:本节点有某用户的连接时才 SUBSCRIBE `<prefix>:<userId>`,最后一个连接
|
|
5
|
+
* 断开后自动退订(不把全量事件广播给所有节点);
|
|
6
|
+
* - 复用 `./ioredis` 子路径的同一客户端(`ctx.redis`):注入时 `duplicate()` 出订阅连接,
|
|
7
|
+
* 不负责关闭注入客户端;
|
|
8
|
+
* - 至少一次、可乱序:`unread` 是快照值,`message` 由客户端按 id 去重。
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* redisMessageBus(process.env.REDIS_URL!, { prefix: 'ww:messages' })
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function redisMessageBus(urlOrRedis, options = {}) {
|
|
15
|
+
const injected = typeof urlOrRedis === 'string' || urlOrRedis === undefined ? null : urlOrRedis;
|
|
16
|
+
const { prefix = 'ww:messages', ...redisOptions } = options;
|
|
17
|
+
const channel = (userId) => `${prefix}:${userId}`;
|
|
18
|
+
const handlers = new Map();
|
|
19
|
+
const pending = new Set();
|
|
20
|
+
let publisherPromise = null;
|
|
21
|
+
let subscriberPromise = null;
|
|
22
|
+
const track = (promise) => {
|
|
23
|
+
const op = promise.then(() => undefined);
|
|
24
|
+
pending.add(op);
|
|
25
|
+
void op.finally(() => pending.delete(op)).catch(() => { });
|
|
26
|
+
op.catch(() => { });
|
|
27
|
+
};
|
|
28
|
+
const connect = async () => {
|
|
29
|
+
const { default: RedisClient } = (await import('ioredis'));
|
|
30
|
+
return RedisClient;
|
|
31
|
+
};
|
|
32
|
+
const url = () => typeof urlOrRedis === 'string'
|
|
33
|
+
? urlOrRedis
|
|
34
|
+
: (process.env.REDIS_URL ?? 'redis://127.0.0.1:6379');
|
|
35
|
+
const publisher = async () => {
|
|
36
|
+
publisherPromise ??= (async () => {
|
|
37
|
+
if (injected)
|
|
38
|
+
return injected;
|
|
39
|
+
const RedisCtor = await connect();
|
|
40
|
+
return new RedisCtor(url(), redisOptions);
|
|
41
|
+
})();
|
|
42
|
+
return publisherPromise;
|
|
43
|
+
};
|
|
44
|
+
const subscriber = async () => {
|
|
45
|
+
subscriberPromise ??= (async () => {
|
|
46
|
+
if (injected)
|
|
47
|
+
return injected.duplicate();
|
|
48
|
+
const RedisCtor = await connect();
|
|
49
|
+
const sub = new RedisCtor(url(), redisOptions);
|
|
50
|
+
sub.on('message', (receivedChannel, payload) => {
|
|
51
|
+
if (!receivedChannel.startsWith(`${prefix}:`))
|
|
52
|
+
return;
|
|
53
|
+
const userId = receivedChannel.slice(prefix.length + 1);
|
|
54
|
+
const set = handlers.get(userId);
|
|
55
|
+
if (!set)
|
|
56
|
+
return;
|
|
57
|
+
let event;
|
|
58
|
+
try {
|
|
59
|
+
event = JSON.parse(payload);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
for (const handler of [...set])
|
|
65
|
+
handler(event);
|
|
66
|
+
});
|
|
67
|
+
return sub;
|
|
68
|
+
})();
|
|
69
|
+
return subscriberPromise;
|
|
70
|
+
};
|
|
71
|
+
const whenReady = async () => {
|
|
72
|
+
await publisher();
|
|
73
|
+
while (pending.size > 0)
|
|
74
|
+
await Promise.all([...pending]);
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
async publish(userId, event) {
|
|
78
|
+
// 先等本节点订阅就绪:刚 watch 完就 publish 时不能丢事件(echo/同进程)
|
|
79
|
+
await whenReady();
|
|
80
|
+
const pub = await publisher();
|
|
81
|
+
await pub.publish(channel(userId), JSON.stringify(event));
|
|
82
|
+
},
|
|
83
|
+
watch(userId, handler) {
|
|
84
|
+
let set = handlers.get(userId);
|
|
85
|
+
if (!set) {
|
|
86
|
+
set = new Set();
|
|
87
|
+
handlers.set(userId, set);
|
|
88
|
+
}
|
|
89
|
+
set.add(handler);
|
|
90
|
+
if (set.size === 1) {
|
|
91
|
+
track(subscriber().then((sub) => sub.subscribe(channel(userId))));
|
|
92
|
+
}
|
|
93
|
+
let stopped = false;
|
|
94
|
+
return () => {
|
|
95
|
+
if (stopped)
|
|
96
|
+
return;
|
|
97
|
+
stopped = true;
|
|
98
|
+
set.delete(handler);
|
|
99
|
+
if (set.size === 0) {
|
|
100
|
+
handlers.delete(userId);
|
|
101
|
+
track(subscriber().then((sub) => sub.unsubscribe(channel(userId))));
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
async ready() {
|
|
106
|
+
await whenReady();
|
|
107
|
+
},
|
|
108
|
+
async close() {
|
|
109
|
+
if (injected)
|
|
110
|
+
return;
|
|
111
|
+
const closing = [];
|
|
112
|
+
if (subscriberPromise) {
|
|
113
|
+
const sub = await subscriberPromise;
|
|
114
|
+
subscriberPromise = null;
|
|
115
|
+
closing.push(sub.quit());
|
|
116
|
+
}
|
|
117
|
+
if (publisherPromise) {
|
|
118
|
+
const pub = await publisherPromise;
|
|
119
|
+
publisherPromise = null;
|
|
120
|
+
closing.push(pub.quit());
|
|
121
|
+
}
|
|
122
|
+
await Promise.all(closing);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../src/redis.ts"],"names":[],"mappings":"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"}
|
package/dist/sqlite.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
|
package/dist/sqlite.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAOV,YAAY,EAEb,MAAM,aAAa,CAAA;AAMpB;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,CAyGjD"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { compareAsc, compareDesc, decodeCursor, encodeCursor, isAfterCursor, isBeyondCursor } from "../cursor.js";
|
|
3
|
+
function clone(message) {
|
|
4
|
+
return { ...message, data: message.data };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 内存驱动:dev/test 用(单进程;不作生产承诺)。
|
|
8
|
+
*/
|
|
9
|
+
export function memoryMessageStore() {
|
|
10
|
+
const rows = new Map();
|
|
11
|
+
const byKey = new Map();
|
|
12
|
+
const store = {
|
|
13
|
+
async insert(input) {
|
|
14
|
+
const key = input.idempotencyKey;
|
|
15
|
+
if (key) {
|
|
16
|
+
const existingId = byKey.get(`${input.userId}\u0000${key}`);
|
|
17
|
+
const existing = existingId ? rows.get(existingId) : undefined;
|
|
18
|
+
if (existing)
|
|
19
|
+
return { message: clone(existing), created: false };
|
|
20
|
+
}
|
|
21
|
+
const message = {
|
|
22
|
+
id: randomUUID(),
|
|
23
|
+
userId: input.userId,
|
|
24
|
+
kind: input.kind,
|
|
25
|
+
title: input.title,
|
|
26
|
+
body: input.body,
|
|
27
|
+
data: input.data,
|
|
28
|
+
readAt: null,
|
|
29
|
+
idempotencyKey: key,
|
|
30
|
+
createdAt: input.createdAt,
|
|
31
|
+
};
|
|
32
|
+
rows.set(message.id, message);
|
|
33
|
+
if (key)
|
|
34
|
+
byKey.set(`${input.userId}\u0000${key}`, message.id);
|
|
35
|
+
return { message: clone(message), created: true };
|
|
36
|
+
},
|
|
37
|
+
async get(id, userId) {
|
|
38
|
+
const message = rows.get(id);
|
|
39
|
+
if (!message || message.userId !== userId)
|
|
40
|
+
return null;
|
|
41
|
+
return clone(message);
|
|
42
|
+
},
|
|
43
|
+
async list(query) {
|
|
44
|
+
const limit = query.limit ?? 20;
|
|
45
|
+
let result = [...rows.values()].filter((message) => message.userId === query.userId);
|
|
46
|
+
if (query.unreadOnly)
|
|
47
|
+
result = result.filter((message) => message.readAt === null);
|
|
48
|
+
if (query.kind)
|
|
49
|
+
result = result.filter((message) => message.kind === query.kind);
|
|
50
|
+
result.sort(compareDesc);
|
|
51
|
+
if (query.cursor) {
|
|
52
|
+
const cursor = decodeCursor(query.cursor);
|
|
53
|
+
if (!cursor)
|
|
54
|
+
throw new Error(`invalid cursor: ${query.cursor}`);
|
|
55
|
+
result = result.filter((message) => isAfterCursor(message, cursor));
|
|
56
|
+
}
|
|
57
|
+
const page = result.slice(0, limit);
|
|
58
|
+
const hasMore = result.length > limit;
|
|
59
|
+
return {
|
|
60
|
+
items: page.map(clone),
|
|
61
|
+
nextCursor: hasMore ? encodeCursor(page[page.length - 1]) : null,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
async listAfter(query) {
|
|
65
|
+
let cursor = null;
|
|
66
|
+
if (query.cursor !== null) {
|
|
67
|
+
cursor = decodeCursor(query.cursor);
|
|
68
|
+
if (!cursor)
|
|
69
|
+
throw new Error(`invalid cursor: ${query.cursor}`);
|
|
70
|
+
}
|
|
71
|
+
let result = [...rows.values()].filter((message) => message.userId === query.userId);
|
|
72
|
+
result.sort(compareAsc);
|
|
73
|
+
if (cursor)
|
|
74
|
+
result = result.filter((message) => isBeyondCursor(message, cursor));
|
|
75
|
+
const page = result.slice(0, query.limit);
|
|
76
|
+
return {
|
|
77
|
+
items: page.map(clone),
|
|
78
|
+
hasMore: result.length > query.limit,
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
async unreadCount(userId) {
|
|
82
|
+
let count = 0;
|
|
83
|
+
for (const message of rows.values()) {
|
|
84
|
+
if (message.userId === userId && message.readAt === null)
|
|
85
|
+
count += 1;
|
|
86
|
+
}
|
|
87
|
+
return count;
|
|
88
|
+
},
|
|
89
|
+
async markRead(userId, ids, now) {
|
|
90
|
+
const wanted = new Set(ids);
|
|
91
|
+
let updated = 0;
|
|
92
|
+
for (const message of rows.values()) {
|
|
93
|
+
if (message.userId !== userId)
|
|
94
|
+
continue;
|
|
95
|
+
if (!wanted.has(message.id) || message.readAt !== null)
|
|
96
|
+
continue;
|
|
97
|
+
message.readAt = now;
|
|
98
|
+
updated += 1;
|
|
99
|
+
}
|
|
100
|
+
return updated;
|
|
101
|
+
},
|
|
102
|
+
async markAllRead(userId, now) {
|
|
103
|
+
let updated = 0;
|
|
104
|
+
for (const message of rows.values()) {
|
|
105
|
+
if (message.userId !== userId || message.readAt !== null)
|
|
106
|
+
continue;
|
|
107
|
+
message.readAt = now;
|
|
108
|
+
updated += 1;
|
|
109
|
+
}
|
|
110
|
+
return updated;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
return store;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type postgresJs from 'postgres';
|
|
2
|
+
import type { MessageStore } from '../types.ts';
|
|
3
|
+
/** postgres.js 的 options(其泛型无默认值,同 `./postgres`) */
|
|
4
|
+
type PostgresOptions = NonNullable<Parameters<typeof postgresJs>[1]>;
|
|
5
|
+
export interface PostgresMessageStore extends MessageStore {
|
|
6
|
+
migrate(): Promise<void>;
|
|
7
|
+
close(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* postgres 驱动(复用 `postgres.js`,与 `./postgres` 的 `ctx.sql` 同一客户端)。
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* postgresMessageStore('postgres://…')
|
|
14
|
+
* postgresMessageStore(ctx.sql) // 注入已有客户端(不负责 close)
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* 幂等键靠 `(user_id, idempotency_key)` 唯一约束 + `ON CONFLICT DO NOTHING` 原子去重;
|
|
18
|
+
* 表结构与 sqlite 对齐,`migrate()` 幂等(`message_migrations`)。
|
|
19
|
+
*/
|
|
20
|
+
export declare function postgresMessageStore(urlOrSql?: string | postgresJs.Sql, options?: PostgresOptions): PostgresMessageStore;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/stores/postgres.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,UAAU,MAAM,UAAU,CAAA;AAEtC,OAAO,KAAK,EAOV,YAAY,EAEb,MAAM,aAAa,CAAA;AAEpB,oDAAoD;AACpD,KAAK,eAAe,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AA8CpE,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC,GAAG,EAClC,OAAO,CAAC,EAAE,eAAe,GACxB,oBAAoB,CA2JtB"}
|