@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.
Files changed (63) hide show
  1. package/README.md +259 -2
  2. package/dist/bus.d.ts +7 -0
  3. package/dist/bus.d.ts.map +1 -0
  4. package/dist/bus.js +30 -0
  5. package/dist/bus.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +82 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/cursor.d.ts +20 -0
  11. package/dist/cursor.d.ts.map +1 -0
  12. package/dist/cursor.js +49 -0
  13. package/dist/cursor.js.map +1 -0
  14. package/dist/hub.d.ts +15 -0
  15. package/dist/hub.d.ts.map +1 -0
  16. package/dist/hub.js +51 -0
  17. package/dist/hub.js.map +1 -0
  18. package/dist/index.d.ts +6 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +5 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/messages.d.ts +10 -0
  23. package/dist/messages.d.ts.map +1 -0
  24. package/dist/messages.js +272 -0
  25. package/dist/messages.js.map +1 -0
  26. package/dist/postgres.d.ts +3 -0
  27. package/dist/postgres.d.ts.map +1 -0
  28. package/dist/postgres.js +2 -0
  29. package/dist/postgres.js.map +1 -0
  30. package/dist/redis.d.ts +24 -0
  31. package/dist/redis.d.ts.map +1 -0
  32. package/dist/redis.js +126 -0
  33. package/dist/redis.js.map +1 -0
  34. package/dist/sqlite.d.ts +3 -0
  35. package/dist/sqlite.d.ts.map +1 -0
  36. package/dist/sqlite.js +2 -0
  37. package/dist/sqlite.js.map +1 -0
  38. package/dist/stores/memory.d.ts +6 -0
  39. package/dist/stores/memory.d.ts.map +1 -0
  40. package/dist/stores/memory.js +115 -0
  41. package/dist/stores/memory.js.map +1 -0
  42. package/dist/stores/postgres.d.ts +22 -0
  43. package/dist/stores/postgres.d.ts.map +1 -0
  44. package/dist/stores/postgres.js +192 -0
  45. package/dist/stores/postgres.js.map +1 -0
  46. package/dist/stores/sqlite.d.ts +13 -0
  47. package/dist/stores/sqlite.d.ts.map +1 -0
  48. package/dist/stores/sqlite.js +180 -0
  49. package/dist/stores/sqlite.js.map +1 -0
  50. package/dist/testing.d.ts +24 -0
  51. package/dist/testing.d.ts.map +1 -0
  52. package/dist/testing.js +248 -0
  53. package/dist/testing.js.map +1 -0
  54. package/dist/types.d.ts +171 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +2 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +64 -4
  59. package/templates/react-router-messages/app/messages.ts +13 -0
  60. package/templates/react-router-messages/app/routes/home.tsx +51 -0
  61. package/templates/react-router-messages/app/routes/notifications.tsx +91 -0
  62. package/templates/react-router-messages/app/routes.ts +10 -0
  63. package/templates/react-router-messages/app/server.ts +41 -0
