message-nexus 1.0.1 → 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 +271 -242
- package/dist/index.cjs +256 -87
- package/dist/index.d.cts +76 -28
- package/dist/index.d.ts +76 -28
- package/dist/index.js +256 -87
- package/package.json +9 -9
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,34 @@
|
|
|
1
1
|
import { Emitter } from 'mitt';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
type JsonRpcId = string | number | null;
|
|
4
|
+
interface JsonRpcRequest {
|
|
5
|
+
jsonrpc: '2.0';
|
|
6
|
+
method: string;
|
|
7
|
+
params?: unknown;
|
|
8
|
+
id: JsonRpcId;
|
|
9
|
+
}
|
|
10
|
+
interface JsonRpcNotification {
|
|
11
|
+
jsonrpc: '2.0';
|
|
12
|
+
method: string;
|
|
13
|
+
params?: unknown;
|
|
14
|
+
}
|
|
15
|
+
interface JsonRpcResponse {
|
|
16
|
+
jsonrpc: '2.0';
|
|
17
|
+
result?: unknown;
|
|
18
|
+
error?: {
|
|
19
|
+
code: number;
|
|
20
|
+
message: string;
|
|
21
|
+
data?: unknown;
|
|
22
|
+
};
|
|
23
|
+
id: JsonRpcId;
|
|
24
|
+
}
|
|
25
|
+
interface NexusEnvelope<T = JsonRpcRequest | JsonRpcResponse | JsonRpcNotification> {
|
|
7
26
|
from: string;
|
|
8
27
|
to?: string;
|
|
9
28
|
metadata?: Record<string, unknown>;
|
|
10
|
-
|
|
11
|
-
error?: unknown;
|
|
29
|
+
payload: T;
|
|
12
30
|
}
|
|
31
|
+
type Message = NexusEnvelope;
|
|
13
32
|
declare class BaseDriver {
|
|
14
33
|
onMessage: ((data: Message) => void) | null;
|
|
15
34
|
constructor();
|
|
@@ -59,11 +78,26 @@ interface LogEntry {
|
|
|
59
78
|
context?: string;
|
|
60
79
|
}
|
|
61
80
|
type LogHandler = (entry: LogEntry) => void;
|
|
62
|
-
|
|
81
|
+
interface LoggerInterface {
|
|
82
|
+
addHandler(handler: LogHandler): void;
|
|
83
|
+
setMinLevel(level: LogLevel): void;
|
|
84
|
+
enable(): void;
|
|
85
|
+
disable(): void;
|
|
86
|
+
isEnabled(): boolean;
|
|
87
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
88
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
89
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
90
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
91
|
+
}
|
|
92
|
+
declare class Logger implements LoggerInterface {
|
|
63
93
|
private handlers;
|
|
64
94
|
private context;
|
|
65
95
|
private minLevel;
|
|
66
|
-
|
|
96
|
+
private enabled;
|
|
97
|
+
constructor(context: string, minLevel?: LogLevel, enabled?: boolean);
|
|
98
|
+
enable(): void;
|
|
99
|
+
disable(): void;
|
|
100
|
+
isEnabled(): boolean;
|
|
67
101
|
addHandler(handler: LogHandler): void;
|
|
68
102
|
setMinLevel(level: LogLevel): void;
|
|
69
103
|
private shouldLog;
|
|
@@ -83,6 +117,7 @@ interface WebSocketDriverOptions {
|
|
|
83
117
|
url: string;
|
|
84
118
|
reconnect?: boolean | ReconnectOptions;
|
|
85
119
|
logger?: Logger;
|
|
120
|
+
onStatusChange?: (status: 'connecting' | 'connected' | 'disconnected' | 'error') => void;
|
|
86
121
|
}
|
|
87
122
|
declare class WebSocketDriver extends BaseDriver {
|
|
88
123
|
private url;
|
|
@@ -94,6 +129,7 @@ declare class WebSocketDriver extends BaseDriver {
|
|
|
94
129
|
private reconnectTimer;
|
|
95
130
|
private isManuallyClosed;
|
|
96
131
|
private logger;
|
|
132
|
+
private onStatusChange?;
|
|
97
133
|
constructor(options: WebSocketDriverOptions);
|
|
98
134
|
private connect;
|
|
99
135
|
private scheduleReconnect;
|
|
@@ -107,21 +143,32 @@ declare function createEmitter(): Emitter<Record<string, Message>>;
|
|
|
107
143
|
interface MessageNexusOptions {
|
|
108
144
|
instanceId?: string;
|
|
109
145
|
timeout?: number;
|
|
110
|
-
logger?:
|
|
146
|
+
logger?: LoggerInterface;
|
|
147
|
+
loggerEnabled?: boolean;
|
|
111
148
|
}
|
|
112
|
-
interface
|
|
113
|
-
|
|
114
|
-
|
|
149
|
+
interface InvokeOptions {
|
|
150
|
+
method: string;
|
|
151
|
+
params?: unknown;
|
|
115
152
|
to?: string;
|
|
116
153
|
metadata?: Record<string, unknown>;
|
|
117
154
|
timeout?: number;
|
|
118
155
|
retryCount?: number;
|
|
119
156
|
retryDelay?: number;
|
|
120
157
|
}
|
|
121
|
-
interface
|
|
122
|
-
|
|
123
|
-
|
|
158
|
+
interface NotificationOptions {
|
|
159
|
+
method: string;
|
|
160
|
+
params?: unknown;
|
|
161
|
+
to?: string;
|
|
162
|
+
metadata?: Record<string, unknown>;
|
|
163
|
+
}
|
|
164
|
+
interface InvokeContext {
|
|
165
|
+
messageId?: string;
|
|
166
|
+
from: string;
|
|
167
|
+
to?: string;
|
|
168
|
+
metadata?: Record<string, unknown>;
|
|
124
169
|
}
|
|
170
|
+
type InvokeHandler<Params = any, Result = any> = (params: Params, context: InvokeContext) => Result | Promise<Result>;
|
|
171
|
+
type NotificationHandler<Params = any> = (params: Params, context: InvokeContext) => void;
|
|
125
172
|
type ErrorHandler = (error: Error, context?: Record<string, unknown>) => void;
|
|
126
173
|
interface Metrics {
|
|
127
174
|
messagesSent: number;
|
|
@@ -133,21 +180,17 @@ interface Metrics {
|
|
|
133
180
|
averageLatency: number;
|
|
134
181
|
}
|
|
135
182
|
type MetricsCallback = (metrics: Metrics) => void;
|
|
136
|
-
declare class MessageNexus<
|
|
183
|
+
declare class MessageNexus<GlobalRequestPayload = unknown, GlobalResponsePayload = unknown> {
|
|
137
184
|
driver: BaseDriver;
|
|
138
185
|
pendingTasks: Map<string, {
|
|
139
|
-
resolve: (value:
|
|
186
|
+
resolve: (value: any) => void;
|
|
140
187
|
reject: (reason?: unknown) => void;
|
|
141
188
|
timer: ReturnType<typeof setTimeout>;
|
|
142
189
|
to?: string;
|
|
143
190
|
timestamp: number;
|
|
144
191
|
}>;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
type: string;
|
|
148
|
-
timestamp: number;
|
|
149
|
-
}>;
|
|
150
|
-
messageHandlers: Set<(data: CommandMessage) => void>;
|
|
192
|
+
invokeHandlers: Map<string, InvokeHandler>;
|
|
193
|
+
notificationHandlers: Map<string, Set<NotificationHandler>>;
|
|
151
194
|
timeout: number;
|
|
152
195
|
instanceId: string;
|
|
153
196
|
private cleanupInterval;
|
|
@@ -158,18 +201,23 @@ declare class MessageNexus<RequestPayload = unknown, ResponsePayload = unknown>
|
|
|
158
201
|
private metrics;
|
|
159
202
|
private metricsCallbacks;
|
|
160
203
|
constructor(driver: BaseDriver, options?: MessageNexusOptions);
|
|
161
|
-
|
|
204
|
+
invoke<T = GlobalResponsePayload>(methodOrOptions: string | InvokeOptions): Promise<T>;
|
|
162
205
|
private _sendMessage;
|
|
163
206
|
onError(handler: ErrorHandler): () => void;
|
|
164
207
|
flushQueue(): void;
|
|
208
|
+
notify(methodOrOptions: string | NotificationOptions): void;
|
|
165
209
|
private _validateMessage;
|
|
166
|
-
_handleIncoming(data: unknown): void
|
|
210
|
+
_handleIncoming(data: unknown): Promise<void>;
|
|
167
211
|
getMetrics(): Metrics;
|
|
168
212
|
onMetrics(callback: MetricsCallback): () => boolean;
|
|
169
213
|
private _notifyMetrics;
|
|
170
|
-
|
|
171
|
-
|
|
214
|
+
handle<Params = any, Result = any>(method: string, handler: InvokeHandler<Params, Result>): () => boolean;
|
|
215
|
+
removeHandler(method: string): void;
|
|
216
|
+
onNotification<Params = any>(method: string, handler: NotificationHandler<Params>): () => void;
|
|
217
|
+
offNotification(method: string, handler: NotificationHandler<any>): void;
|
|
218
|
+
private _reply;
|
|
219
|
+
private _replyError;
|
|
172
220
|
destroy(): void;
|
|
173
221
|
}
|
|
174
222
|
|
|
175
|
-
export { BaseDriver, BroadcastDriver, type
|
|
223
|
+
export { BaseDriver, BroadcastDriver, type ErrorHandler, type InvokeContext, type InvokeHandler, type InvokeOptions, type LoggerInterface, type Message, type MessageNexusOptions, type Metrics, type MetricsCallback, MittDriver, type NotificationHandler, type NotificationOptions, PostMessageDriver, WebSocketDriver, createEmitter, MessageNexus as default };
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var BroadcastDriver = class extends BaseDriver {
|
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
// src/drivers/MittDriver.ts
|
|
55
|
-
var eventIndicator = "
|
|
55
|
+
var eventIndicator = "message_nexus_message_event";
|
|
56
56
|
var MittDriver = class extends BaseDriver {
|
|
57
57
|
constructor(emitter) {
|
|
58
58
|
super();
|
|
@@ -121,11 +121,26 @@ var PostMessageDriver = class extends BaseDriver {
|
|
|
121
121
|
};
|
|
122
122
|
|
|
123
123
|
// src/utils/logger.ts
|
|
124
|
+
function isLogger(value) {
|
|
125
|
+
if (value == null || typeof value !== "object") return false;
|
|
126
|
+
const logger = value;
|
|
127
|
+
return typeof logger.addHandler === "function" && typeof logger.setMinLevel === "function" && typeof logger.enable === "function" && typeof logger.disable === "function" && typeof logger.isEnabled === "function" && typeof logger.debug === "function" && typeof logger.info === "function" && typeof logger.warn === "function" && typeof logger.error === "function";
|
|
128
|
+
}
|
|
124
129
|
var Logger = class {
|
|
125
|
-
constructor(context, minLevel = "info" /* INFO
|
|
130
|
+
constructor(context, minLevel = "info" /* INFO */, enabled = false) {
|
|
126
131
|
this.handlers = [];
|
|
127
132
|
this.context = context;
|
|
128
133
|
this.minLevel = minLevel;
|
|
134
|
+
this.enabled = enabled;
|
|
135
|
+
}
|
|
136
|
+
enable() {
|
|
137
|
+
this.enabled = true;
|
|
138
|
+
}
|
|
139
|
+
disable() {
|
|
140
|
+
this.enabled = false;
|
|
141
|
+
}
|
|
142
|
+
isEnabled() {
|
|
143
|
+
return this.enabled;
|
|
129
144
|
}
|
|
130
145
|
addHandler(handler) {
|
|
131
146
|
this.handlers.push(handler);
|
|
@@ -138,7 +153,7 @@ var Logger = class {
|
|
|
138
153
|
return levels.indexOf(level) >= levels.indexOf(this.minLevel);
|
|
139
154
|
}
|
|
140
155
|
log(level, message, metadata) {
|
|
141
|
-
if (!this.shouldLog(level)) return;
|
|
156
|
+
if (!this.enabled || !this.shouldLog(level)) return;
|
|
142
157
|
const entry = {
|
|
143
158
|
level,
|
|
144
159
|
timestamp: Date.now(),
|
|
@@ -161,9 +176,19 @@ var Logger = class {
|
|
|
161
176
|
this.log("error" /* ERROR */, message, metadata);
|
|
162
177
|
}
|
|
163
178
|
};
|
|
179
|
+
function genTimestamp() {
|
|
180
|
+
const now = /* @__PURE__ */ new Date();
|
|
181
|
+
return now.toLocaleTimeString("zh-CN", {
|
|
182
|
+
hour12: false,
|
|
183
|
+
hour: "2-digit",
|
|
184
|
+
minute: "2-digit",
|
|
185
|
+
second: "2-digit",
|
|
186
|
+
fractionalSecondDigits: 3
|
|
187
|
+
});
|
|
188
|
+
}
|
|
164
189
|
var createConsoleHandler = () => {
|
|
165
190
|
return (entry) => {
|
|
166
|
-
const timestamp =
|
|
191
|
+
const timestamp = genTimestamp();
|
|
167
192
|
const prefix = `[${timestamp}] [${entry.level.toUpperCase()}] [${entry.context || "app"}]`;
|
|
168
193
|
const logFn = entry.level === "debug" /* DEBUG */ ? console.debug : entry.level === "info" /* INFO */ ? console.info : entry.level === "warn" /* WARN */ ? console.warn : console.error;
|
|
169
194
|
if (entry.metadata) {
|
|
@@ -189,13 +214,16 @@ var WebSocketDriver = class extends BaseDriver {
|
|
|
189
214
|
this.retryInterval = (typeof options.reconnect === "object" ? options.reconnect.retryInterval : void 0) ?? 5e3;
|
|
190
215
|
this.logger = options.logger || new Logger("WebSocketDriver");
|
|
191
216
|
this.logger.addHandler(createConsoleHandler());
|
|
217
|
+
this.onStatusChange = options.onStatusChange;
|
|
192
218
|
this.connect();
|
|
193
219
|
}
|
|
194
220
|
connect() {
|
|
221
|
+
this.onStatusChange?.("connecting");
|
|
195
222
|
this.ws = new WebSocket(this.url);
|
|
196
223
|
this.ws.addEventListener("open", () => {
|
|
197
224
|
this.logger.info("WebSocket connected", { url: this.url });
|
|
198
225
|
this.retryCount = 0;
|
|
226
|
+
this.onStatusChange?.("connected");
|
|
199
227
|
});
|
|
200
228
|
this.ws.addEventListener("message", (event) => {
|
|
201
229
|
try {
|
|
@@ -213,6 +241,7 @@ var WebSocketDriver = class extends BaseDriver {
|
|
|
213
241
|
});
|
|
214
242
|
this.ws.addEventListener("error", (event) => {
|
|
215
243
|
this.logger.error("WebSocket error", { event });
|
|
244
|
+
this.onStatusChange?.("error");
|
|
216
245
|
});
|
|
217
246
|
this.ws.addEventListener("close", () => {
|
|
218
247
|
this.logger.info("WebSocket connection closed", {
|
|
@@ -222,6 +251,8 @@ var WebSocketDriver = class extends BaseDriver {
|
|
|
222
251
|
});
|
|
223
252
|
if (!this.isManuallyClosed && this.reconnectEnabled && this.retryCount < this.maxRetries) {
|
|
224
253
|
this.scheduleReconnect();
|
|
254
|
+
} else {
|
|
255
|
+
this.onStatusChange?.("disconnected");
|
|
225
256
|
}
|
|
226
257
|
});
|
|
227
258
|
}
|
|
@@ -234,6 +265,7 @@ var WebSocketDriver = class extends BaseDriver {
|
|
|
234
265
|
maxRetries: this.maxRetries,
|
|
235
266
|
url: this.url
|
|
236
267
|
});
|
|
268
|
+
this.onStatusChange?.("connecting");
|
|
237
269
|
this.reconnectTimer = window.setTimeout(() => {
|
|
238
270
|
this.connect();
|
|
239
271
|
}, delay);
|
|
@@ -264,6 +296,7 @@ var WebSocketDriver = class extends BaseDriver {
|
|
|
264
296
|
this.ws.close();
|
|
265
297
|
this.ws = null;
|
|
266
298
|
}
|
|
299
|
+
this.onStatusChange?.("disconnected");
|
|
267
300
|
}
|
|
268
301
|
destroy() {
|
|
269
302
|
this.close();
|
|
@@ -297,45 +330,45 @@ var MessageNexus = class {
|
|
|
297
330
|
this.driver = driver;
|
|
298
331
|
this.instanceId = options?.instanceId || crypto.randomUUID();
|
|
299
332
|
this.timeout = options?.timeout ?? 1e4;
|
|
300
|
-
|
|
301
|
-
|
|
333
|
+
if (options?.logger && isLogger(options.logger)) {
|
|
334
|
+
this.logger = options.logger;
|
|
335
|
+
} else {
|
|
336
|
+
this.logger = new Logger("MessageNexus");
|
|
337
|
+
}
|
|
338
|
+
const loggerEnabled = options?.loggerEnabled ?? false;
|
|
339
|
+
if (loggerEnabled) {
|
|
340
|
+
this.logger.enable();
|
|
341
|
+
this.logger.addHandler(createConsoleHandler());
|
|
342
|
+
this.logger.info("MessageNexus initialized", {
|
|
343
|
+
instanceId: this.instanceId,
|
|
344
|
+
timeout: this.timeout
|
|
345
|
+
});
|
|
346
|
+
}
|
|
302
347
|
this.pendingTasks = /* @__PURE__ */ new Map();
|
|
303
|
-
this.
|
|
304
|
-
this.
|
|
348
|
+
this.invokeHandlers = /* @__PURE__ */ new Map();
|
|
349
|
+
this.notificationHandlers = /* @__PURE__ */ new Map();
|
|
305
350
|
this.cleanupInterval = null;
|
|
306
351
|
this.driver.onMessage = (data) => this._handleIncoming(data);
|
|
307
|
-
this.logger.info("MessageNexus initialized", {
|
|
308
|
-
instanceId: this.instanceId,
|
|
309
|
-
timeout: this.timeout
|
|
310
|
-
});
|
|
311
|
-
this.cleanupInterval = window.setInterval(() => {
|
|
312
|
-
const now = Date.now();
|
|
313
|
-
for (const [id, msg] of this.incomingMessages.entries()) {
|
|
314
|
-
if (now - msg.timestamp > this.timeout * 2) {
|
|
315
|
-
this.incomingMessages.delete(id);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}, 6e4);
|
|
319
352
|
}
|
|
320
|
-
async
|
|
353
|
+
async invoke(methodOrOptions) {
|
|
321
354
|
const id = crypto.randomUUID();
|
|
322
|
-
let
|
|
323
|
-
let
|
|
355
|
+
let method;
|
|
356
|
+
let params;
|
|
324
357
|
let to;
|
|
325
358
|
let metadata;
|
|
326
359
|
let timeout;
|
|
327
360
|
let retryCount = 0;
|
|
328
361
|
let retryDelay = 1e3;
|
|
329
|
-
if (typeof
|
|
330
|
-
|
|
331
|
-
|
|
362
|
+
if (typeof methodOrOptions === "string") {
|
|
363
|
+
method = methodOrOptions;
|
|
364
|
+
params = void 0;
|
|
332
365
|
to = void 0;
|
|
333
366
|
metadata = {};
|
|
334
367
|
timeout = this.timeout;
|
|
335
368
|
} else {
|
|
336
|
-
const opts =
|
|
337
|
-
|
|
338
|
-
|
|
369
|
+
const opts = methodOrOptions;
|
|
370
|
+
method = opts.method;
|
|
371
|
+
params = opts.params;
|
|
339
372
|
to = opts.to;
|
|
340
373
|
metadata = opts.metadata || {};
|
|
341
374
|
timeout = opts.timeout ?? this.timeout;
|
|
@@ -348,16 +381,20 @@ var MessageNexus = class {
|
|
|
348
381
|
this.pendingTasks.delete(id);
|
|
349
382
|
this.metrics.messagesFailed++;
|
|
350
383
|
this.metrics.pendingMessages--;
|
|
351
|
-
reject(new Error(`Message timeout: ${
|
|
384
|
+
reject(new Error(`Message timeout: ${method} (${id})`));
|
|
352
385
|
}, timeout);
|
|
353
386
|
this.pendingTasks.set(id, { resolve, reject, timer, timestamp: Date.now() });
|
|
387
|
+
const rpcRequest = {
|
|
388
|
+
jsonrpc: "2.0",
|
|
389
|
+
method,
|
|
390
|
+
params,
|
|
391
|
+
id
|
|
392
|
+
};
|
|
354
393
|
const message = {
|
|
355
|
-
id,
|
|
356
|
-
type,
|
|
357
|
-
payload,
|
|
358
394
|
from: this.instanceId,
|
|
359
395
|
to,
|
|
360
|
-
metadata: { ...metadata, timestamp: Date.now() }
|
|
396
|
+
metadata: { ...metadata, timestamp: Date.now() },
|
|
397
|
+
payload: rpcRequest
|
|
361
398
|
};
|
|
362
399
|
this._sendMessage(message);
|
|
363
400
|
}).catch((error) => {
|
|
@@ -374,20 +411,26 @@ var MessageNexus = class {
|
|
|
374
411
|
return attempt(0);
|
|
375
412
|
}
|
|
376
413
|
_sendMessage(message) {
|
|
414
|
+
const payload = message.payload;
|
|
415
|
+
const isRequest = "method" in payload;
|
|
416
|
+
const messageId = "id" in payload ? String(payload.id) : void 0;
|
|
417
|
+
const typeOrMethod = isRequest ? payload.method : "RESPONSE";
|
|
377
418
|
try {
|
|
378
419
|
this.driver.send(message);
|
|
379
420
|
this.metrics.messagesSent++;
|
|
380
|
-
|
|
381
|
-
|
|
421
|
+
if (isRequest && messageId !== void 0) {
|
|
422
|
+
this.metrics.pendingMessages++;
|
|
423
|
+
}
|
|
424
|
+
this.logger.debug("Message sent", { messageId, type: typeOrMethod });
|
|
382
425
|
} catch (error) {
|
|
383
426
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
384
427
|
this.metrics.messagesFailed++;
|
|
385
|
-
this.logger.error("Failed to send message", { error: err.message, messageId
|
|
428
|
+
this.logger.error("Failed to send message", { error: err.message, messageId });
|
|
386
429
|
this.errorHandler?.(err, { message });
|
|
387
430
|
if (this.messageQueue.length < this.maxQueueSize) {
|
|
388
431
|
this.messageQueue.push(message);
|
|
389
432
|
this.logger.debug("Message queued", {
|
|
390
|
-
messageId
|
|
433
|
+
messageId,
|
|
391
434
|
queueSize: this.messageQueue.length + 1
|
|
392
435
|
});
|
|
393
436
|
} else {
|
|
@@ -420,55 +463,145 @@ var MessageNexus = class {
|
|
|
420
463
|
}
|
|
421
464
|
}
|
|
422
465
|
}
|
|
466
|
+
notify(methodOrOptions) {
|
|
467
|
+
let method;
|
|
468
|
+
let params;
|
|
469
|
+
let to;
|
|
470
|
+
let metadata;
|
|
471
|
+
if (typeof methodOrOptions === "string") {
|
|
472
|
+
method = methodOrOptions;
|
|
473
|
+
params = void 0;
|
|
474
|
+
to = void 0;
|
|
475
|
+
metadata = {};
|
|
476
|
+
} else {
|
|
477
|
+
const opts = methodOrOptions;
|
|
478
|
+
method = opts.method;
|
|
479
|
+
params = opts.params;
|
|
480
|
+
to = opts.to;
|
|
481
|
+
metadata = opts.metadata || {};
|
|
482
|
+
}
|
|
483
|
+
const rpcNotification = {
|
|
484
|
+
jsonrpc: "2.0",
|
|
485
|
+
method,
|
|
486
|
+
params
|
|
487
|
+
};
|
|
488
|
+
const message = {
|
|
489
|
+
from: this.instanceId,
|
|
490
|
+
to,
|
|
491
|
+
metadata: { ...metadata, timestamp: Date.now() },
|
|
492
|
+
payload: rpcNotification
|
|
493
|
+
};
|
|
494
|
+
this._sendMessage(message);
|
|
495
|
+
}
|
|
423
496
|
_validateMessage(data) {
|
|
424
497
|
if (!data || typeof data !== "object") return false;
|
|
425
|
-
const
|
|
426
|
-
if (typeof
|
|
427
|
-
if (typeof
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
if (
|
|
431
|
-
if (
|
|
498
|
+
const env = data;
|
|
499
|
+
if (typeof env.from !== "string") return false;
|
|
500
|
+
if (env.to !== void 0 && typeof env.to !== "string") return false;
|
|
501
|
+
if (env.metadata !== void 0 && typeof env.metadata !== "object") return false;
|
|
502
|
+
const payload = env.payload;
|
|
503
|
+
if (!payload || typeof payload !== "object") return false;
|
|
504
|
+
if (payload.jsonrpc !== "2.0") return false;
|
|
505
|
+
const isRequest = "method" in payload;
|
|
506
|
+
const isResponse = "result" in payload || "error" in payload;
|
|
507
|
+
if (!isRequest && !isResponse) return false;
|
|
432
508
|
return true;
|
|
433
509
|
}
|
|
434
|
-
_handleIncoming(data) {
|
|
510
|
+
async _handleIncoming(data) {
|
|
435
511
|
if (!this._validateMessage(data)) {
|
|
436
512
|
this.logger.error("Invalid message format received", { data });
|
|
437
513
|
this.errorHandler?.(new Error("Invalid message format received"), { data });
|
|
438
514
|
this.metrics.messagesFailed++;
|
|
439
515
|
return;
|
|
440
516
|
}
|
|
441
|
-
const
|
|
442
|
-
|
|
517
|
+
const envelope = data;
|
|
518
|
+
const payload = envelope.payload;
|
|
519
|
+
if (envelope.to && envelope.to !== this.instanceId) {
|
|
443
520
|
this.logger.debug("Message filtered: not for this instance", {
|
|
444
|
-
messageId: id,
|
|
445
|
-
to,
|
|
521
|
+
messageId: "id" in payload ? payload.id : void 0,
|
|
522
|
+
to: envelope.to,
|
|
446
523
|
instanceId: this.instanceId
|
|
447
524
|
});
|
|
448
525
|
return;
|
|
449
526
|
}
|
|
450
|
-
if (
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
this.pendingTasks.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
527
|
+
if ("result" in payload || "error" in payload) {
|
|
528
|
+
const response = payload;
|
|
529
|
+
const id = String(response.id);
|
|
530
|
+
if (this.pendingTasks.has(id)) {
|
|
531
|
+
const { resolve, reject, timer, timestamp } = this.pendingTasks.get(id);
|
|
532
|
+
clearTimeout(timer);
|
|
533
|
+
this.pendingTasks.delete(id);
|
|
534
|
+
const latency = Date.now() - timestamp;
|
|
535
|
+
this.metrics.messagesReceived++;
|
|
536
|
+
this.metrics.pendingMessages--;
|
|
537
|
+
this.metrics.totalLatency += latency;
|
|
538
|
+
this.metrics.averageLatency = this.metrics.totalLatency / this.metrics.messagesReceived;
|
|
539
|
+
this.logger.debug("Response received", { messageId: id, latency });
|
|
540
|
+
if (response.error) {
|
|
541
|
+
const err = new Error(response.error.message);
|
|
542
|
+
err.code = response.error.code;
|
|
543
|
+
err.data = response.error.data;
|
|
544
|
+
reject(err);
|
|
545
|
+
} else {
|
|
546
|
+
resolve(response.result);
|
|
547
|
+
}
|
|
548
|
+
this._notifyMetrics();
|
|
549
|
+
} else {
|
|
550
|
+
this.logger.warn("Orphaned response received", { messageId: id });
|
|
551
|
+
}
|
|
463
552
|
return;
|
|
464
553
|
}
|
|
465
|
-
if (
|
|
466
|
-
|
|
467
|
-
|
|
554
|
+
if ("method" in payload) {
|
|
555
|
+
if ("id" in payload) {
|
|
556
|
+
const request = payload;
|
|
557
|
+
const id = String(request.id);
|
|
558
|
+
this.logger.debug("Invoke message received", {
|
|
559
|
+
messageId: id,
|
|
560
|
+
type: request.method,
|
|
561
|
+
from: envelope.from
|
|
562
|
+
});
|
|
563
|
+
const context = {
|
|
564
|
+
messageId: id,
|
|
565
|
+
from: envelope.from,
|
|
566
|
+
to: envelope.to,
|
|
567
|
+
metadata: envelope.metadata
|
|
568
|
+
};
|
|
569
|
+
const handler = this.invokeHandlers.get(request.method);
|
|
570
|
+
if (handler) {
|
|
571
|
+
try {
|
|
572
|
+
const result = await handler(request.params, context);
|
|
573
|
+
this._reply(id, envelope.from, result);
|
|
574
|
+
} catch (error) {
|
|
575
|
+
this._replyError(id, envelope.from, error);
|
|
576
|
+
}
|
|
577
|
+
} else {
|
|
578
|
+
const err = new Error(`Method not found: ${request.method}`);
|
|
579
|
+
err.code = -32601;
|
|
580
|
+
this._replyError(id, envelope.from, err);
|
|
581
|
+
}
|
|
582
|
+
} else {
|
|
583
|
+
const notification = payload;
|
|
584
|
+
this.logger.debug("Notification message received", {
|
|
585
|
+
type: notification.method,
|
|
586
|
+
from: envelope.from
|
|
587
|
+
});
|
|
588
|
+
const context = {
|
|
589
|
+
from: envelope.from,
|
|
590
|
+
to: envelope.to,
|
|
591
|
+
metadata: envelope.metadata
|
|
592
|
+
};
|
|
593
|
+
const handlers = this.notificationHandlers.get(notification.method);
|
|
594
|
+
if (handlers) {
|
|
595
|
+
handlers.forEach((handler) => {
|
|
596
|
+
try {
|
|
597
|
+
handler(notification.params, context);
|
|
598
|
+
} catch (error) {
|
|
599
|
+
this.logger.error("Error in notification handler", { error: String(error) });
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
468
604
|
}
|
|
469
|
-
this.logger.debug("Command message received", { messageId: id, type, from });
|
|
470
|
-
this.incomingMessages.set(id, { from, type, timestamp: Date.now() });
|
|
471
|
-
this.messageHandlers.forEach((handler) => handler(data));
|
|
472
605
|
}
|
|
473
606
|
getMetrics() {
|
|
474
607
|
return { ...this.metrics, pendingMessages: this.pendingTasks.size };
|
|
@@ -481,27 +614,62 @@ var MessageNexus = class {
|
|
|
481
614
|
const metrics = this.getMetrics();
|
|
482
615
|
this.metricsCallbacks.forEach((callback) => callback(metrics));
|
|
483
616
|
}
|
|
484
|
-
|
|
485
|
-
this.
|
|
486
|
-
|
|
617
|
+
handle(method, handler) {
|
|
618
|
+
if (this.invokeHandlers.has(method)) {
|
|
619
|
+
this.logger.warn(`Overriding existing handler for method: ${method}`);
|
|
620
|
+
}
|
|
621
|
+
this.invokeHandlers.set(method, handler);
|
|
622
|
+
return () => this.invokeHandlers.delete(method);
|
|
623
|
+
}
|
|
624
|
+
removeHandler(method) {
|
|
625
|
+
this.invokeHandlers.delete(method);
|
|
487
626
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
627
|
+
onNotification(method, handler) {
|
|
628
|
+
if (!this.notificationHandlers.has(method)) {
|
|
629
|
+
this.notificationHandlers.set(method, /* @__PURE__ */ new Set());
|
|
630
|
+
}
|
|
631
|
+
this.notificationHandlers.get(method).add(handler);
|
|
632
|
+
return () => this.offNotification(method, handler);
|
|
633
|
+
}
|
|
634
|
+
offNotification(method, handler) {
|
|
635
|
+
const handlers = this.notificationHandlers.get(method);
|
|
636
|
+
if (handlers) {
|
|
637
|
+
handlers.delete(handler);
|
|
638
|
+
if (handlers.size === 0) {
|
|
639
|
+
this.notificationHandlers.delete(method);
|
|
640
|
+
}
|
|
492
641
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
642
|
+
}
|
|
643
|
+
_reply(messageId, to, payload) {
|
|
644
|
+
const rpcResponse = {
|
|
645
|
+
jsonrpc: "2.0",
|
|
496
646
|
id: messageId,
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
isResponse: true,
|
|
647
|
+
result: payload
|
|
648
|
+
};
|
|
649
|
+
const message = {
|
|
501
650
|
from: this.instanceId,
|
|
502
|
-
to
|
|
503
|
-
|
|
504
|
-
|
|
651
|
+
to,
|
|
652
|
+
payload: rpcResponse
|
|
653
|
+
};
|
|
654
|
+
this.driver.send(message);
|
|
655
|
+
}
|
|
656
|
+
_replyError(messageId, to, error) {
|
|
657
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
658
|
+
const rpcResponse = {
|
|
659
|
+
jsonrpc: "2.0",
|
|
660
|
+
id: messageId,
|
|
661
|
+
error: {
|
|
662
|
+
code: err.code || -32e3,
|
|
663
|
+
message: err.message,
|
|
664
|
+
data: err.data
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
const message = {
|
|
668
|
+
from: this.instanceId,
|
|
669
|
+
to,
|
|
670
|
+
payload: rpcResponse
|
|
671
|
+
};
|
|
672
|
+
this.driver.send(message);
|
|
505
673
|
}
|
|
506
674
|
destroy() {
|
|
507
675
|
this.logger.info("MessageNexus destroying", {
|
|
@@ -515,7 +683,8 @@ var MessageNexus = class {
|
|
|
515
683
|
clearInterval(this.cleanupInterval);
|
|
516
684
|
this.cleanupInterval = null;
|
|
517
685
|
}
|
|
518
|
-
this.
|
|
686
|
+
this.invokeHandlers.clear();
|
|
687
|
+
this.notificationHandlers.clear();
|
|
519
688
|
this.metricsCallbacks.clear();
|
|
520
689
|
}
|
|
521
690
|
};
|