message-nexus 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 ADDED
@@ -0,0 +1,573 @@
1
+ # message-nexus
2
+
3
+ 一个统一、类型安全、支持多种传输协议的跨上下文消息通信库。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install message-nexus
9
+ # or
10
+ pnpm add message-nexus
11
+ ```
12
+
13
+ ## 特性
14
+
15
+ - **统一接口**: 支持 Mitt(进程内)、PostMessage(iframe/window 间)、BroadcastChannel(跨标签页)、WebSocket(网络通信)
16
+ - **类型安全**: 完整的 TypeScript 支持,泛型类型推断
17
+ - **请求-响应模式**: Promise 风格的异步通信,内置超时保护
18
+ - **自动重连**: WebSocket 自动重连机制,支持指数退避
19
+ - **消息队列**: 离线消息缓存,连接恢复后自动发送
20
+ - **重试机制**: 请求失败自动重试,可配置重试次数和延迟
21
+ - **消息验证**: 运行时消息格式验证,防止非法消息
22
+ - **监控指标**: 内置消息统计和性能监控
23
+ - **结构化日志**: 支持自定义日志处理器,便于调试和生产监控
24
+ - **资源管理**: 所有 Driver 支持 `destroy()` 方法,正确清理资源
25
+
26
+ ## 快速开始
27
+
28
+ ### 1. 进程内通信(Mitt)
29
+
30
+ ```typescript
31
+ import mitt from 'mitt'
32
+ import { MittDriver, MessageBridge } from 'message-nexus'
33
+
34
+ const emitter = mitt()
35
+ const driver = new MittDriver(emitter)
36
+ const bridge = new MessageBridge(driver)
37
+
38
+ // 发送请求
39
+ const response = await bridge.request('GET_DATA', { id: 123 })
40
+ console.log(response)
41
+
42
+ // 监听命令
43
+ const unsubscribe = bridge.onCommand((data) => {
44
+ if (data.type === 'GET_DATA') {
45
+ bridge.reply(data.id, { name: 'test', value: 42 })
46
+ }
47
+ })
48
+ ```
49
+
50
+ ### 2. iframe/Window 通信(PostMessage)
51
+
52
+ ```typescript
53
+ import { PostMessageDriver, MessageBridge } from 'message-nexus'
54
+
55
+ // 发送方
56
+ const driver = new PostMessageDriver(window.parent, 'https://example.com')
57
+ const bridge = new MessageBridge(driver)
58
+
59
+ const response = await bridge.request('PING')
60
+ console.log('Pong:', response)
61
+
62
+ // 接收方
63
+ const iframeDriver = new PostMessageDriver(iframe.contentWindow, 'https://example.com')
64
+ const iframeBridge = new MessageBridge(iframeDriver)
65
+
66
+ iframeBridge.onCommand((data) => {
67
+ if (data.type === 'PING') {
68
+ bridge.reply(data.id, { time: Date.now() })
69
+ }
70
+ })
71
+ ```
72
+
73
+ ### 3. 跨标签页通信(BroadcastChannel)
74
+
75
+ ```typescript
76
+ import { BroadcastDriver, MessageBridge } from 'message-nexus'
77
+
78
+ // 创建 BroadcastDriver,指定频道名称
79
+ const driver = new BroadcastDriver({ channel: 'my-app-channel' })
80
+ const bridge = new MessageBridge(driver)
81
+
82
+ // 监听命令
83
+ bridge.onCommand((data) => {
84
+ console.log('Received:', data.type, data.payload)
85
+ bridge.reply(data.id, { result: 'success' })
86
+ })
87
+
88
+ // 发送请求(会在所有同频道的标签页中广播)
89
+ const response = await bridge.request({
90
+ type: 'SYNC_STATE',
91
+ payload: { state: '...' },
92
+ })
93
+
94
+ // 清理资源
95
+ bridge.destroy()
96
+ ```
97
+
98
+ ### 4. WebSocket 通信
99
+
100
+ ```typescript
101
+ import { WebSocketDriver, MessageBridge } from 'message-nexus'
102
+
103
+ // 自动重连配置
104
+ const driver = new WebSocketDriver({
105
+ url: 'wss://api.example.com/ws',
106
+ reconnect: {
107
+ maxRetries: 5, // 最大重试次数
108
+ retryInterval: 3000, // 重试间隔(毫秒)
109
+ },
110
+ })
111
+
112
+ const bridge = new MessageBridge(driver)
113
+
114
+ // 发送请求
115
+ const response = await bridge.request({
116
+ type: 'GET_USER',
117
+ payload: { userId: 123 },
118
+ timeout: 5000,
119
+ retryCount: 3, // 失败重试 3 次
120
+ retryDelay: 1000, // 重试延迟
121
+ })
122
+ ```
123
+
124
+ ## API 文档
125
+
126
+ ### MessageBridge
127
+
128
+ #### 构造函数
129
+
130
+ ```typescript
131
+ new MessageBridge<RequestPayload, ResponsePayload>(
132
+ driver: BaseDriver,
133
+ options?: MessageBridgeOptions
134
+ )
135
+ ```
136
+
137
+ **Options:**
138
+
139
+ | 参数 | 类型 | 默认值 | 说明 |
140
+ | ---------- | ------ | -------------- | --------------------- |
141
+ | instanceId | string | auto-generated | 实例 ID,用于消息路由 |
142
+ | timeout | number | 10000 | 请求超时时间(毫秒) |
143
+ | logger | Logger | new Logger() | 日志实例 |
144
+
145
+ #### 方法
146
+
147
+ ##### request()
148
+
149
+ 发送请求并等待响应。
150
+
151
+ ```typescript
152
+ bridge.request(typeOrOptions: string | RequestOptions): Promise<ResponsePayload>
153
+ ```
154
+
155
+ **Options:**
156
+
157
+ | 参数 | 类型 | 必填 | 说明 |
158
+ | ---------- | ----------------------- | ---- | -------------------- |
159
+ | type | string | 是 | 消息类型 |
160
+ | payload | unknown | 否 | 请求数据 |
161
+ | to | string | 否 | 目标实例 ID |
162
+ | metadata | Record<string, unknown> | 否 | 元数据 |
163
+ | timeout | number | 否 | 超时时间(覆盖全局) |
164
+ | retryCount | number | 否 | 失败重试次数 |
165
+ | retryDelay | number | 否 | 重试延迟(毫秒) |
166
+
167
+ **示例:**
168
+
169
+ ```typescript
170
+ // 简单请求
171
+ const result = await bridge.request('FETCH_DATA')
172
+
173
+ // 完整配置
174
+ const result = await bridge.request({
175
+ type: 'FETCH_DATA',
176
+ payload: { id: 123 },
177
+ to: 'target-instance',
178
+ timeout: 5000,
179
+ retryCount: 3,
180
+ retryDelay: 1000,
181
+ })
182
+ ```
183
+
184
+ ##### onCommand()
185
+
186
+ 注册消息处理器。
187
+
188
+ ```typescript
189
+ bridge.onCommand(handler: (data: CommandMessage) => void): () => void
190
+ ```
191
+
192
+ **返回值:** 取消监听的函数
193
+
194
+ **示例:**
195
+
196
+ ```typescript
197
+ const unsubscribe = bridge.onCommand((data) => {
198
+ console.log('Received:', data.type, data.payload)
199
+
200
+ if (data.type === 'ECHO') {
201
+ bridge.reply(data.id, { echoed: data.payload })
202
+ }
203
+ })
204
+
205
+ // 取消监听
206
+ unsubscribe()
207
+ ```
208
+
209
+ ##### reply()
210
+
211
+ 回复传入消息。
212
+
213
+ ```typescript
214
+ bridge.reply(messageId: string, payload: unknown, error?: unknown)
215
+ ```
216
+
217
+ **示例:**
218
+
219
+ ```typescript
220
+ bridge.reply('message-id-123', { success: true })
221
+ bridge.reply('message-id-456', null, new Error('Invalid request'))
222
+ ```
223
+
224
+ ##### onError()
225
+
226
+ 注册错误处理器。
227
+
228
+ ```typescript
229
+ bridge.onError(handler: ErrorHandler): () => void
230
+ ```
231
+
232
+ **示例:**
233
+
234
+ ```typescript
235
+ bridge.onError((error, context) => {
236
+ console.error('Bridge error:', error.message, context)
237
+ // 发送到错误追踪服务
238
+ Sentry.captureException(error, { extra: context })
239
+ })
240
+ ```
241
+
242
+ ##### getMetrics()
243
+
244
+ 获取监控指标。
245
+
246
+ ```typescript
247
+ bridge.getMetrics(): Metrics
248
+ ```
249
+
250
+ **返回值:**
251
+
252
+ ```typescript
253
+ {
254
+ messagesSent: number // 发送消息数
255
+ messagesReceived: number // 接收消息数
256
+ messagesFailed: number // 失败消息数
257
+ pendingMessages: number // 待处理消息数
258
+ queuedMessages: number // 队列消息数
259
+ totalLatency: number // 总延迟(毫秒)
260
+ averageLatency: number // 平均延迟(毫秒)
261
+ }
262
+ ```
263
+
264
+ **示例:**
265
+
266
+ ```typescript
267
+ const metrics = bridge.getMetrics()
268
+ console.log(`Avg latency: ${metrics.averageLatency}ms`)
269
+ console.log(
270
+ `Success rate: ${((metrics.messagesReceived / metrics.messagesSent) * 100).toFixed(2)}%`,
271
+ )
272
+ ```
273
+
274
+ ##### onMetrics()
275
+
276
+ 注册指标变更回调。
277
+
278
+ ```typescript
279
+ bridge.onMetrics(callback: MetricsCallback): () => void
280
+ ```
281
+
282
+ **示例:**
283
+
284
+ ```typescript
285
+ const unsubscribe = bridge.onMetrics((metrics) => {
286
+ // 发送到监控系统
287
+ metricsService.report(metrics)
288
+ })
289
+ ```
290
+
291
+ ##### flushQueue()
292
+
293
+ 刷新消息队列,发送所有缓存的消息。
294
+
295
+ ```typescript
296
+ bridge.flushQueue()
297
+ ```
298
+
299
+ ##### destroy()
300
+
301
+ 销毁实例,清理资源。
302
+
303
+ ```typescript
304
+ bridge.destroy()
305
+ ```
306
+
307
+ **注意**: `destroy()` 方法会自动调用驱动的 `destroy()` 方法来清理事件监听器等资源。建议在组件卸载时调用此方法以避免内存泄漏。
308
+
309
+ ### WebSocketDriver
310
+
311
+ #### 构造函数
312
+
313
+ ```typescript
314
+ new WebSocketDriver(options: WebSocketDriverOptions)
315
+ ```
316
+
317
+ **Options:**
318
+
319
+ | 参数 | 类型 | 默认值 | 说明 |
320
+ | --------- | --------------------------- | ------------ | ------------- |
321
+ | url | string | 必填 | WebSocket URL |
322
+ | reconnect | boolean \| ReconnectOptions | true | 是否自动重连 |
323
+ | logger | Logger | new Logger() | 日志实例 |
324
+
325
+ **ReconnectOptions:**
326
+
327
+ | 参数 | 类型 | 默认值 | 说明 |
328
+ | ------------- | ------ | -------- | ---------------- |
329
+ | maxRetries | number | Infinity | 最大重试次数 |
330
+ | retryInterval | number | 5000 | 重试间隔(毫秒) |
331
+
332
+ **示例:**
333
+
334
+ ```typescript
335
+ const driver = new WebSocketDriver({
336
+ url: 'wss://api.example.com/ws',
337
+ reconnect: {
338
+ maxRetries: 10,
339
+ retryInterval: 3000,
340
+ },
341
+ })
342
+ ```
343
+
344
+ #### 方法
345
+
346
+ ##### close()
347
+
348
+ 关闭连接并停止重连。
349
+
350
+ ```typescript
351
+ driver.close()
352
+ ```
353
+
354
+ ### PostMessageDriver
355
+
356
+ #### 构造函数
357
+
358
+ ```typescript
359
+ new PostMessageDriver(targetWindow: Window, targetOrigin: string)
360
+ ```
361
+
362
+ **参数:**
363
+
364
+ | 参数 | 类型 | 必填 | 说明 |
365
+ | ------------ | ------ | ---- | ------------------------------------- |
366
+ | targetWindow | Window | 是 | 目标窗口对象 |
367
+ | targetOrigin | string | 是 | 目标源地址(安全要求,不能使用 '\*') |
368
+
369
+ **示例:**
370
+
371
+ ```typescript
372
+ const driver = new PostMessageDriver(window.parent, 'https://app.example.com')
373
+ ```
374
+
375
+ ### MittDriver
376
+
377
+ #### 构造函数
378
+
379
+ ```typescript
380
+ new MittDriver(emitter: Emitter<Record<string, Message>>)
381
+ ```
382
+
383
+ **示例:**
384
+
385
+ ```typescript
386
+ import { createEmitter, MittDriver } from 'message-nexus'
387
+
388
+ // 使用工厂函数创建独立的 emitter 实例
389
+ const emitter = createEmitter()
390
+ const driver = new MittDriver(emitter)
391
+ ```
392
+
393
+ **注意**: 推荐使用 `createEmitter()` 工厂函数创建独立的 emitter 实例。
394
+
395
+ ### BroadcastDriver
396
+
397
+ #### 构造函数
398
+
399
+ ```typescript
400
+ new BroadcastDriver(options: BroadcastDriverOptions)
401
+ ```
402
+
403
+ **BroadcastDriverOptions:**
404
+
405
+ | 参数 | 类型 | 默认值 | 说明 |
406
+ | ------- | ------ | ------ | ------------ |
407
+ | channel | string | 必填 | 广播频道名称 |
408
+
409
+ **示例:**
410
+
411
+ ```typescript
412
+ import { BroadcastDriver, MessageBridge } from 'message-nexus'
413
+
414
+ const driver = new BroadcastDriver({ channel: 'my-app-channel' })
415
+ const bridge = new MessageBridge(driver)
416
+
417
+ // 监听来自其他标签页的消息
418
+ bridge.onCommand((data) => {
419
+ console.log('Received from another tab:', data)
420
+ bridge.reply(data.id, { received: true })
421
+ })
422
+
423
+ // 清理资源
424
+ bridge.destroy()
425
+ ```
426
+
427
+ **特性:**
428
+
429
+ - 同一源下的多个标签页可以通过相同频道名进行通信
430
+ - 自动添加协议标识符,过滤非 MessageBridge 消息
431
+ - 支持动态切换频道
432
+
433
+ ## Logger 日志
434
+
435
+ ### 基本使用
436
+
437
+ ```typescript
438
+ import { Logger, createConsoleHandler, LogLevel } from 'message-nexus/utils/logger'
439
+
440
+ const logger = new Logger('MyApp', LogLevel.DEBUG)
441
+ logger.addHandler(createConsoleHandler())
442
+
443
+ logger.debug('Debug message', { data: 123 })
444
+ logger.info('Info message')
445
+ logger.warn('Warning message')
446
+ logger.error('Error message', { error: new Error('test') })
447
+ ```
448
+
449
+ ### 自定义日志处理器
450
+
451
+ ```typescript
452
+ const apiHandler = (entry) => {
453
+ fetch('/api/logs', {
454
+ method: 'POST',
455
+ body: JSON.stringify(entry),
456
+ })
457
+ }
458
+
459
+ logger.addHandler(apiHandler)
460
+ ```
461
+
462
+ ### 设置日志级别
463
+
464
+ ```typescript
465
+ logger.setMinLevel(LogLevel.WARN) // 只输出 WARN 和 ERROR
466
+ ```
467
+
468
+ ### 在 Bridge 中使用
469
+
470
+ ```typescript
471
+ import { Logger } from 'message-nexus/utils/logger'
472
+
473
+ const logger = new Logger('MyBridge')
474
+ const bridge = new MessageBridge(driver, { logger })
475
+ ```
476
+
477
+ ## 设计亮点
478
+
479
+ ### 1. 类型安全
480
+
481
+ MessageBridge 使用 TypeScript 泛型提供完整的类型推断:
482
+
483
+ ```typescript
484
+ interface UserRequest {
485
+ userId: number
486
+ }
487
+
488
+ interface UserResponse {
489
+ id: number
490
+ name: string
491
+ }
492
+
493
+ const bridge = new MessageBridge<UserRequest, UserResponse>(driver)
494
+
495
+ // 完整的类型推断
496
+ const response = await bridge.request({
497
+ type: 'GET_USER',
498
+ payload: { userId: 123 }, // 类型: UserRequest
499
+ })
500
+
501
+ // response 类型: UserResponse
502
+ console.log(response.name)
503
+ ```
504
+
505
+ ### 2. 内存安全
506
+
507
+ - **自动清理**: 定期清理过期的消息记录
508
+ - **手动清理**: `reply()` 后立即删除记录
509
+ - **资源释放**: `destroy()` 方法清理所有定时器和事件监听器
510
+ - **队列限制**: 消息队列有最大大小限制,防止无限增长
511
+ - **Driver 生命周期**: 每个 Driver 实现 `destroy()` 方法,正确释放资源
512
+ - **Emitter 隔离**: 推荐使用 `createEmitter()` 创建独立实例,避免共享单例导致的内存泄漏
513
+
514
+ ### 3. 错误恢复
515
+
516
+ - **自动重连**: WebSocket 断线自动重连,指数退避策略
517
+ - **请求重试**: 失败请求自动重试,可配置次数和延迟
518
+ - **消息队列**: 离线消息缓存,连接恢复后自动发送
519
+ - **错误回调**: 统一的错误处理机制
520
+
521
+ ### 4. 安全加固
522
+
523
+ - **PostMessage**: 禁止使用 `'*'` 作为 targetOrigin,必须明确指定源地址
524
+ - **BroadcastChannel**: 使用协议标识符 `__messageBridge` 区分 MessageBridge 消息和用户自定义消息
525
+ - **消息验证**: 运行时验证消息格式,防止非法消息导致崩溃
526
+ - **来源过滤**: 自动过滤非目标消息
527
+
528
+ ### 5. 可观测性
529
+
530
+ 内置监控指标,便于生产环境监控:
531
+
532
+ ```typescript
533
+ const metrics = bridge.getMetrics()
534
+
535
+ console.log(`Messages: ${metrics.messagesSent} sent, ${metrics.messagesReceived} received`)
536
+ console.log(
537
+ `Success rate: ${((metrics.messagesReceived / metrics.messagesSent) * 100).toFixed(2)}%`,
538
+ )
539
+ console.log(`Avg latency: ${metrics.averageLatency}ms`)
540
+ console.log(`Pending: ${metrics.pendingMessages}, Queued: ${metrics.queuedMessages}`)
541
+ ```
542
+
543
+ ### 6. 结构化日志
544
+
545
+ 统一的日志接口,支持多种输出方式:
546
+
547
+ ```typescript
548
+ // 控制台输出
549
+ logger.addHandler(createConsoleHandler())
550
+
551
+ // 发送到 API
552
+ logger.addHandler((entry) => {
553
+ fetch('/api/logs', { body: JSON.stringify(entry) })
554
+ })
555
+
556
+ // 发送到 ELK
557
+ logger.addHandler((entry) => {
558
+ elk.send(entry)
559
+ })
560
+ ```
561
+
562
+ ## 测试
563
+
564
+ 运行单元测试:
565
+
566
+ ```bash
567
+ cd packages/message-nexus
568
+ pnpm test:run
569
+ ```
570
+
571
+ ## 许可证
572
+
573
+ MIT