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/dist/index.d.ts CHANGED
@@ -1,15 +1,34 @@
1
1
  import { Emitter } from 'mitt';
2
2
 
3
- interface Message {
4
- id: string;
5
- type: string;
6
- payload?: unknown;
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
- isResponse?: boolean;
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
- declare class Logger {
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
- constructor(context: string, minLevel?: LogLevel);
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?: Logger;
146
+ logger?: LoggerInterface;
147
+ loggerEnabled?: boolean;
111
148
  }
112
- interface RequestOptions {
113
- type: string;
114
- payload?: unknown;
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 CommandMessage extends Message {
122
- type: string;
123
- payload?: unknown;
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<RequestPayload = unknown, ResponsePayload = unknown> {
183
+ declare class MessageNexus<GlobalRequestPayload = unknown, GlobalResponsePayload = unknown> {
137
184
  driver: BaseDriver;
138
185
  pendingTasks: Map<string, {
139
- resolve: (value: ResponsePayload) => void;
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
- incomingMessages: Map<string, {
146
- from?: string;
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
- request(typeOrOptions: string | RequestOptions): Promise<ResponsePayload>;
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
- onCommand(handler: (data: CommandMessage) => void): () => boolean;
171
- reply(messageId: string, payload: unknown, error?: unknown): void;
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 CommandMessage, type ErrorHandler, type Message, type MessageNexusOptions, type Metrics, type MetricsCallback, MittDriver, PostMessageDriver, type RequestOptions, WebSocketDriver, createEmitter, MessageNexus as default };
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 = "message_bridge_message_event";
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 = new Date(entry.timestamp).toISOString();
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
- this.logger = options?.logger || new Logger("MessageNexus");
301
- this.logger.addHandler(createConsoleHandler());
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.incomingMessages = /* @__PURE__ */ new Map();
304
- this.messageHandlers = /* @__PURE__ */ new Set();
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 request(typeOrOptions) {
353
+ async invoke(methodOrOptions) {
321
354
  const id = crypto.randomUUID();
322
- let type;
323
- let payload;
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 typeOrOptions === "string") {
330
- type = typeOrOptions;
331
- payload = void 0;
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 = typeOrOptions;
337
- type = opts.type;
338
- payload = opts.payload;
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: ${type} (${id})`));
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
- this.metrics.pendingMessages++;
381
- this.logger.debug("Message sent", { messageId: message.id, type: message.type });
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: message.id });
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: message.id,
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 msg = data;
426
- if (typeof msg.id !== "string") return false;
427
- if (typeof msg.type !== "string") return false;
428
- if (msg.from && typeof msg.from !== "string") return false;
429
- if (msg.to && typeof msg.to !== "string") return false;
430
- if (msg.metadata && typeof msg.metadata !== "object") return false;
431
- if (msg.isResponse !== void 0 && typeof msg.isResponse !== "boolean") return false;
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 { id, to, type, payload, isResponse, error, from } = data;
442
- if (to && to !== this.instanceId) {
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 (isResponse && this.pendingTasks.has(id)) {
451
- const { resolve, reject, timer, timestamp } = this.pendingTasks.get(id);
452
- clearTimeout(timer);
453
- this.pendingTasks.delete(id);
454
- const latency = Date.now() - timestamp;
455
- this.metrics.messagesReceived++;
456
- this.metrics.pendingMessages--;
457
- this.metrics.totalLatency += latency;
458
- this.metrics.averageLatency = this.metrics.totalLatency / this.metrics.messagesReceived;
459
- this.logger.debug("Response received", { messageId: id, latency });
460
- if (error) reject(error);
461
- else resolve(payload);
462
- this._notifyMetrics();
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 (isResponse) {
466
- this.logger.warn("Orphaned response received", { messageId: id });
467
- return;
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
- onCommand(handler) {
485
- this.messageHandlers.add(handler);
486
- return () => this.messageHandlers.delete(handler);
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
- reply(messageId, payload, error) {
489
- const incoming = this.incomingMessages.get(messageId);
490
- if (!incoming) {
491
- throw new Error(`Message not found: ${messageId}`);
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
- const responsePayload = payload;
494
- const responseError = error;
495
- this.driver.send({
642
+ }
643
+ _reply(messageId, to, payload) {
644
+ const rpcResponse = {
645
+ jsonrpc: "2.0",
496
646
  id: messageId,
497
- type: `${incoming.type}_RESPONSE`,
498
- payload: responsePayload,
499
- error: responseError,
500
- isResponse: true,
647
+ result: payload
648
+ };
649
+ const message = {
501
650
  from: this.instanceId,
502
- to: incoming.from
503
- });
504
- this.incomingMessages.delete(messageId);
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.messageHandlers.clear();
686
+ this.invokeHandlers.clear();
687
+ this.notificationHandlers.clear();
519
688
  this.metricsCallbacks.clear();
520
689
  }
521
690
  };