@@ -0,0 +1,192 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { decodeCursor, encodeCursor } from "../cursor.js";
3
+ const SCHEMA = [
4
+ `
5
+ CREATE TABLE IF NOT EXISTS messages (
6
+ id TEXT PRIMARY KEY,
7
+ user_id TEXT NOT NULL,
8
+ kind TEXT NOT NULL,
9
+ title TEXT NOT NULL,
10
+ body TEXT,
11
+ data TEXT,
12
+ read_at TEXT,
13
+ idempotency_key TEXT,
14
+ created_at TEXT NOT NULL,
15
+ UNIQUE (user_id, idempotency_key)
16
+ )`,
17
+ 'CREATE INDEX IF NOT EXISTS messages_inbox_idx ON messages(user_id, created_at DESC, id DESC)',
18
+ 'CREATE INDEX IF NOT EXISTS messages_unread_idx ON messages(user_id, read_at)',
19
+ ];
20
+ function toMessage(row) {
21
+ return {
22
+ id: row.id,
23
+ userId: row.user_id,
24
+ kind: row.kind,
25
+ title: row.title,
26
+ body: row.body,
27
+ data: row.data === null ? null : JSON.parse(row.data),
28
+ readAt: row.read_at === null ? null : new Date(row.read_at),
29
+ idempotencyKey: row.idempotency_key,
30
+ createdAt: new Date(row.created_at),
31
+ };
32
+ }
33
+ /**
34
+ * postgres 驱动(复用 `postgres.js`,与 `./postgres` 的 `ctx.sql` 同一客户端)。
35
+ *
36
+ * ```ts
37
+ * postgresMessageStore('postgres://…')
38
+ * postgresMessageStore(ctx.sql) // 注入已有客户端(不负责 close)
39
+ * ```
40
+ *
41
+ * 幂等键靠 `(user_id, idempotency_key)` 唯一约束 + `ON CONFLICT DO NOTHING` 原子去重;
42
+ * 表结构与 sqlite 对齐,`migrate()` 幂等(`message_migrations`)。
43
+ */
44
+ export function postgresMessageStore(urlOrSql, options) {
45
+ const injected = typeof urlOrSql === 'function' ? urlOrSql : null;
46
+ let client = injected
47
+ ? Promise.resolve(injected)
48
+ : null;
49
+ const sql = () => {
50
+ client ??= (async () => {
51
+ const { default: createPostgres } = (await import('postgres'));
52
+ if (typeof urlOrSql === 'string')
53
+ return createPostgres(urlOrSql, options);
54
+ const url = process.env.DATABASE_URL;
55
+ if (url)
56
+ return createPostgres(url, options);
57
+ return createPostgres(options);
58
+ })();
59
+ return client;
60
+ };
61
+ const applyMigrations = async (db) => {
62
+ // 并发实例串行化:DDL 与版本写入同一事务。
63
+ // PG 的 `CREATE TABLE IF NOT EXISTS` 并非 race-free(pg_type 唯一冲突),
64
+ // 多进程同时启动迁移会互相炸;advisory xact lock 让后来者排队。
65
+ await db.begin(async (tx) => {
66
+ await tx `SELECT pg_advisory_xact_lock(hashtext('weifuwu:messages:migrate'))`;
67
+ await tx `
68
+ CREATE TABLE IF NOT EXISTS message_migrations (
69
+ version INTEGER PRIMARY KEY,
70
+ applied_at TEXT NOT NULL
71
+ )`;
72
+ const applied = await tx `SELECT version FROM message_migrations WHERE version = 1`;
73
+ if (applied.length > 0)
74
+ return;
75
+ for (const statement of SCHEMA)
76
+ await tx.unsafe(statement);
77
+ await tx `INSERT INTO message_migrations (version, applied_at) VALUES (1, ${new Date().toISOString()})`;
78
+ });
79
+ };
80
+ const store = {
81
+ async migrate() {
82
+ await applyMigrations(await sql());
83
+ },
84
+ async close() {
85
+ if (!client || injected)
86
+ return;
87
+ const db = await client;
88
+ client = null;
89
+ await db.end({ timeout: 5 });
90
+ },
91
+ async insert(input) {
92
+ const db = await sql();
93
+ const key = input.idempotencyKey;
94
+ const rows = (await db `
95
+ INSERT INTO messages (id, user_id, kind, title, body, data, read_at,
96
+ idempotency_key, created_at)
97
+ VALUES (${randomUUID()}, ${input.userId}, ${input.kind}, ${input.title},
98
+ ${input.body}, ${input.data === null ? null : JSON.stringify(input.data)},
99
+ NULL, ${key}, ${input.createdAt.toISOString()})
100
+ ON CONFLICT (user_id, idempotency_key) DO NOTHING
101
+ RETURNING *`);
102
+ if (rows[0])
103
+ return { message: toMessage(rows[0]), created: true };
104
+ // 幂等键命中:返回已有消息(不覆盖)
105
+ const existing = (await db `
106
+ SELECT * FROM messages
107
+ WHERE user_id = ${input.userId} AND idempotency_key = ${key}
108
+ LIMIT 1`);
109
+ if (existing[0])
110
+ return { message: toMessage(existing[0]), created: false };
111
+ throw new Error('insert 冲突但未找到已有消息(idempotency_key 非空?)');
112
+ },
113
+ async get(id, userId) {
114
+ const db = await sql();
115
+ const rows = (await db `
116
+ SELECT * FROM messages WHERE id = ${id} AND user_id = ${userId}`);
117
+ return rows[0] ? toMessage(rows[0]) : null;
118
+ },
119
+ async list(query) {
120
+ const db = await sql();
121
+ const unreadFilter = query.unreadOnly ? db `AND read_at IS NULL` : db ``;
122
+ const kindFilter = query.kind ? db `AND kind = ${query.kind}` : db ``;
123
+ let cursorFilter = db ``;
124
+ if (query.cursor) {
125
+ const cursor = decodeCursor(query.cursor);
126
+ if (!cursor)
127
+ throw new Error(`invalid cursor: ${query.cursor}`);
128
+ const iso = new Date(cursor.createdAtMs).toISOString();
129
+ cursorFilter = db `AND (created_at < ${iso} OR (created_at = ${iso} AND id < ${cursor.id}))`;
130
+ }
131
+ const limit = query.limit ?? 20;
132
+ const rows = (await db `
133
+ SELECT * FROM messages
134
+ WHERE user_id = ${query.userId} ${unreadFilter} ${kindFilter} ${cursorFilter}
135
+ ORDER BY created_at DESC, id DESC
136
+ LIMIT ${limit + 1}`);
137
+ const hasMore = rows.length > limit;
138
+ const page = rows.slice(0, limit).map(toMessage);
139
+ return {
140
+ items: page,
141
+ nextCursor: hasMore ? encodeCursor(page[page.length - 1]) : null,
142
+ };
143
+ },
144
+ async listAfter(query) {
145
+ const db = await sql();
146
+ let cursorFilter = db ``;
147
+ if (query.cursor !== null) {
148
+ const cursor = decodeCursor(query.cursor);
149
+ if (!cursor)
150
+ throw new Error(`invalid cursor: ${query.cursor}`);
151
+ const iso = new Date(cursor.createdAtMs).toISOString();
152
+ cursorFilter = db `AND (created_at > ${iso} OR (created_at = ${iso} AND id > ${cursor.id}))`;
153
+ }
154
+ const rows = (await db `
155
+ SELECT * FROM messages
156
+ WHERE user_id = ${query.userId} ${cursorFilter}
157
+ ORDER BY created_at ASC, id ASC
158
+ LIMIT ${query.limit + 1}`);
159
+ return {
160
+ items: rows.slice(0, query.limit).map(toMessage),
161
+ hasMore: rows.length > query.limit,
162
+ };
163
+ },
164
+ async unreadCount(userId) {
165
+ const db = await sql();
166
+ const rows = (await db `
167
+ SELECT COUNT(*)::int AS count FROM messages
168
+ WHERE user_id = ${userId} AND read_at IS NULL`);
169
+ return Number(rows[0].count);
170
+ },
171
+ async markRead(userId, ids, now) {
172
+ if (ids.length === 0)
173
+ return 0;
174
+ const db = await sql();
175
+ const rows = (await db `
176
+ UPDATE messages SET read_at = ${now.toISOString()}
177
+ WHERE user_id = ${userId} AND read_at IS NULL AND id = ANY(${ids})
178
+ RETURNING id`);
179
+ return rows.length;
180
+ },
181
+ async markAllRead(userId, now) {
182
+ const db = await sql();
183
+ const rows = (await db `
184
+ UPDATE messages SET read_at = ${now.toISOString()}
185
+ WHERE user_id = ${userId} AND read_at IS NULL
186
+ RETURNING id`);
187
+ return rows.length;
188
+ },
189
+ };
190
+ return store;
191
+ }
192
+ //# sourceMappingURL=postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/stores/postgres.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import type { MessageStore } from '../types.ts';
3
+ export interface SqliteMessageStore extends MessageStore {
4
+ readonly db: DatabaseSync;
5
+ migrate(): Promise<void>;
6
+ close(): void;
7
+ }
8
+ /**
9
+ * sqlite 驱动(`node:sqlite`,零依赖)。构造自动迁移,迁移幂等;
10
+ * `:memory:` 用于测试;**单进程**语义(多进程不承诺,文档写明)。
11
+ */
12
+ export declare function sqliteMessageStore(path?: string): SqliteMessageStore;
13
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/stores/sqlite.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,KAAK,EAOV,YAAY,EAEb,MAAM,aAAa,CAAA;AAkEpB,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAA;IACzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,SAAqB,GAAG,kBAAkB,CAgJhF"}
@@ -0,0 +1,180 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdirSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+ import { DatabaseSync } from 'node:sqlite';
5
+ import { decodeCursor, encodeCursor } from "../cursor.js";
6
+ const SCHEMA = `
7
+ CREATE TABLE IF NOT EXISTS messages (
8
+ id TEXT PRIMARY KEY,
9
+ user_id TEXT NOT NULL,
10
+ kind TEXT NOT NULL,
11
+ title TEXT NOT NULL,
12
+ body TEXT,
13
+ data TEXT,
14
+ read_at TEXT,
15
+ idempotency_key TEXT,
16
+ created_at TEXT NOT NULL,
17
+ UNIQUE (user_id, idempotency_key)
18
+ );
19
+ CREATE INDEX IF NOT EXISTS messages_inbox_idx ON messages(user_id, created_at DESC, id DESC);
20
+ CREATE INDEX IF NOT EXISTS messages_unread_idx ON messages(user_id, read_at);
21
+ `;
22
+ function toMessage(row) {
23
+ return {
24
+ id: row.id,
25
+ userId: row.user_id,
26
+ kind: row.kind,
27
+ title: row.title,
28
+ body: row.body,
29
+ data: row.data === null ? null : JSON.parse(row.data),
30
+ readAt: row.read_at === null ? null : new Date(row.read_at),
31
+ idempotencyKey: row.idempotency_key,
32
+ createdAt: new Date(row.created_at),
33
+ };
34
+ }
35
+ function applyMigrations(db) {
36
+ db.exec('CREATE TABLE IF NOT EXISTS message_migrations (version INTEGER PRIMARY KEY, applied_at TEXT NOT NULL)');
37
+ const applied = db
38
+ .prepare('SELECT version FROM message_migrations WHERE version = ?')
39
+ .get(1);
40
+ if (applied)
41
+ return;
42
+ db.exec('BEGIN');
43
+ try {
44
+ db.exec(SCHEMA);
45
+ db.prepare('INSERT INTO message_migrations (version, applied_at) VALUES (?, ?)').run(1, new Date().toISOString());
46
+ db.exec('COMMIT');
47
+ }
48
+ catch (error) {
49
+ db.exec('ROLLBACK');
50
+ throw error;
51
+ }
52
+ }
53
+ /**
54
+ * sqlite 驱动(`node:sqlite`,零依赖)。构造自动迁移,迁移幂等;
55
+ * `:memory:` 用于测试;**单进程**语义(多进程不承诺,文档写明)。
56
+ */
57
+ export function sqliteMessageStore(path = 'data/messages.db') {
58
+ if (path !== ':memory:')
59
+ mkdirSync(dirname(path), { recursive: true });
60
+ const db = new DatabaseSync(path);
61
+ applyMigrations(db);
62
+ const store = {
63
+ db,
64
+ async migrate() {
65
+ applyMigrations(db);
66
+ },
67
+ close() {
68
+ db.close();
69
+ },
70
+ async insert(input) {
71
+ const key = input.idempotencyKey;
72
+ if (key) {
73
+ const existing = db
74
+ .prepare('SELECT * FROM messages WHERE user_id = ? AND idempotency_key = ?')
75
+ .get(input.userId, key);
76
+ if (existing)
77
+ return { message: toMessage(existing), created: false };
78
+ }
79
+ const id = randomUUID();
80
+ try {
81
+ db.prepare(`INSERT INTO messages (id, user_id, kind, title, body, data, read_at,
82
+ idempotency_key, created_at)
83
+ VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?)`).run(id, input.userId, input.kind, input.title, input.body, input.data === null ? null : JSON.stringify(input.data), key, input.createdAt.toISOString());
84
+ }
85
+ catch (error) {
86
+ if (key && /UNIQUE/i.test(String(error))) {
87
+ const existing = db
88
+ .prepare('SELECT * FROM messages WHERE user_id = ? AND idempotency_key = ?')
89
+ .get(input.userId, key);
90
+ if (existing)
91
+ return { message: toMessage(existing), created: false };
92
+ }
93
+ throw error;
94
+ }
95
+ const row = db
96
+ .prepare('SELECT * FROM messages WHERE id = ?')
97
+ .get(id);
98
+ return { message: toMessage(row), created: true };
99
+ },
100
+ async get(id, userId) {
101
+ const row = db
102
+ .prepare('SELECT * FROM messages WHERE id = ? AND user_id = ?')
103
+ .get(id, userId);
104
+ return row ? toMessage(row) : null;
105
+ },
106
+ async list(query) {
107
+ const where = ['user_id = ?'];
108
+ const params = [query.userId];
109
+ if (query.unreadOnly)
110
+ where.push('read_at IS NULL');
111
+ if (query.kind) {
112
+ where.push('kind = ?');
113
+ params.push(query.kind);
114
+ }
115
+ if (query.cursor) {
116
+ const cursor = decodeCursor(query.cursor);
117
+ if (!cursor)
118
+ throw new Error(`invalid cursor: ${query.cursor}`);
119
+ const iso = new Date(cursor.createdAtMs).toISOString();
120
+ where.push('(created_at < ? OR (created_at = ? AND id < ?))');
121
+ params.push(iso, iso, cursor.id);
122
+ }
123
+ const limit = query.limit ?? 20;
124
+ const rows = db
125
+ .prepare(`SELECT * FROM messages WHERE ${where.join(' AND ')}
126
+ ORDER BY created_at DESC, id DESC LIMIT ?`)
127
+ .all(...params, limit + 1);
128
+ const hasMore = rows.length > limit;
129
+ const page = rows.slice(0, limit).map(toMessage);
130
+ return {
131
+ items: page,
132
+ nextCursor: hasMore ? encodeCursor(page[page.length - 1]) : null,
133
+ };
134
+ },
135
+ async listAfter(query) {
136
+ const where = ['user_id = ?'];
137
+ const params = [query.userId];
138
+ if (query.cursor !== null) {
139
+ const cursor = decodeCursor(query.cursor);
140
+ if (!cursor)
141
+ throw new Error(`invalid cursor: ${query.cursor}`);
142
+ const iso = new Date(cursor.createdAtMs).toISOString();
143
+ where.push('(created_at > ? OR (created_at = ? AND id > ?))');
144
+ params.push(iso, iso, cursor.id);
145
+ }
146
+ const rows = db
147
+ .prepare(`SELECT * FROM messages WHERE ${where.join(' AND ')}
148
+ ORDER BY created_at ASC, id ASC LIMIT ?`)
149
+ .all(...params, query.limit + 1);
150
+ return {
151
+ items: rows.slice(0, query.limit).map(toMessage),
152
+ hasMore: rows.length > query.limit,
153
+ };
154
+ },
155
+ async unreadCount(userId) {
156
+ const row = db
157
+ .prepare('SELECT COUNT(*) AS count FROM messages WHERE user_id = ? AND read_at IS NULL')
158
+ .get(userId);
159
+ return Number(row.count);
160
+ },
161
+ async markRead(userId, ids, now) {
162
+ if (ids.length === 0)
163
+ return 0;
164
+ const placeholders = ids.map(() => '?').join(', ');
165
+ const result = db
166
+ .prepare(`UPDATE messages SET read_at = ?
167
+ WHERE user_id = ? AND read_at IS NULL AND id IN (${placeholders})`)
168
+ .run(now.toISOString(), userId, ...ids);
169
+ return Number(result.changes);
170
+ },
171
+ async markAllRead(userId, now) {
172
+ const result = db
173
+ .prepare('UPDATE messages SET read_at = ? WHERE user_id = ? AND read_at IS NULL')
174
+ .run(now.toISOString(), userId);
175
+ return Number(result.changes);
176
+ },
177
+ };
178
+ return store;
179
+ }
180
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/stores/sqlite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { MessageBus, MessageStore } from './types.ts';
2
+ /**
3
+ * 消息存储契约套件:memory / sqlite / postgres / redis 与自定义驱动共用。
4
+ *
5
+ * ```ts
6
+ * import { messagesStoreContract } from '@weifuwujs/messages/testing'
7
+ * messagesStoreContract('my-store', () => myMessageStore())
8
+ * ```
9
+ */
10
+ export declare function messagesStoreContract(name: string, create: () => MessageStore | Promise<MessageStore>, options?: {
11
+ skip?: boolean | string;
12
+ }): void;
13
+ /**
14
+ * 消息总线契约套件:local / redis 与自定义总线共用。
15
+ *
16
+ * ```ts
17
+ * import { messageBusContract } from '@weifuwujs/messages/testing'
18
+ * messageBusContract('my-bus', () => myMessageBus())
19
+ * ```
20
+ */
21
+ export declare function messageBusContract(name: string, create: () => MessageBus | Promise<MessageBus>, options?: {
22
+ skip?: boolean | string;
23
+ }): void;
24
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAiB,MAAM,YAAY,CAAA;AAkBzE;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,EAClD,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CAAO,GACxC,IAAI,CAwON;AAaD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,EAC9C,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CAAO,GACxC,IAAI,CAmDN"}