message-nexus 1.0.0 → 1.1.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 +311 -260
- package/dist/index.cjs +259 -90
- package/dist/index.d.cts +78 -30
- package/dist/index.d.ts +78 -30
- package/dist/index.js +259 -90
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# message-nexus
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A unified, type-safe cross-context message communication library supporting multiple transport protocols.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install message-nexus
|
|
@@ -10,170 +10,198 @@ npm install message-nexus
|
|
|
10
10
|
pnpm add message-nexus
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Features
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
15
|
+
- **Unified Interface**: Supports Mitt (in-process), PostMessage (iframe/window), BroadcastChannel (cross-tab), and WebSocket (network communication)
|
|
16
|
+
- **JSON-RPC 2.0 Compliance**: Strict adherence to the JSON-RPC 2.0 specification for standardized communication
|
|
17
|
+
- **Envelope Pattern**: Extensible message envelope containing routing information (from, to) and metadata
|
|
18
|
+
- **Type Safety**: Full TypeScript support with generic type inference
|
|
19
|
+
- **Request-Response Pattern**: Promise-style asynchronous communication with built-in timeout protection
|
|
20
|
+
- **Auto Reconnect**: WebSocket automatic reconnection mechanism with exponential backoff support
|
|
21
|
+
- **Message Queue**: Offline message caching, automatically sent after connection recovery
|
|
22
|
+
- **Retry Mechanism**: Automatic retry on request failure, configurable retry counts and delays
|
|
23
|
+
- **Message Validation**: Runtime message format validation to prevent illegal messages
|
|
24
|
+
- **Monitoring Metrics**: Built-in message statistics and performance monitoring
|
|
25
|
+
- **Structured Logging**: Supports custom log handlers for easy debugging and production monitoring
|
|
26
|
+
- **Resource Management**: All drivers support the `destroy()` method to properly clean up resources.
|
|
25
27
|
|
|
26
|
-
##
|
|
28
|
+
## Quick Start
|
|
27
29
|
|
|
28
|
-
### 1.
|
|
30
|
+
### 1. In-Process Communication (Mitt)
|
|
29
31
|
|
|
30
32
|
```typescript
|
|
31
|
-
import
|
|
32
|
-
|
|
33
|
+
import MessageNexus, { MittDriver, createEmitter } from 'message-nexus'
|
|
34
|
+
|
|
35
|
+
// Shared emitter
|
|
36
|
+
const emitter = createEmitter()
|
|
33
37
|
|
|
34
|
-
const emitter = mitt()
|
|
35
38
|
const driver = new MittDriver(emitter)
|
|
36
|
-
const
|
|
39
|
+
const nexus = new MessageNexus(driver)
|
|
37
40
|
|
|
38
|
-
//
|
|
39
|
-
const response = await
|
|
41
|
+
// Send request
|
|
42
|
+
const response = await nexus.invoke('GET_DATA', { id: 123 })
|
|
40
43
|
console.log(response)
|
|
41
44
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// Send one-way notification
|
|
46
|
+
nexus.notify('UPDATE_STATUS', { status: 'active' })
|
|
47
|
+
|
|
48
|
+
// Listen for commands
|
|
49
|
+
const receiverDriver = new MittDriver(emitter)
|
|
50
|
+
const receiverNexus = new MessageNexus(receiverDriver)
|
|
51
|
+
const unsubscribe = receiverNexus.handle('GET_DATA', (params, context) => {
|
|
52
|
+
return { name: 'test', value: 42 }
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// Listen for notifications
|
|
56
|
+
const unsubscribeNotify = receiverNexus.onNotification('UPDATE_STATUS', (params, context) => {
|
|
57
|
+
console.log('Notification received:', params)
|
|
47
58
|
})
|
|
48
59
|
```
|
|
49
60
|
|
|
50
|
-
### 2. iframe/Window
|
|
61
|
+
### 2. iframe/Window Communication (PostMessage)
|
|
51
62
|
|
|
52
63
|
```typescript
|
|
53
|
-
import { PostMessageDriver
|
|
64
|
+
import MessageNexus, { PostMessageDriver } from 'message-nexus'
|
|
54
65
|
|
|
55
|
-
//
|
|
66
|
+
// Sender
|
|
56
67
|
const driver = new PostMessageDriver(window.parent, 'https://example.com')
|
|
57
|
-
const
|
|
68
|
+
const nexus = new MessageNexus(driver)
|
|
58
69
|
|
|
59
|
-
const response = await
|
|
70
|
+
const response = await nexus.invoke('PING')
|
|
60
71
|
console.log('Pong:', response)
|
|
61
72
|
|
|
62
|
-
//
|
|
73
|
+
// Receiver
|
|
63
74
|
const iframeDriver = new PostMessageDriver(iframe.contentWindow, 'https://example.com')
|
|
64
|
-
const
|
|
75
|
+
const iframeNexus = new MessageNexus(iframeDriver)
|
|
65
76
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
bridge.reply(data.id, { time: Date.now() })
|
|
69
|
-
}
|
|
77
|
+
iframeNexus.handle('PING', (params, context) => {
|
|
78
|
+
return { time: Date.now() }
|
|
70
79
|
})
|
|
71
80
|
```
|
|
72
81
|
|
|
73
|
-
### 3.
|
|
82
|
+
### 3. Cross-Tab Communication (BroadcastChannel)
|
|
74
83
|
|
|
75
84
|
```typescript
|
|
76
|
-
import { BroadcastDriver
|
|
85
|
+
import MessageNexus, { BroadcastDriver } from 'message-nexus'
|
|
77
86
|
|
|
78
|
-
//
|
|
87
|
+
// Create BroadcastDriver, specifying the channel name
|
|
79
88
|
const driver = new BroadcastDriver({ channel: 'my-app-channel' })
|
|
80
|
-
const
|
|
89
|
+
const nexus = new MessageNexus(driver)
|
|
81
90
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
console.log('Received:',
|
|
85
|
-
|
|
91
|
+
// Listen for commands
|
|
92
|
+
nexus.handle('SYNC_STATE', (params, context) => {
|
|
93
|
+
console.log('Received:', params)
|
|
94
|
+
return { result: 'success' }
|
|
86
95
|
})
|
|
87
96
|
|
|
88
|
-
//
|
|
89
|
-
const response = await
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
// Send request (will be broadcast to all tabs on the same channel)
|
|
98
|
+
const response = await nexus.invoke({
|
|
99
|
+
method: 'SYNC_STATE',
|
|
100
|
+
params: { state: '...' },
|
|
92
101
|
})
|
|
93
102
|
|
|
94
|
-
//
|
|
95
|
-
|
|
103
|
+
// Receiver
|
|
104
|
+
const receiverDriver = new BroadcastDriver({ channel: 'my-app-channel' })
|
|
105
|
+
const receiverNexus = new MessageNexus(receiverDriver)
|
|
106
|
+
receiverNexus.handle('SYNC_STATE', (params, context) => {
|
|
107
|
+
console.log('Received:', params)
|
|
108
|
+
return { result: 'success' }
|
|
109
|
+
})
|
|
96
110
|
```
|
|
97
111
|
|
|
98
|
-
### 4. WebSocket
|
|
112
|
+
### 4. WebSocket Communication
|
|
99
113
|
|
|
100
114
|
```typescript
|
|
101
|
-
import { WebSocketDriver
|
|
115
|
+
import MessageNexus, { WebSocketDriver } from 'message-nexus'
|
|
102
116
|
|
|
103
|
-
//
|
|
117
|
+
// Automatic reconnection configuration
|
|
104
118
|
const driver = new WebSocketDriver({
|
|
105
119
|
url: 'wss://api.example.com/ws',
|
|
106
120
|
reconnect: {
|
|
107
|
-
maxRetries: 5, //
|
|
108
|
-
retryInterval: 3000, //
|
|
121
|
+
maxRetries: 5, // Maximum retry count
|
|
122
|
+
retryInterval: 3000, // Retry interval (milliseconds)
|
|
109
123
|
},
|
|
110
124
|
})
|
|
111
125
|
|
|
112
|
-
const
|
|
126
|
+
const nexus = new MessageNexus(driver)
|
|
113
127
|
|
|
114
|
-
//
|
|
115
|
-
const response = await
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
// Send request
|
|
129
|
+
const response = await nexus.invoke({
|
|
130
|
+
method: 'GET_USER',
|
|
131
|
+
params: { userId: 123 },
|
|
118
132
|
timeout: 5000,
|
|
119
|
-
retryCount: 3, //
|
|
120
|
-
retryDelay: 1000, //
|
|
133
|
+
retryCount: 3, // Retry 3 times on failure
|
|
134
|
+
retryDelay: 1000, // Retry delay
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// Receiver
|
|
138
|
+
const receiverDriver = new WebSocketDriver({
|
|
139
|
+
url: 'wss://api.example.com/ws',
|
|
140
|
+
reconnect: {
|
|
141
|
+
maxRetries: 5, // Maximum retry count
|
|
142
|
+
retryInterval: 3000, // Retry interval (milliseconds)
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
const receiverNexus = new MessageNexus(receiverDriver)
|
|
146
|
+
receiverNexus.handle('SYNC_STATE', (params, context) => {
|
|
147
|
+
console.log('Received:', params)
|
|
148
|
+
return { result: 'success' }
|
|
121
149
|
})
|
|
122
150
|
```
|
|
123
151
|
|
|
124
|
-
## API
|
|
152
|
+
## API Documentation
|
|
125
153
|
|
|
126
|
-
###
|
|
154
|
+
### MessageNexus
|
|
127
155
|
|
|
128
|
-
####
|
|
156
|
+
#### Constructor
|
|
129
157
|
|
|
130
158
|
```typescript
|
|
131
|
-
new
|
|
159
|
+
new MessageNexus<RequestPayload, ResponsePayload>(
|
|
132
160
|
driver: BaseDriver,
|
|
133
|
-
options?:
|
|
161
|
+
options?: MessageNexusOptions
|
|
134
162
|
)
|
|
135
163
|
```
|
|
136
164
|
|
|
137
165
|
**Options:**
|
|
138
166
|
|
|
139
|
-
|
|
|
140
|
-
| ---------- | ------ | -------------- |
|
|
141
|
-
| instanceId | string | auto-generated |
|
|
142
|
-
| timeout | number | 10000 |
|
|
143
|
-
| logger | Logger | new Logger() |
|
|
167
|
+
| Parameter | Type | Default Value | Description |
|
|
168
|
+
| ---------- | ------ | -------------- | ------------------------------------- |
|
|
169
|
+
| instanceId | string | auto-generated | Instance ID, used for message routing |
|
|
170
|
+
| timeout | number | 10000 | Request timeout (milliseconds) |
|
|
171
|
+
| logger | Logger | new Logger() | Logger instance |
|
|
144
172
|
|
|
145
|
-
####
|
|
173
|
+
#### Methods
|
|
146
174
|
|
|
147
|
-
#####
|
|
175
|
+
##### invoke()
|
|
148
176
|
|
|
149
|
-
|
|
177
|
+
Send request and wait for response.
|
|
150
178
|
|
|
151
179
|
```typescript
|
|
152
|
-
|
|
180
|
+
nexus.invoke<T>(methodOrOptions: string | InvokeOptions): Promise<T>
|
|
153
181
|
```
|
|
154
182
|
|
|
155
183
|
**Options:**
|
|
156
184
|
|
|
157
|
-
|
|
|
158
|
-
| ---------- | ----------------------- |
|
|
159
|
-
|
|
|
160
|
-
|
|
|
161
|
-
| to | string |
|
|
162
|
-
| metadata | Record<string, unknown> |
|
|
163
|
-
| timeout | number |
|
|
164
|
-
| retryCount | number |
|
|
165
|
-
| retryDelay | number |
|
|
185
|
+
| Parameter | Type | Required | Description |
|
|
186
|
+
| ---------- | ----------------------- | -------- | ---------------------------- |
|
|
187
|
+
| method | string | Yes | Message method |
|
|
188
|
+
| params | unknown | No | Request data |
|
|
189
|
+
| to | string | No | Target instance ID |
|
|
190
|
+
| metadata | Record<string, unknown> | No | Metadata |
|
|
191
|
+
| timeout | number | No | Timeout (overrides global) |
|
|
192
|
+
| retryCount | number | No | Number of retries on failure |
|
|
193
|
+
| retryDelay | number | No | Retry delay (milliseconds) |
|
|
166
194
|
|
|
167
|
-
|
|
195
|
+
**Example:**
|
|
168
196
|
|
|
169
197
|
```typescript
|
|
170
|
-
//
|
|
171
|
-
const result = await
|
|
198
|
+
// Simple request
|
|
199
|
+
const result = await nexus.invoke('FETCH_DATA')
|
|
172
200
|
|
|
173
|
-
//
|
|
174
|
-
const result = await
|
|
175
|
-
|
|
176
|
-
|
|
201
|
+
// Full configuration
|
|
202
|
+
const result = await nexus.invoke({
|
|
203
|
+
method: 'FETCH_DATA',
|
|
204
|
+
params: { id: 123 },
|
|
177
205
|
to: 'target-instance',
|
|
178
206
|
timeout: 5000,
|
|
179
207
|
retryCount: 3,
|
|
@@ -181,90 +209,134 @@ const result = await bridge.request({
|
|
|
181
209
|
})
|
|
182
210
|
```
|
|
183
211
|
|
|
184
|
-
#####
|
|
212
|
+
##### notify()
|
|
213
|
+
|
|
214
|
+
Send a one-way notification (Fire-and-Forget). Does not wait for a response and does not generate an ID. Complies with JSON-RPC 2.0 Notification specification.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
nexus.notify(methodOrOptions: string | Omit<InvokeOptions, 'timeout' | 'retryCount' | 'retryDelay'>): void
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Options:**
|
|
221
|
+
|
|
222
|
+
| Parameter | Type | Required | Description |
|
|
223
|
+
| --------- | ----------------------- | -------- | ------------------- |
|
|
224
|
+
| method | string | Yes | Notification method |
|
|
225
|
+
| params | unknown | No | Notification data |
|
|
226
|
+
| to | string | No | Target instance ID |
|
|
227
|
+
| metadata | Record<string, unknown> | No | Metadata |
|
|
185
228
|
|
|
186
|
-
|
|
229
|
+
**Example:**
|
|
187
230
|
|
|
188
231
|
```typescript
|
|
189
|
-
|
|
232
|
+
// Simple notification
|
|
233
|
+
nexus.notify('HEARTBEAT')
|
|
234
|
+
|
|
235
|
+
// Full configuration
|
|
236
|
+
nexus.notify({
|
|
237
|
+
method: 'UPDATE_STATE',
|
|
238
|
+
params: { state: 'ready' },
|
|
239
|
+
to: 'target-instance',
|
|
240
|
+
})
|
|
190
241
|
```
|
|
191
242
|
|
|
192
|
-
|
|
243
|
+
##### handle()
|
|
193
244
|
|
|
194
|
-
|
|
245
|
+
Register a request handler for a specific method. The return value (or resolved value of a returned Promise) is automatically sent back as the response.
|
|
195
246
|
|
|
196
247
|
```typescript
|
|
197
|
-
|
|
198
|
-
|
|
248
|
+
nexus.handle<Params, Result>(method: string, handler: InvokeHandler<Params, Result>): () => void
|
|
249
|
+
```
|
|
199
250
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
251
|
+
**Parameters:**
|
|
252
|
+
|
|
253
|
+
- `method`: The method name to handle.
|
|
254
|
+
- `handler`: A function that receives `(params, context)` and returns a result or a Promise.
|
|
255
|
+
|
|
256
|
+
**InvokeContext:**
|
|
257
|
+
|
|
258
|
+
| Property | Type | Description |
|
|
259
|
+
| ----------- | ------------------------- | ----------------------------------------------- |
|
|
260
|
+
| `messageId` | `string` | Unique identifier for the request (JSON-RPC ID) |
|
|
261
|
+
| `from` | `string` | Instance ID of the sender |
|
|
262
|
+
| `to` | `string` | Instance ID of the receiver (your instance ID) |
|
|
263
|
+
| `metadata` | `Record<string, unknown>` | Custom metadata sent with the envelope |
|
|
264
|
+
|
|
265
|
+
**Example:**
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
const unsubscribe = nexus.handle('ECHO', (params, context) => {
|
|
269
|
+
console.log(`Received ECHO from ${context.from}`)
|
|
270
|
+
return { echoed: params }
|
|
203
271
|
})
|
|
204
272
|
|
|
205
|
-
//
|
|
273
|
+
// Unsubscribe
|
|
206
274
|
unsubscribe()
|
|
207
275
|
```
|
|
208
276
|
|
|
209
|
-
#####
|
|
277
|
+
##### onNotification()
|
|
210
278
|
|
|
211
|
-
|
|
279
|
+
Register a handler for a specific notification method (one-way messages).
|
|
212
280
|
|
|
213
281
|
```typescript
|
|
214
|
-
|
|
282
|
+
nexus.onNotification<Params>(method: string, handler: NotificationHandler<Params>): () => void
|
|
215
283
|
```
|
|
216
284
|
|
|
217
|
-
|
|
285
|
+
**Example:**
|
|
218
286
|
|
|
219
287
|
```typescript
|
|
220
|
-
|
|
221
|
-
|
|
288
|
+
const unsubscribe = nexus.onNotification('HEARTBEAT', (params, context) => {
|
|
289
|
+
console.log(`Heartbeat from ${context.from}`)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// Unsubscribe
|
|
293
|
+
unsubscribe()
|
|
222
294
|
```
|
|
223
295
|
|
|
224
296
|
##### onError()
|
|
225
297
|
|
|
226
|
-
|
|
298
|
+
Register error handler.
|
|
227
299
|
|
|
228
300
|
```typescript
|
|
229
|
-
|
|
301
|
+
nexus.onError(handler: ErrorHandler): () => void
|
|
230
302
|
```
|
|
231
303
|
|
|
232
|
-
|
|
304
|
+
**Example:**
|
|
233
305
|
|
|
234
306
|
```typescript
|
|
235
|
-
|
|
307
|
+
nexus.onError((error, context) => {
|
|
236
308
|
console.error('Bridge error:', error.message, context)
|
|
237
|
-
//
|
|
309
|
+
// Send to error tracking service
|
|
238
310
|
Sentry.captureException(error, { extra: context })
|
|
239
311
|
})
|
|
240
312
|
```
|
|
241
313
|
|
|
242
314
|
##### getMetrics()
|
|
243
315
|
|
|
244
|
-
|
|
316
|
+
Get monitoring metrics.
|
|
245
317
|
|
|
246
318
|
```typescript
|
|
247
|
-
|
|
319
|
+
nexus.getMetrics(): Metrics
|
|
248
320
|
```
|
|
249
321
|
|
|
250
|
-
|
|
322
|
+
**Return Value:**
|
|
251
323
|
|
|
252
324
|
```typescript
|
|
253
325
|
{
|
|
254
|
-
messagesSent: number //
|
|
255
|
-
messagesReceived: number //
|
|
256
|
-
messagesFailed: number //
|
|
257
|
-
pendingMessages: number //
|
|
258
|
-
queuedMessages: number //
|
|
259
|
-
totalLatency: number //
|
|
260
|
-
averageLatency: number //
|
|
326
|
+
messagesSent: number // Messages sent
|
|
327
|
+
messagesReceived: number // Messages received
|
|
328
|
+
messagesFailed: number // Messages failed
|
|
329
|
+
pendingMessages: number // Pending messages
|
|
330
|
+
queuedMessages: number // Queued messages
|
|
331
|
+
totalLatency: number // Total latency (milliseconds)
|
|
332
|
+
averageLatency: number // Average latency (milliseconds)
|
|
261
333
|
}
|
|
262
334
|
```
|
|
263
335
|
|
|
264
|
-
|
|
336
|
+
**Example:**
|
|
265
337
|
|
|
266
338
|
```typescript
|
|
267
|
-
const metrics =
|
|
339
|
+
const metrics = nexus.getMetrics()
|
|
268
340
|
console.log(`Avg latency: ${metrics.averageLatency}ms`)
|
|
269
341
|
console.log(
|
|
270
342
|
`Success rate: ${((metrics.messagesReceived / metrics.messagesSent) * 100).toFixed(2)}%`,
|
|
@@ -273,42 +345,42 @@ console.log(
|
|
|
273
345
|
|
|
274
346
|
##### onMetrics()
|
|
275
347
|
|
|
276
|
-
|
|
348
|
+
Register metrics change callback.
|
|
277
349
|
|
|
278
350
|
```typescript
|
|
279
|
-
|
|
351
|
+
nexus.onMetrics(callback: MetricsCallback): () => void
|
|
280
352
|
```
|
|
281
353
|
|
|
282
|
-
|
|
354
|
+
**Example:**
|
|
283
355
|
|
|
284
356
|
```typescript
|
|
285
|
-
const unsubscribe =
|
|
286
|
-
//
|
|
357
|
+
const unsubscribe = nexus.onMetrics((metrics) => {
|
|
358
|
+
// Send to monitoring system
|
|
287
359
|
metricsService.report(metrics)
|
|
288
360
|
})
|
|
289
361
|
```
|
|
290
362
|
|
|
291
363
|
##### flushQueue()
|
|
292
364
|
|
|
293
|
-
|
|
365
|
+
Flush the message queue, sending all cached messages.
|
|
294
366
|
|
|
295
367
|
```typescript
|
|
296
|
-
|
|
368
|
+
nexus.flushQueue()
|
|
297
369
|
```
|
|
298
370
|
|
|
299
371
|
##### destroy()
|
|
300
372
|
|
|
301
|
-
|
|
373
|
+
Destroy the instance and clean up resources.
|
|
302
374
|
|
|
303
375
|
```typescript
|
|
304
|
-
|
|
376
|
+
nexus.destroy()
|
|
305
377
|
```
|
|
306
378
|
|
|
307
|
-
|
|
379
|
+
**Note**: The `destroy()` method automatically calls the driver's `destroy()` method to clean up resources like event listeners. It is recommended to call this method when the component is unmounted to avoid memory leaks.
|
|
308
380
|
|
|
309
381
|
### WebSocketDriver
|
|
310
382
|
|
|
311
|
-
####
|
|
383
|
+
#### Constructor
|
|
312
384
|
|
|
313
385
|
```typescript
|
|
314
386
|
new WebSocketDriver(options: WebSocketDriverOptions)
|
|
@@ -316,20 +388,20 @@ new WebSocketDriver(options: WebSocketDriverOptions)
|
|
|
316
388
|
|
|
317
389
|
**Options:**
|
|
318
390
|
|
|
319
|
-
|
|
|
320
|
-
| --------- | --------------------------- |
|
|
321
|
-
| url | string |
|
|
322
|
-
| reconnect | boolean \| ReconnectOptions | true
|
|
323
|
-
| logger | Logger | new Logger()
|
|
391
|
+
| Parameter | Type | Default Value | Description |
|
|
392
|
+
| --------- | --------------------------- | ------------- | ---------------------------------- |
|
|
393
|
+
| url | string | Required | WebSocket URL |
|
|
394
|
+
| reconnect | boolean \| ReconnectOptions | true | Whether to automatically reconnect |
|
|
395
|
+
| logger | Logger | new Logger() | Logger instance |
|
|
324
396
|
|
|
325
397
|
**ReconnectOptions:**
|
|
326
398
|
|
|
327
|
-
|
|
|
328
|
-
| ------------- | ------ |
|
|
329
|
-
| maxRetries | number | Infinity
|
|
330
|
-
| retryInterval | number | 5000
|
|
399
|
+
| Parameter | Type | Default Value | Description |
|
|
400
|
+
| ------------- | ------ | ------------- | ----------------------------- |
|
|
401
|
+
| maxRetries | number | Infinity | Maximum retry count |
|
|
402
|
+
| retryInterval | number | 5000 | Retry interval (milliseconds) |
|
|
331
403
|
|
|
332
|
-
|
|
404
|
+
**Example:**
|
|
333
405
|
|
|
334
406
|
```typescript
|
|
335
407
|
const driver = new WebSocketDriver({
|
|
@@ -341,11 +413,11 @@ const driver = new WebSocketDriver({
|
|
|
341
413
|
})
|
|
342
414
|
```
|
|
343
415
|
|
|
344
|
-
####
|
|
416
|
+
#### Methods
|
|
345
417
|
|
|
346
418
|
##### close()
|
|
347
419
|
|
|
348
|
-
|
|
420
|
+
Close connection and stop reconnection.
|
|
349
421
|
|
|
350
422
|
```typescript
|
|
351
423
|
driver.close()
|
|
@@ -353,20 +425,20 @@ driver.close()
|
|
|
353
425
|
|
|
354
426
|
### PostMessageDriver
|
|
355
427
|
|
|
356
|
-
####
|
|
428
|
+
#### Constructor
|
|
357
429
|
|
|
358
430
|
```typescript
|
|
359
431
|
new PostMessageDriver(targetWindow: Window, targetOrigin: string)
|
|
360
432
|
```
|
|
361
433
|
|
|
362
|
-
|
|
434
|
+
**Parameters:**
|
|
363
435
|
|
|
364
|
-
|
|
|
365
|
-
| ------------ | ------ |
|
|
366
|
-
| targetWindow | Window |
|
|
367
|
-
| targetOrigin | string |
|
|
436
|
+
| Parameter | Type | Required | Description |
|
|
437
|
+
| ------------ | ------ | -------- | ----------------------------------------------------------------- |
|
|
438
|
+
| targetWindow | Window | Yes | Target window object |
|
|
439
|
+
| targetOrigin | string | Yes | Target origin address (security requirement, '\*' cannot be used) |
|
|
368
440
|
|
|
369
|
-
|
|
441
|
+
**Example:**
|
|
370
442
|
|
|
371
443
|
```typescript
|
|
372
444
|
const driver = new PostMessageDriver(window.parent, 'https://app.example.com')
|
|
@@ -374,27 +446,27 @@ const driver = new PostMessageDriver(window.parent, 'https://app.example.com')
|
|
|
374
446
|
|
|
375
447
|
### MittDriver
|
|
376
448
|
|
|
377
|
-
####
|
|
449
|
+
#### Constructor
|
|
378
450
|
|
|
379
451
|
```typescript
|
|
380
452
|
new MittDriver(emitter: Emitter<Record<string, Message>>)
|
|
381
453
|
```
|
|
382
454
|
|
|
383
|
-
|
|
455
|
+
**Example:**
|
|
384
456
|
|
|
385
457
|
```typescript
|
|
386
458
|
import { createEmitter, MittDriver } from 'message-nexus'
|
|
387
459
|
|
|
388
|
-
//
|
|
460
|
+
// Use the factory function to create an independent emitter instance
|
|
389
461
|
const emitter = createEmitter()
|
|
390
462
|
const driver = new MittDriver(emitter)
|
|
391
463
|
```
|
|
392
464
|
|
|
393
|
-
|
|
465
|
+
**Note**: It is recommended to use the `createEmitter()` factory function to create an independent emitter instance.
|
|
394
466
|
|
|
395
467
|
### BroadcastDriver
|
|
396
468
|
|
|
397
|
-
####
|
|
469
|
+
#### Constructor
|
|
398
470
|
|
|
399
471
|
```typescript
|
|
400
472
|
new BroadcastDriver(options: BroadcastDriverOptions)
|
|
@@ -402,83 +474,80 @@ new BroadcastDriver(options: BroadcastDriverOptions)
|
|
|
402
474
|
|
|
403
475
|
**BroadcastDriverOptions:**
|
|
404
476
|
|
|
405
|
-
|
|
|
406
|
-
|
|
|
407
|
-
| channel
|
|
477
|
+
| Parameter | Type | Default Value | Description |
|
|
478
|
+
| --------- | ------ | ------------- | ---------------------- |
|
|
479
|
+
| channel | string | Required | Broadcast channel name |
|
|
408
480
|
|
|
409
|
-
|
|
481
|
+
**Example:**
|
|
410
482
|
|
|
411
483
|
```typescript
|
|
412
|
-
import { BroadcastDriver,
|
|
484
|
+
import { BroadcastDriver, MessageNexus } from 'message-nexus'
|
|
413
485
|
|
|
414
486
|
const driver = new BroadcastDriver({ channel: 'my-app-channel' })
|
|
415
|
-
const
|
|
487
|
+
const nexus = new MessageNexus(driver)
|
|
416
488
|
|
|
417
|
-
//
|
|
418
|
-
|
|
419
|
-
console.log('Received from another tab:',
|
|
420
|
-
|
|
489
|
+
// Listen for messages from other tabs
|
|
490
|
+
nexus.handle('SOME_METHOD', (params, context) => {
|
|
491
|
+
console.log('Received from another tab:', params)
|
|
492
|
+
return { received: true }
|
|
421
493
|
})
|
|
422
494
|
|
|
423
|
-
//
|
|
424
|
-
|
|
495
|
+
// Clean up resources
|
|
496
|
+
nexus.destroy()
|
|
425
497
|
```
|
|
426
498
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
- 同一源下的多个标签页可以通过相同频道名进行通信
|
|
430
|
-
- 自动添加协议标识符,过滤非 MessageBridge 消息
|
|
431
|
-
- 支持动态切换频道
|
|
499
|
+
**Features:**
|
|
432
500
|
|
|
433
|
-
|
|
501
|
+
- Multiple tabs under the same origin can communicate via the same channel name
|
|
502
|
+
- Automatically adds a protocol identifier to filter non-MessageNexus messages
|
|
503
|
+
- Supports dynamic channel switching
|
|
434
504
|
|
|
435
|
-
|
|
505
|
+
## Advanced Usage / Techniques
|
|
436
506
|
|
|
437
|
-
|
|
438
|
-
import { Logger, createConsoleHandler, LogLevel } from 'message-nexus/utils/logger'
|
|
507
|
+
### Asynchronous Handlers
|
|
439
508
|
|
|
440
|
-
|
|
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
|
-
### 自定义日志处理器
|
|
509
|
+
Handlers registered via `handle()` can be `async` functions or return a `Promise`. MessageNexus will wait for the Promise to resolve before sending the response back to the caller.
|
|
450
510
|
|
|
451
511
|
```typescript
|
|
452
|
-
|
|
453
|
-
fetch(
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
})
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
logger.addHandler(apiHandler)
|
|
512
|
+
nexus.handle('FETCH_REMOTE', async (params) => {
|
|
513
|
+
const data = await fetch(`https://api.example.com/items/${params.id}`)
|
|
514
|
+
return await data.json()
|
|
515
|
+
})
|
|
460
516
|
```
|
|
461
517
|
|
|
462
|
-
###
|
|
518
|
+
### Suspending Responses (Manual Reply Simulation)
|
|
519
|
+
|
|
520
|
+
In some cases, you may need to wait for a user action (like clicking a button in the UI) before replying to a request. You can achieve this by returning a Promise and storing its `resolve` function.
|
|
463
521
|
|
|
464
522
|
```typescript
|
|
465
|
-
|
|
466
|
-
```
|
|
523
|
+
const pendingResolvers = new Map<string, (value: any) => void>()
|
|
467
524
|
|
|
468
|
-
|
|
525
|
+
nexus.handle('user.confirm', (params, context) => {
|
|
526
|
+
return new Promise((resolve) => {
|
|
527
|
+
// Store the resolve function indexed by messageId
|
|
528
|
+
const id = context.messageId!
|
|
529
|
+
pendingResolvers.set(id, resolve)
|
|
469
530
|
|
|
470
|
-
|
|
471
|
-
|
|
531
|
+
// Trigger some UI to show a confirmation dialog
|
|
532
|
+
showDialog(params.message)
|
|
533
|
+
})
|
|
534
|
+
})
|
|
472
535
|
|
|
473
|
-
|
|
474
|
-
|
|
536
|
+
// Later, when the user clicks "Confirm"
|
|
537
|
+
function onUserConfirm(id: string) {
|
|
538
|
+
const resolve = pendingResolvers.get(id)
|
|
539
|
+
if (resolve) {
|
|
540
|
+
resolve({ confirmed: true })
|
|
541
|
+
pendingResolvers.delete(id)
|
|
542
|
+
}
|
|
543
|
+
}
|
|
475
544
|
```
|
|
476
545
|
|
|
477
|
-
##
|
|
546
|
+
## Design Highlights
|
|
478
547
|
|
|
479
|
-
### 1.
|
|
548
|
+
### 1. Type Safety
|
|
480
549
|
|
|
481
|
-
|
|
550
|
+
MessageNexus uses TypeScript generics to provide full type inference:
|
|
482
551
|
|
|
483
552
|
```typescript
|
|
484
553
|
interface UserRequest {
|
|
@@ -490,47 +559,48 @@ interface UserResponse {
|
|
|
490
559
|
name: string
|
|
491
560
|
}
|
|
492
561
|
|
|
493
|
-
const
|
|
562
|
+
const nexus = new MessageNexus<UserRequest, UserResponse>(driver)
|
|
494
563
|
|
|
495
|
-
//
|
|
496
|
-
const response = await
|
|
497
|
-
|
|
498
|
-
|
|
564
|
+
// Full type inference
|
|
565
|
+
const response = await nexus.invoke({
|
|
566
|
+
method: 'GET_USER',
|
|
567
|
+
params: { userId: 123 }, // Type: UserRequest
|
|
499
568
|
})
|
|
500
569
|
|
|
501
|
-
// response
|
|
570
|
+
// response Type: UserResponse
|
|
502
571
|
console.log(response.name)
|
|
503
572
|
```
|
|
504
573
|
|
|
505
|
-
### 2.
|
|
574
|
+
### 2. Memory Safety
|
|
506
575
|
|
|
507
|
-
-
|
|
508
|
-
-
|
|
509
|
-
-
|
|
510
|
-
-
|
|
511
|
-
- **
|
|
512
|
-
- **
|
|
576
|
+
- **Auto Cleanup**: Regularly clean up expired message records
|
|
577
|
+
- **Manual Cleanup**: Internal request records are deleted immediately after the response is received or timed out
|
|
578
|
+
- **Auto-Reply**: Handlers automatically send responses when the return value is resolved, ensuring no orphaned requests
|
|
579
|
+
- **Resource Release**: The `destroy()` method cleans up all timers and event listeners
|
|
580
|
+
- **Queue Limits**: The message queue has a maximum size limit to prevent infinite growth
|
|
581
|
+
- **Driver Lifecycle**: Each driver implements the `destroy()` method to correctly release resources
|
|
582
|
+
- **Emitter Isolation**: Recommended to use `createEmitter()` to create independent instances, avoiding memory leaks caused by shared singletons
|
|
513
583
|
|
|
514
|
-
### 3.
|
|
584
|
+
### 3. Error Recovery
|
|
515
585
|
|
|
516
|
-
-
|
|
517
|
-
-
|
|
518
|
-
-
|
|
519
|
-
-
|
|
586
|
+
- **Auto Reconnect**: WebSocket automatic reconnection mechanism with exponential backoff strategy
|
|
587
|
+
- **Request Retry**: Automatic retry on request failure, configurable retry counts and delays
|
|
588
|
+
- **Message Queue**: Offline message caching, automatically sent after connection recovery
|
|
589
|
+
- **Error Callback**: Unified error handling mechanism
|
|
520
590
|
|
|
521
|
-
### 4.
|
|
591
|
+
### 4. Security Hardening
|
|
522
592
|
|
|
523
|
-
- **PostMessage**:
|
|
524
|
-
- **BroadcastChannel**:
|
|
525
|
-
-
|
|
526
|
-
-
|
|
593
|
+
- **PostMessage**: Prohibit using `'*'` as targetOrigin; the origin address must be explicitly specified
|
|
594
|
+
- **BroadcastChannel**: Use the protocol identifier `__messageBridge` to distinguish MessageNexus messages from user-defined messages
|
|
595
|
+
- **Message Validation**: Runtime validation of message format to prevent crashes from illegal messages
|
|
596
|
+
- **Source Filtering**: Automatically filters non-target messages
|
|
527
597
|
|
|
528
|
-
### 5.
|
|
598
|
+
### 5. Observability
|
|
529
599
|
|
|
530
|
-
|
|
600
|
+
Built-in monitoring metrics for easy production environment monitoring:
|
|
531
601
|
|
|
532
602
|
```typescript
|
|
533
|
-
const metrics =
|
|
603
|
+
const metrics = nexus.getMetrics()
|
|
534
604
|
|
|
535
605
|
console.log(`Messages: ${metrics.messagesSent} sent, ${metrics.messagesReceived} received`)
|
|
536
606
|
console.log(
|
|
@@ -540,34 +610,15 @@ console.log(`Avg latency: ${metrics.averageLatency}ms`)
|
|
|
540
610
|
console.log(`Pending: ${metrics.pendingMessages}, Queued: ${metrics.queuedMessages}`)
|
|
541
611
|
```
|
|
542
612
|
|
|
543
|
-
|
|
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
|
-
## 测试
|
|
613
|
+
## Testing
|
|
563
614
|
|
|
564
|
-
|
|
615
|
+
Run unit tests:
|
|
565
616
|
|
|
566
617
|
```bash
|
|
567
618
|
cd packages/message-nexus
|
|
568
619
|
pnpm test:run
|
|
569
620
|
```
|
|
570
621
|
|
|
571
|
-
##
|
|
622
|
+
## License
|
|
572
623
|
|
|
573
624
|
MIT
|