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 +573 -0
- package/dist/index.cjs +572 -0
- package/dist/index.d.cts +175 -0
- package/dist/index.d.ts +175 -0
- package/dist/index.js +530 -0
- package/package.json +54 -0
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
|