message-nexus 1.0.1 → 1.1.1
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 +271 -242
- package/dist/index.cjs +259 -90
- package/dist/index.d.cts +79 -31
- package/dist/index.d.ts +79 -31
- package/dist/index.js +259 -90
- package/package.json +1 -1
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,144 +10,150 @@ 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
33
|
import MessageNexus, { MittDriver, createEmitter } from 'message-nexus'
|
|
32
34
|
|
|
33
|
-
//
|
|
35
|
+
// Shared emitter
|
|
34
36
|
const emitter = createEmitter()
|
|
35
37
|
|
|
36
38
|
const driver = new MittDriver(emitter)
|
|
37
39
|
const nexus = new MessageNexus(driver)
|
|
38
40
|
|
|
39
|
-
//
|
|
40
|
-
const response = await nexus.
|
|
41
|
+
// Send request
|
|
42
|
+
const response = await nexus.invoke('GET_DATA', { id: 123 })
|
|
41
43
|
console.log(response)
|
|
42
44
|
|
|
43
|
-
//
|
|
45
|
+
// Send one-way notification
|
|
46
|
+
nexus.notify('UPDATE_STATUS', { status: 'active' })
|
|
47
|
+
|
|
48
|
+
// Listen for commands
|
|
44
49
|
const receiverDriver = new MittDriver(emitter)
|
|
45
50
|
const receiverNexus = new MessageNexus(receiverDriver)
|
|
46
|
-
const unsubscribe = receiverNexus.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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)
|
|
50
58
|
})
|
|
51
59
|
```
|
|
52
60
|
|
|
53
|
-
### 2. iframe/Window
|
|
61
|
+
### 2. iframe/Window Communication (PostMessage)
|
|
54
62
|
|
|
55
63
|
```typescript
|
|
56
64
|
import MessageNexus, { PostMessageDriver } from 'message-nexus'
|
|
57
65
|
|
|
58
|
-
//
|
|
66
|
+
// Sender
|
|
59
67
|
const driver = new PostMessageDriver(window.parent, 'https://example.com')
|
|
60
68
|
const nexus = new MessageNexus(driver)
|
|
61
69
|
|
|
62
|
-
const response = await nexus.
|
|
70
|
+
const response = await nexus.invoke('PING')
|
|
63
71
|
console.log('Pong:', response)
|
|
64
72
|
|
|
65
|
-
//
|
|
73
|
+
// Receiver
|
|
66
74
|
const iframeDriver = new PostMessageDriver(iframe.contentWindow, 'https://example.com')
|
|
67
75
|
const iframeNexus = new MessageNexus(iframeDriver)
|
|
68
76
|
|
|
69
|
-
iframeNexus.
|
|
70
|
-
|
|
71
|
-
iframeNexus.reply(data.id, { time: Date.now() })
|
|
72
|
-
}
|
|
77
|
+
iframeNexus.handle('PING', (params, context) => {
|
|
78
|
+
return { time: Date.now() }
|
|
73
79
|
})
|
|
74
80
|
```
|
|
75
81
|
|
|
76
|
-
### 3.
|
|
82
|
+
### 3. Cross-Tab Communication (BroadcastChannel)
|
|
77
83
|
|
|
78
84
|
```typescript
|
|
79
85
|
import MessageNexus, { BroadcastDriver } from 'message-nexus'
|
|
80
86
|
|
|
81
|
-
//
|
|
87
|
+
// Create BroadcastDriver, specifying the channel name
|
|
82
88
|
const driver = new BroadcastDriver({ channel: 'my-app-channel' })
|
|
83
89
|
const nexus = new MessageNexus(driver)
|
|
84
90
|
|
|
85
|
-
//
|
|
86
|
-
nexus.
|
|
87
|
-
console.log('Received:',
|
|
88
|
-
|
|
91
|
+
// Listen for commands
|
|
92
|
+
nexus.handle('SYNC_STATE', (params, context) => {
|
|
93
|
+
console.log('Received:', params)
|
|
94
|
+
return { result: 'success' }
|
|
89
95
|
})
|
|
90
96
|
|
|
91
|
-
//
|
|
92
|
-
const response = await nexus.
|
|
93
|
-
|
|
94
|
-
|
|
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: '...' },
|
|
95
101
|
})
|
|
96
102
|
|
|
97
|
-
//
|
|
103
|
+
// Receiver
|
|
98
104
|
const receiverDriver = new BroadcastDriver({ channel: 'my-app-channel' })
|
|
99
105
|
const receiverNexus = new MessageNexus(receiverDriver)
|
|
100
|
-
receiverNexus.
|
|
101
|
-
console.log('Received:',
|
|
102
|
-
|
|
106
|
+
receiverNexus.handle('SYNC_STATE', (params, context) => {
|
|
107
|
+
console.log('Received:', params)
|
|
108
|
+
return { result: 'success' }
|
|
103
109
|
})
|
|
104
110
|
```
|
|
105
111
|
|
|
106
|
-
### 4. WebSocket
|
|
112
|
+
### 4. WebSocket Communication
|
|
107
113
|
|
|
108
114
|
```typescript
|
|
109
115
|
import MessageNexus, { WebSocketDriver } from 'message-nexus'
|
|
110
116
|
|
|
111
|
-
//
|
|
117
|
+
// Automatic reconnection configuration
|
|
112
118
|
const driver = new WebSocketDriver({
|
|
113
119
|
url: 'wss://api.example.com/ws',
|
|
114
120
|
reconnect: {
|
|
115
|
-
maxRetries: 5, //
|
|
116
|
-
retryInterval: 3000, //
|
|
121
|
+
maxRetries: 5, // Maximum retry count
|
|
122
|
+
retryInterval: 3000, // Retry interval (milliseconds)
|
|
117
123
|
},
|
|
118
124
|
})
|
|
119
125
|
|
|
120
126
|
const nexus = new MessageNexus(driver)
|
|
121
127
|
|
|
122
|
-
//
|
|
123
|
-
const response = await nexus.
|
|
124
|
-
|
|
125
|
-
|
|
128
|
+
// Send request
|
|
129
|
+
const response = await nexus.invoke({
|
|
130
|
+
method: 'GET_USER',
|
|
131
|
+
params: { userId: 123 },
|
|
126
132
|
timeout: 5000,
|
|
127
|
-
retryCount: 3, //
|
|
128
|
-
retryDelay: 1000, //
|
|
133
|
+
retryCount: 3, // Retry 3 times on failure
|
|
134
|
+
retryDelay: 1000, // Retry delay
|
|
129
135
|
})
|
|
130
136
|
|
|
131
|
-
//
|
|
137
|
+
// Receiver
|
|
132
138
|
const receiverDriver = new WebSocketDriver({
|
|
133
139
|
url: 'wss://api.example.com/ws',
|
|
134
140
|
reconnect: {
|
|
135
|
-
maxRetries: 5, //
|
|
136
|
-
retryInterval: 3000, //
|
|
141
|
+
maxRetries: 5, // Maximum retry count
|
|
142
|
+
retryInterval: 3000, // Retry interval (milliseconds)
|
|
137
143
|
},
|
|
138
144
|
})
|
|
139
145
|
const receiverNexus = new MessageNexus(receiverDriver)
|
|
140
|
-
receiverNexus.
|
|
141
|
-
console.log('Received:',
|
|
142
|
-
|
|
146
|
+
receiverNexus.handle('SYNC_STATE', (params, context) => {
|
|
147
|
+
console.log('Received:', params)
|
|
148
|
+
return { result: 'success' }
|
|
143
149
|
})
|
|
144
150
|
```
|
|
145
151
|
|
|
146
|
-
## API
|
|
152
|
+
## API Documentation
|
|
147
153
|
|
|
148
154
|
### MessageNexus
|
|
149
155
|
|
|
150
|
-
####
|
|
156
|
+
#### Constructor
|
|
151
157
|
|
|
152
158
|
```typescript
|
|
153
159
|
new MessageNexus<RequestPayload, ResponsePayload>(
|
|
@@ -158,44 +164,44 @@ new MessageNexus<RequestPayload, ResponsePayload>(
|
|
|
158
164
|
|
|
159
165
|
**Options:**
|
|
160
166
|
|
|
161
|
-
|
|
|
162
|
-
| ---------- | ------ | -------------- |
|
|
163
|
-
| instanceId | string | auto-generated |
|
|
164
|
-
| timeout | number | 10000 |
|
|
165
|
-
| 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 |
|
|
166
172
|
|
|
167
|
-
####
|
|
173
|
+
#### Methods
|
|
168
174
|
|
|
169
|
-
#####
|
|
175
|
+
##### invoke()
|
|
170
176
|
|
|
171
|
-
|
|
177
|
+
Send request and wait for response.
|
|
172
178
|
|
|
173
179
|
```typescript
|
|
174
|
-
nexus.
|
|
180
|
+
nexus.invoke<T>(methodOrOptions: string | InvokeOptions): Promise<T>
|
|
175
181
|
```
|
|
176
182
|
|
|
177
183
|
**Options:**
|
|
178
184
|
|
|
179
|
-
|
|
|
180
|
-
| ---------- | ----------------------- |
|
|
181
|
-
|
|
|
182
|
-
|
|
|
183
|
-
| to | string |
|
|
184
|
-
| metadata | Record<string, unknown> |
|
|
185
|
-
| timeout | number |
|
|
186
|
-
| retryCount | number |
|
|
187
|
-
| 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) |
|
|
188
194
|
|
|
189
|
-
|
|
195
|
+
**Example:**
|
|
190
196
|
|
|
191
197
|
```typescript
|
|
192
|
-
//
|
|
193
|
-
const result = await nexus.
|
|
198
|
+
// Simple request
|
|
199
|
+
const result = await nexus.invoke('FETCH_DATA')
|
|
194
200
|
|
|
195
|
-
//
|
|
196
|
-
const result = await nexus.
|
|
197
|
-
|
|
198
|
-
|
|
201
|
+
// Full configuration
|
|
202
|
+
const result = await nexus.invoke({
|
|
203
|
+
method: 'FETCH_DATA',
|
|
204
|
+
params: { id: 123 },
|
|
199
205
|
to: 'target-instance',
|
|
200
206
|
timeout: 5000,
|
|
201
207
|
retryCount: 3,
|
|
@@ -203,87 +209,131 @@ const result = await nexus.request({
|
|
|
203
209
|
})
|
|
204
210
|
```
|
|
205
211
|
|
|
206
|
-
#####
|
|
212
|
+
##### notify()
|
|
207
213
|
|
|
208
|
-
|
|
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.
|
|
209
215
|
|
|
210
216
|
```typescript
|
|
211
|
-
nexus.
|
|
217
|
+
nexus.notify(methodOrOptions: string | Omit<InvokeOptions, 'timeout' | 'retryCount' | 'retryDelay'>): void
|
|
212
218
|
```
|
|
213
219
|
|
|
214
|
-
|
|
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 |
|
|
215
228
|
|
|
216
|
-
|
|
229
|
+
**Example:**
|
|
217
230
|
|
|
218
231
|
```typescript
|
|
219
|
-
|
|
220
|
-
|
|
232
|
+
// Simple notification
|
|
233
|
+
nexus.notify('HEARTBEAT')
|
|
221
234
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
235
|
+
// Full configuration
|
|
236
|
+
nexus.notify({
|
|
237
|
+
method: 'UPDATE_STATE',
|
|
238
|
+
params: { state: 'ready' },
|
|
239
|
+
to: 'target-instance',
|
|
240
|
+
})
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
##### handle()
|
|
244
|
+
|
|
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.
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
nexus.handle<Params, Result>(method: string, handler: InvokeHandler<Params, Result>): () => void
|
|
249
|
+
```
|
|
250
|
+
|
|
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 }
|
|
225
271
|
})
|
|
226
272
|
|
|
227
|
-
//
|
|
273
|
+
// Unsubscribe
|
|
228
274
|
unsubscribe()
|
|
229
275
|
```
|
|
230
276
|
|
|
231
|
-
#####
|
|
277
|
+
##### onNotification()
|
|
232
278
|
|
|
233
|
-
|
|
279
|
+
Register a handler for a specific notification method (one-way messages).
|
|
234
280
|
|
|
235
281
|
```typescript
|
|
236
|
-
nexus.
|
|
282
|
+
nexus.onNotification<Params>(method: string, handler: NotificationHandler<Params>): () => void
|
|
237
283
|
```
|
|
238
284
|
|
|
239
|
-
|
|
285
|
+
**Example:**
|
|
240
286
|
|
|
241
287
|
```typescript
|
|
242
|
-
nexus.
|
|
243
|
-
|
|
288
|
+
const unsubscribe = nexus.onNotification('HEARTBEAT', (params, context) => {
|
|
289
|
+
console.log(`Heartbeat from ${context.from}`)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// Unsubscribe
|
|
293
|
+
unsubscribe()
|
|
244
294
|
```
|
|
245
295
|
|
|
246
296
|
##### onError()
|
|
247
297
|
|
|
248
|
-
|
|
298
|
+
Register error handler.
|
|
249
299
|
|
|
250
300
|
```typescript
|
|
251
301
|
nexus.onError(handler: ErrorHandler): () => void
|
|
252
302
|
```
|
|
253
303
|
|
|
254
|
-
|
|
304
|
+
**Example:**
|
|
255
305
|
|
|
256
306
|
```typescript
|
|
257
307
|
nexus.onError((error, context) => {
|
|
258
308
|
console.error('Bridge error:', error.message, context)
|
|
259
|
-
//
|
|
309
|
+
// Send to error tracking service
|
|
260
310
|
Sentry.captureException(error, { extra: context })
|
|
261
311
|
})
|
|
262
312
|
```
|
|
263
313
|
|
|
264
314
|
##### getMetrics()
|
|
265
315
|
|
|
266
|
-
|
|
316
|
+
Get monitoring metrics.
|
|
267
317
|
|
|
268
318
|
```typescript
|
|
269
319
|
nexus.getMetrics(): Metrics
|
|
270
320
|
```
|
|
271
321
|
|
|
272
|
-
|
|
322
|
+
**Return Value:**
|
|
273
323
|
|
|
274
324
|
```typescript
|
|
275
325
|
{
|
|
276
|
-
messagesSent: number //
|
|
277
|
-
messagesReceived: number //
|
|
278
|
-
messagesFailed: number //
|
|
279
|
-
pendingMessages: number //
|
|
280
|
-
queuedMessages: number //
|
|
281
|
-
totalLatency: number //
|
|
282
|
-
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)
|
|
283
333
|
}
|
|
284
334
|
```
|
|
285
335
|
|
|
286
|
-
|
|
336
|
+
**Example:**
|
|
287
337
|
|
|
288
338
|
```typescript
|
|
289
339
|
const metrics = nexus.getMetrics()
|
|
@@ -295,24 +345,24 @@ console.log(
|
|
|
295
345
|
|
|
296
346
|
##### onMetrics()
|
|
297
347
|
|
|
298
|
-
|
|
348
|
+
Register metrics change callback.
|
|
299
349
|
|
|
300
350
|
```typescript
|
|
301
351
|
nexus.onMetrics(callback: MetricsCallback): () => void
|
|
302
352
|
```
|
|
303
353
|
|
|
304
|
-
|
|
354
|
+
**Example:**
|
|
305
355
|
|
|
306
356
|
```typescript
|
|
307
357
|
const unsubscribe = nexus.onMetrics((metrics) => {
|
|
308
|
-
//
|
|
358
|
+
// Send to monitoring system
|
|
309
359
|
metricsService.report(metrics)
|
|
310
360
|
})
|
|
311
361
|
```
|
|
312
362
|
|
|
313
363
|
##### flushQueue()
|
|
314
364
|
|
|
315
|
-
|
|
365
|
+
Flush the message queue, sending all cached messages.
|
|
316
366
|
|
|
317
367
|
```typescript
|
|
318
368
|
nexus.flushQueue()
|
|
@@ -320,17 +370,17 @@ nexus.flushQueue()
|
|
|
320
370
|
|
|
321
371
|
##### destroy()
|
|
322
372
|
|
|
323
|
-
|
|
373
|
+
Destroy the instance and clean up resources.
|
|
324
374
|
|
|
325
375
|
```typescript
|
|
326
376
|
nexus.destroy()
|
|
327
377
|
```
|
|
328
378
|
|
|
329
|
-
|
|
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.
|
|
330
380
|
|
|
331
381
|
### WebSocketDriver
|
|
332
382
|
|
|
333
|
-
####
|
|
383
|
+
#### Constructor
|
|
334
384
|
|
|
335
385
|
```typescript
|
|
336
386
|
new WebSocketDriver(options: WebSocketDriverOptions)
|
|
@@ -338,20 +388,20 @@ new WebSocketDriver(options: WebSocketDriverOptions)
|
|
|
338
388
|
|
|
339
389
|
**Options:**
|
|
340
390
|
|
|
341
|
-
|
|
|
342
|
-
| --------- | --------------------------- |
|
|
343
|
-
| url | string |
|
|
344
|
-
| reconnect | boolean \| ReconnectOptions | true
|
|
345
|
-
| 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 |
|
|
346
396
|
|
|
347
397
|
**ReconnectOptions:**
|
|
348
398
|
|
|
349
|
-
|
|
|
350
|
-
| ------------- | ------ |
|
|
351
|
-
| maxRetries | number | Infinity
|
|
352
|
-
| retryInterval | number | 5000
|
|
399
|
+
| Parameter | Type | Default Value | Description |
|
|
400
|
+
| ------------- | ------ | ------------- | ----------------------------- |
|
|
401
|
+
| maxRetries | number | Infinity | Maximum retry count |
|
|
402
|
+
| retryInterval | number | 5000 | Retry interval (milliseconds) |
|
|
353
403
|
|
|
354
|
-
|
|
404
|
+
**Example:**
|
|
355
405
|
|
|
356
406
|
```typescript
|
|
357
407
|
const driver = new WebSocketDriver({
|
|
@@ -363,11 +413,11 @@ const driver = new WebSocketDriver({
|
|
|
363
413
|
})
|
|
364
414
|
```
|
|
365
415
|
|
|
366
|
-
####
|
|
416
|
+
#### Methods
|
|
367
417
|
|
|
368
418
|
##### close()
|
|
369
419
|
|
|
370
|
-
|
|
420
|
+
Close connection and stop reconnection.
|
|
371
421
|
|
|
372
422
|
```typescript
|
|
373
423
|
driver.close()
|
|
@@ -375,20 +425,20 @@ driver.close()
|
|
|
375
425
|
|
|
376
426
|
### PostMessageDriver
|
|
377
427
|
|
|
378
|
-
####
|
|
428
|
+
#### Constructor
|
|
379
429
|
|
|
380
430
|
```typescript
|
|
381
431
|
new PostMessageDriver(targetWindow: Window, targetOrigin: string)
|
|
382
432
|
```
|
|
383
433
|
|
|
384
|
-
|
|
434
|
+
**Parameters:**
|
|
385
435
|
|
|
386
|
-
|
|
|
387
|
-
| ------------ | ------ |
|
|
388
|
-
| targetWindow | Window |
|
|
389
|
-
| 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) |
|
|
390
440
|
|
|
391
|
-
|
|
441
|
+
**Example:**
|
|
392
442
|
|
|
393
443
|
```typescript
|
|
394
444
|
const driver = new PostMessageDriver(window.parent, 'https://app.example.com')
|
|
@@ -396,27 +446,27 @@ const driver = new PostMessageDriver(window.parent, 'https://app.example.com')
|
|
|
396
446
|
|
|
397
447
|
### MittDriver
|
|
398
448
|
|
|
399
|
-
####
|
|
449
|
+
#### Constructor
|
|
400
450
|
|
|
401
451
|
```typescript
|
|
402
452
|
new MittDriver(emitter: Emitter<Record<string, Message>>)
|
|
403
453
|
```
|
|
404
454
|
|
|
405
|
-
|
|
455
|
+
**Example:**
|
|
406
456
|
|
|
407
457
|
```typescript
|
|
408
458
|
import { createEmitter, MittDriver } from 'message-nexus'
|
|
409
459
|
|
|
410
|
-
//
|
|
460
|
+
// Use the factory function to create an independent emitter instance
|
|
411
461
|
const emitter = createEmitter()
|
|
412
462
|
const driver = new MittDriver(emitter)
|
|
413
463
|
```
|
|
414
464
|
|
|
415
|
-
|
|
465
|
+
**Note**: It is recommended to use the `createEmitter()` factory function to create an independent emitter instance.
|
|
416
466
|
|
|
417
467
|
### BroadcastDriver
|
|
418
468
|
|
|
419
|
-
####
|
|
469
|
+
#### Constructor
|
|
420
470
|
|
|
421
471
|
```typescript
|
|
422
472
|
new BroadcastDriver(options: BroadcastDriverOptions)
|
|
@@ -424,11 +474,11 @@ new BroadcastDriver(options: BroadcastDriverOptions)
|
|
|
424
474
|
|
|
425
475
|
**BroadcastDriverOptions:**
|
|
426
476
|
|
|
427
|
-
|
|
|
428
|
-
|
|
|
429
|
-
| channel
|
|
477
|
+
| Parameter | Type | Default Value | Description |
|
|
478
|
+
| --------- | ------ | ------------- | ---------------------- |
|
|
479
|
+
| channel | string | Required | Broadcast channel name |
|
|
430
480
|
|
|
431
|
-
|
|
481
|
+
**Example:**
|
|
432
482
|
|
|
433
483
|
```typescript
|
|
434
484
|
import { BroadcastDriver, MessageNexus } from 'message-nexus'
|
|
@@ -436,71 +486,68 @@ import { BroadcastDriver, MessageNexus } from 'message-nexus'
|
|
|
436
486
|
const driver = new BroadcastDriver({ channel: 'my-app-channel' })
|
|
437
487
|
const nexus = new MessageNexus(driver)
|
|
438
488
|
|
|
439
|
-
//
|
|
440
|
-
nexus.
|
|
441
|
-
console.log('Received from another tab:',
|
|
442
|
-
|
|
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 }
|
|
443
493
|
})
|
|
444
494
|
|
|
445
|
-
//
|
|
495
|
+
// Clean up resources
|
|
446
496
|
nexus.destroy()
|
|
447
497
|
```
|
|
448
498
|
|
|
449
|
-
|
|
499
|
+
**Features:**
|
|
450
500
|
|
|
451
|
-
-
|
|
452
|
-
-
|
|
453
|
-
-
|
|
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
|
|
454
504
|
|
|
455
|
-
##
|
|
505
|
+
## Advanced Usage / Techniques
|
|
456
506
|
|
|
457
|
-
###
|
|
507
|
+
### Asynchronous Handlers
|
|
458
508
|
|
|
459
|
-
|
|
460
|
-
import { Logger, createConsoleHandler, LogLevel } from 'message-nexus/utils/logger'
|
|
461
|
-
|
|
462
|
-
const logger = new Logger('MyApp', LogLevel.DEBUG)
|
|
463
|
-
logger.addHandler(createConsoleHandler())
|
|
464
|
-
|
|
465
|
-
logger.debug('Debug message', { data: 123 })
|
|
466
|
-
logger.info('Info message')
|
|
467
|
-
logger.warn('Warning message')
|
|
468
|
-
logger.error('Error message', { error: new Error('test') })
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
### 自定义日志处理器
|
|
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.
|
|
472
510
|
|
|
473
511
|
```typescript
|
|
474
|
-
|
|
475
|
-
fetch(
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
})
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
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
|
+
})
|
|
482
516
|
```
|
|
483
517
|
|
|
484
|
-
###
|
|
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.
|
|
485
521
|
|
|
486
522
|
```typescript
|
|
487
|
-
|
|
488
|
-
```
|
|
523
|
+
const pendingResolvers = new Map<string, (value: any) => void>()
|
|
489
524
|
|
|
490
|
-
|
|
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)
|
|
491
530
|
|
|
492
|
-
|
|
493
|
-
|
|
531
|
+
// Trigger some UI to show a confirmation dialog
|
|
532
|
+
showDialog(params.message)
|
|
533
|
+
})
|
|
534
|
+
})
|
|
494
535
|
|
|
495
|
-
|
|
496
|
-
|
|
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
|
+
}
|
|
497
544
|
```
|
|
498
545
|
|
|
499
|
-
##
|
|
546
|
+
## Design Highlights
|
|
500
547
|
|
|
501
|
-
### 1.
|
|
548
|
+
### 1. Type Safety
|
|
502
549
|
|
|
503
|
-
MessageNexus
|
|
550
|
+
MessageNexus uses TypeScript generics to provide full type inference:
|
|
504
551
|
|
|
505
552
|
```typescript
|
|
506
553
|
interface UserRequest {
|
|
@@ -514,42 +561,43 @@ interface UserResponse {
|
|
|
514
561
|
|
|
515
562
|
const nexus = new MessageNexus<UserRequest, UserResponse>(driver)
|
|
516
563
|
|
|
517
|
-
//
|
|
518
|
-
const response = await nexus.
|
|
519
|
-
|
|
520
|
-
|
|
564
|
+
// Full type inference
|
|
565
|
+
const response = await nexus.invoke({
|
|
566
|
+
method: 'GET_USER',
|
|
567
|
+
params: { userId: 123 }, // Type: UserRequest
|
|
521
568
|
})
|
|
522
569
|
|
|
523
|
-
// response
|
|
570
|
+
// response Type: UserResponse
|
|
524
571
|
console.log(response.name)
|
|
525
572
|
```
|
|
526
573
|
|
|
527
|
-
### 2.
|
|
574
|
+
### 2. Memory Safety
|
|
528
575
|
|
|
529
|
-
-
|
|
530
|
-
-
|
|
531
|
-
-
|
|
532
|
-
-
|
|
533
|
-
- **
|
|
534
|
-
- **
|
|
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
|
|
535
583
|
|
|
536
|
-
### 3.
|
|
584
|
+
### 3. Error Recovery
|
|
537
585
|
|
|
538
|
-
-
|
|
539
|
-
-
|
|
540
|
-
-
|
|
541
|
-
-
|
|
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
|
|
542
590
|
|
|
543
|
-
### 4.
|
|
591
|
+
### 4. Security Hardening
|
|
544
592
|
|
|
545
|
-
- **PostMessage**:
|
|
546
|
-
- **BroadcastChannel**:
|
|
547
|
-
-
|
|
548
|
-
-
|
|
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
|
|
549
597
|
|
|
550
|
-
### 5.
|
|
598
|
+
### 5. Observability
|
|
551
599
|
|
|
552
|
-
|
|
600
|
+
Built-in monitoring metrics for easy production environment monitoring:
|
|
553
601
|
|
|
554
602
|
```typescript
|
|
555
603
|
const metrics = nexus.getMetrics()
|
|
@@ -562,34 +610,15 @@ console.log(`Avg latency: ${metrics.averageLatency}ms`)
|
|
|
562
610
|
console.log(`Pending: ${metrics.pendingMessages}, Queued: ${metrics.queuedMessages}`)
|
|
563
611
|
```
|
|
564
612
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
统一的日志接口,支持多种输出方式:
|
|
568
|
-
|
|
569
|
-
```typescript
|
|
570
|
-
// 控制台输出
|
|
571
|
-
logger.addHandler(createConsoleHandler())
|
|
572
|
-
|
|
573
|
-
// 发送到 API
|
|
574
|
-
logger.addHandler((entry) => {
|
|
575
|
-
fetch('/api/logs', { body: JSON.stringify(entry) })
|
|
576
|
-
})
|
|
577
|
-
|
|
578
|
-
// 发送到 ELK
|
|
579
|
-
logger.addHandler((entry) => {
|
|
580
|
-
elk.send(entry)
|
|
581
|
-
})
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
## 测试
|
|
613
|
+
## Testing
|
|
585
614
|
|
|
586
|
-
|
|
615
|
+
Run unit tests:
|
|
587
616
|
|
|
588
617
|
```bash
|
|
589
618
|
cd packages/message-nexus
|
|
590
619
|
pnpm test:run
|
|
591
620
|
```
|
|
592
621
|
|
|
593
|
-
##
|
|
622
|
+
## License
|
|
594
623
|
|
|
595
624
|
MIT
|