message-nexus 1.1.0 → 1.1.2

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.cjs CHANGED
@@ -1,741 +1 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- BaseDriver: () => BaseDriver,
34
- BroadcastDriver: () => BroadcastDriver,
35
- MittDriver: () => MittDriver,
36
- PostMessageDriver: () => PostMessageDriver,
37
- WebSocketDriver: () => WebSocketDriver,
38
- createEmitter: () => createEmitter,
39
- default: () => MessageNexus
40
- });
41
- module.exports = __toCommonJS(index_exports);
42
-
43
- // src/drivers/BaseDriver.ts
44
- var BaseDriver = class {
45
- constructor() {
46
- this.onMessage = null;
47
- }
48
- send(data) {
49
- throw new Error("Not implemented");
50
- }
51
- destroy() {
52
- }
53
- };
54
-
55
- // src/drivers/BroadcastDriver.ts
56
- var MESSAGE_NEXUS_PROTOCOL = "message-nexus-v1";
57
- function isBridgeMessage(data) {
58
- return typeof data === "object" && data !== null && "__messageBridge" in data && data.__messageBridge === MESSAGE_NEXUS_PROTOCOL;
59
- }
60
- var BroadcastDriver = class extends BaseDriver {
61
- constructor(options) {
62
- super();
63
- this.messageHandler = null;
64
- if (!options.channel) {
65
- throw new Error("BroadcastDriver requires a channel name");
66
- }
67
- this.channel = new BroadcastChannel(options.channel);
68
- this.messageHandler = (event) => {
69
- if (!isBridgeMessage(event.data)) {
70
- return;
71
- }
72
- const { __messageBridge, ...message } = event.data;
73
- this.onMessage?.(message);
74
- };
75
- this.channel.addEventListener("message", this.messageHandler);
76
- }
77
- send(data) {
78
- const bridgeMessage = {
79
- ...data,
80
- __messageBridge: MESSAGE_NEXUS_PROTOCOL
81
- };
82
- this.channel.postMessage(bridgeMessage);
83
- }
84
- destroy() {
85
- if (this.channel) {
86
- this.channel.close();
87
- }
88
- if (this.messageHandler) {
89
- this.channel.removeEventListener("message", this.messageHandler);
90
- this.messageHandler = null;
91
- }
92
- this.onMessage = null;
93
- }
94
- };
95
-
96
- // src/drivers/MittDriver.ts
97
- var eventIndicator = "message_nexus_message_event";
98
- var MittDriver = class extends BaseDriver {
99
- constructor(emitter) {
100
- super();
101
- this.emitter = emitter;
102
- this.listener = () => {
103
- };
104
- const handler = (data) => {
105
- if (data) this.onMessage?.(data);
106
- };
107
- this.emitter.on(eventIndicator, handler);
108
- this.listener = () => {
109
- this.emitter.off(eventIndicator, handler);
110
- };
111
- }
112
- send(data) {
113
- this.emitter.emit(eventIndicator, data);
114
- }
115
- destroy() {
116
- this.listener();
117
- this.onMessage = null;
118
- }
119
- };
120
-
121
- // src/drivers/PostMessageDriver.ts
122
- var MESSAGE_NEXUS_PROTOCOL2 = "message-nexus-v1";
123
- function isBridgeMessage2(data) {
124
- return typeof data === "object" && data !== null && "__messageBridge" in data && data.__messageBridge === MESSAGE_NEXUS_PROTOCOL2;
125
- }
126
- var PostMessageDriver = class extends BaseDriver {
127
- constructor(targetWindow, targetOrigin) {
128
- super();
129
- this.messageHandler = null;
130
- if (!targetOrigin || targetOrigin === "*") {
131
- throw new Error(
132
- 'PostMessageDriver requires explicit targetOrigin for security. Do not use "*" as it allows any origin.'
133
- );
134
- }
135
- this.targetWindow = targetWindow;
136
- this.targetOrigin = targetOrigin;
137
- this.messageHandler = (event) => {
138
- if (event.origin !== this.targetOrigin) {
139
- return;
140
- }
141
- if (!isBridgeMessage2(event.data)) {
142
- return;
143
- }
144
- const { __messageBridge, ...message } = event.data;
145
- this.onMessage?.(message);
146
- };
147
- window.addEventListener("message", this.messageHandler);
148
- }
149
- send(data) {
150
- const bridgeMessage = {
151
- ...data,
152
- __messageBridge: MESSAGE_NEXUS_PROTOCOL2
153
- };
154
- this.targetWindow.postMessage(bridgeMessage, this.targetOrigin);
155
- }
156
- destroy() {
157
- if (this.messageHandler) {
158
- window.removeEventListener("message", this.messageHandler);
159
- this.messageHandler = null;
160
- }
161
- this.onMessage = null;
162
- }
163
- };
164
-
165
- // src/utils/logger.ts
166
- function isLogger(value) {
167
- if (value == null || typeof value !== "object") return false;
168
- const logger = value;
169
- 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";
170
- }
171
- var Logger = class {
172
- constructor(context, minLevel = "info" /* INFO */, enabled = false) {
173
- this.handlers = [];
174
- this.context = context;
175
- this.minLevel = minLevel;
176
- this.enabled = enabled;
177
- }
178
- enable() {
179
- this.enabled = true;
180
- }
181
- disable() {
182
- this.enabled = false;
183
- }
184
- isEnabled() {
185
- return this.enabled;
186
- }
187
- addHandler(handler) {
188
- this.handlers.push(handler);
189
- }
190
- setMinLevel(level) {
191
- this.minLevel = level;
192
- }
193
- shouldLog(level) {
194
- const levels = ["debug" /* DEBUG */, "info" /* INFO */, "warn" /* WARN */, "error" /* ERROR */];
195
- return levels.indexOf(level) >= levels.indexOf(this.minLevel);
196
- }
197
- log(level, message, metadata) {
198
- if (!this.enabled || !this.shouldLog(level)) return;
199
- const entry = {
200
- level,
201
- timestamp: Date.now(),
202
- message,
203
- metadata,
204
- context: this.context
205
- };
206
- this.handlers.forEach((handler) => handler(entry));
207
- }
208
- debug(message, metadata) {
209
- this.log("debug" /* DEBUG */, message, metadata);
210
- }
211
- info(message, metadata) {
212
- this.log("info" /* INFO */, message, metadata);
213
- }
214
- warn(message, metadata) {
215
- this.log("warn" /* WARN */, message, metadata);
216
- }
217
- error(message, metadata) {
218
- this.log("error" /* ERROR */, message, metadata);
219
- }
220
- };
221
- function genTimestamp() {
222
- const now = /* @__PURE__ */ new Date();
223
- return now.toLocaleTimeString("zh-CN", {
224
- hour12: false,
225
- hour: "2-digit",
226
- minute: "2-digit",
227
- second: "2-digit",
228
- fractionalSecondDigits: 3
229
- });
230
- }
231
- var createConsoleHandler = () => {
232
- return (entry) => {
233
- const timestamp = genTimestamp();
234
- const prefix = `[${timestamp}] [${entry.level.toUpperCase()}] [${entry.context || "app"}]`;
235
- const logFn = entry.level === "debug" /* DEBUG */ ? console.debug : entry.level === "info" /* INFO */ ? console.info : entry.level === "warn" /* WARN */ ? console.warn : console.error;
236
- if (entry.metadata) {
237
- logFn(prefix, entry.message, entry.metadata);
238
- } else {
239
- logFn(prefix, entry.message);
240
- }
241
- };
242
- };
243
-
244
- // src/drivers/WebSocktDriver.ts
245
- var MESSAGE_NEXUS_PROTOCOL3 = "message-nexus-v1";
246
- var WebSocketDriver = class extends BaseDriver {
247
- constructor(options) {
248
- super();
249
- this.ws = null;
250
- this.retryCount = 0;
251
- this.reconnectTimer = null;
252
- this.isManuallyClosed = false;
253
- this.url = options.url;
254
- this.reconnectEnabled = options.reconnect !== false;
255
- this.maxRetries = (typeof options.reconnect === "object" ? options.reconnect.maxRetries : void 0) ?? Infinity;
256
- this.retryInterval = (typeof options.reconnect === "object" ? options.reconnect.retryInterval : void 0) ?? 5e3;
257
- this.logger = options.logger || new Logger("WebSocketDriver");
258
- this.logger.addHandler(createConsoleHandler());
259
- this.onStatusChange = options.onStatusChange;
260
- this.connect();
261
- }
262
- connect() {
263
- this.onStatusChange?.("connecting");
264
- this.ws = new WebSocket(this.url);
265
- this.ws.addEventListener("open", () => {
266
- this.logger.info("WebSocket connected", { url: this.url });
267
- this.retryCount = 0;
268
- this.onStatusChange?.("connected");
269
- });
270
- this.ws.addEventListener("message", (event) => {
271
- try {
272
- const rawData = JSON.parse(event.data);
273
- if (typeof rawData === "object" && rawData !== null && "__messageBridge" in rawData && rawData.__messageBridge === MESSAGE_NEXUS_PROTOCOL3) {
274
- const { __messageBridge, ...data } = rawData;
275
- this.logger.debug("Message received", { data });
276
- this.onMessage?.(data);
277
- } else {
278
- this.logger.debug("Ignored non-bridge message", { data: rawData });
279
- }
280
- } catch (error) {
281
- this.logger.error("Failed to parse WebSocket message", { error, data: event.data });
282
- }
283
- });
284
- this.ws.addEventListener("error", (event) => {
285
- this.logger.error("WebSocket error", { event });
286
- this.onStatusChange?.("error");
287
- });
288
- this.ws.addEventListener("close", () => {
289
- this.logger.info("WebSocket connection closed", {
290
- manuallyClosed: this.isManuallyClosed,
291
- retryCount: this.retryCount,
292
- maxRetries: this.maxRetries
293
- });
294
- if (!this.isManuallyClosed && this.reconnectEnabled && this.retryCount < this.maxRetries) {
295
- this.scheduleReconnect();
296
- } else {
297
- this.onStatusChange?.("disconnected");
298
- }
299
- });
300
- }
301
- scheduleReconnect() {
302
- this.retryCount++;
303
- const delay = this.retryInterval * this.retryCount;
304
- this.logger.info("Reconnecting scheduled", {
305
- delay,
306
- attempt: this.retryCount,
307
- maxRetries: this.maxRetries,
308
- url: this.url
309
- });
310
- this.onStatusChange?.("connecting");
311
- this.reconnectTimer = window.setTimeout(() => {
312
- this.connect();
313
- }, delay);
314
- }
315
- send(data) {
316
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
317
- this.logger.error("WebSocket is not open", {
318
- state: this.ws?.readyState,
319
- url: this.url
320
- });
321
- throw new Error("WebSocket is not open");
322
- }
323
- this.logger.debug("Sending message", { data });
324
- const bridgeMessage = {
325
- ...data,
326
- __messageBridge: MESSAGE_NEXUS_PROTOCOL3
327
- };
328
- this.ws.send(JSON.stringify(bridgeMessage));
329
- }
330
- close() {
331
- this.logger.info("Closing WebSocket connection", { url: this.url });
332
- this.isManuallyClosed = true;
333
- if (this.reconnectTimer) {
334
- clearTimeout(this.reconnectTimer);
335
- this.reconnectTimer = null;
336
- }
337
- if (this.ws) {
338
- this.ws.close();
339
- this.ws = null;
340
- }
341
- this.onStatusChange?.("disconnected");
342
- }
343
- destroy() {
344
- this.close();
345
- this.onMessage = null;
346
- }
347
- };
348
-
349
- // src/utils/emitter.ts
350
- var import_mitt = __toESM(require("mitt"), 1);
351
- function createEmitter() {
352
- return (0, import_mitt.default)();
353
- }
354
-
355
- // src/index.ts
356
- var MessageNexus = class {
357
- constructor(driver, options) {
358
- this.cleanupInterval = null;
359
- this.messageQueue = [];
360
- this.maxQueueSize = 100;
361
- this.errorHandler = null;
362
- this.metrics = {
363
- messagesSent: 0,
364
- messagesReceived: 0,
365
- messagesFailed: 0,
366
- pendingMessages: 0,
367
- queuedMessages: 0,
368
- totalLatency: 0,
369
- averageLatency: 0
370
- };
371
- this.metricsCallbacks = /* @__PURE__ */ new Set();
372
- this.driver = driver;
373
- this.instanceId = options?.instanceId || crypto.randomUUID();
374
- this.timeout = options?.timeout ?? 1e4;
375
- if (options?.logger && isLogger(options.logger)) {
376
- this.logger = options.logger;
377
- } else {
378
- this.logger = new Logger("MessageNexus");
379
- }
380
- const loggerEnabled = options?.loggerEnabled ?? false;
381
- if (loggerEnabled) {
382
- this.logger.enable();
383
- this.logger.addHandler(createConsoleHandler());
384
- this.logger.info("MessageNexus initialized", {
385
- instanceId: this.instanceId,
386
- timeout: this.timeout
387
- });
388
- }
389
- this.pendingTasks = /* @__PURE__ */ new Map();
390
- this.invokeHandlers = /* @__PURE__ */ new Map();
391
- this.notificationHandlers = /* @__PURE__ */ new Map();
392
- this.cleanupInterval = null;
393
- this.driver.onMessage = (data) => this._handleIncoming(data);
394
- }
395
- async invoke(methodOrOptions) {
396
- const id = crypto.randomUUID();
397
- let method;
398
- let params;
399
- let to;
400
- let metadata;
401
- let timeout;
402
- let retryCount = 0;
403
- let retryDelay = 1e3;
404
- if (typeof methodOrOptions === "string") {
405
- method = methodOrOptions;
406
- params = void 0;
407
- to = void 0;
408
- metadata = {};
409
- timeout = this.timeout;
410
- } else {
411
- const opts = methodOrOptions;
412
- method = opts.method;
413
- params = opts.params;
414
- to = opts.to;
415
- metadata = opts.metadata || {};
416
- timeout = opts.timeout ?? this.timeout;
417
- retryCount = opts.retryCount ?? 0;
418
- retryDelay = opts.retryDelay ?? 1e3;
419
- }
420
- const attempt = async (attemptNumber) => {
421
- return new Promise((resolve, reject) => {
422
- const timer = setTimeout(() => {
423
- this.pendingTasks.delete(id);
424
- this.metrics.messagesFailed++;
425
- this.metrics.pendingMessages--;
426
- reject(new Error(`Message timeout: ${method} (${id})`));
427
- }, timeout);
428
- this.pendingTasks.set(id, { resolve, reject, timer, timestamp: Date.now() });
429
- const rpcRequest = {
430
- jsonrpc: "2.0",
431
- method,
432
- params,
433
- id
434
- };
435
- const message = {
436
- from: this.instanceId,
437
- to,
438
- metadata: { ...metadata, timestamp: Date.now() },
439
- payload: rpcRequest
440
- };
441
- this._sendMessage(message);
442
- }).catch((error) => {
443
- if (attemptNumber < retryCount) {
444
- return new Promise(
445
- (resolve) => setTimeout(() => resolve(attempt(attemptNumber + 1)), retryDelay * (attemptNumber + 1))
446
- );
447
- }
448
- this.metrics.messagesFailed++;
449
- this.metrics.pendingMessages--;
450
- throw error;
451
- });
452
- };
453
- return attempt(0);
454
- }
455
- _sendMessage(message) {
456
- const payload = message.payload;
457
- const isRequest = "method" in payload;
458
- const messageId = "id" in payload ? String(payload.id) : void 0;
459
- const typeOrMethod = isRequest ? payload.method : "RESPONSE";
460
- try {
461
- this.driver.send(message);
462
- this.metrics.messagesSent++;
463
- if (isRequest && messageId !== void 0) {
464
- this.metrics.pendingMessages++;
465
- }
466
- this.logger.debug("Message sent", { messageId, type: typeOrMethod });
467
- } catch (error) {
468
- const err = error instanceof Error ? error : new Error(String(error));
469
- this.metrics.messagesFailed++;
470
- this.logger.error("Failed to send message", { error: err.message, messageId });
471
- this.errorHandler?.(err, { message });
472
- if (this.messageQueue.length < this.maxQueueSize) {
473
- this.messageQueue.push(message);
474
- this.logger.debug("Message queued", {
475
- messageId,
476
- queueSize: this.messageQueue.length + 1
477
- });
478
- } else {
479
- this.logger.warn("Message queue full, dropping oldest message", {
480
- queueSize: this.messageQueue.length
481
- });
482
- this.messageQueue.shift();
483
- this.messageQueue.push(message);
484
- }
485
- }
486
- this.metrics.queuedMessages = this.messageQueue.length;
487
- this._notifyMetrics();
488
- }
489
- onError(handler) {
490
- this.errorHandler = handler;
491
- return () => {
492
- this.errorHandler = null;
493
- };
494
- }
495
- flushQueue() {
496
- while (this.messageQueue.length > 0) {
497
- const message = this.messageQueue.shift();
498
- if (message) {
499
- try {
500
- this.driver.send(message);
501
- } catch (error) {
502
- this.messageQueue.unshift(message);
503
- break;
504
- }
505
- }
506
- }
507
- }
508
- notify(methodOrOptions) {
509
- let method;
510
- let params;
511
- let to;
512
- let metadata;
513
- if (typeof methodOrOptions === "string") {
514
- method = methodOrOptions;
515
- params = void 0;
516
- to = void 0;
517
- metadata = {};
518
- } else {
519
- const opts = methodOrOptions;
520
- method = opts.method;
521
- params = opts.params;
522
- to = opts.to;
523
- metadata = opts.metadata || {};
524
- }
525
- const rpcNotification = {
526
- jsonrpc: "2.0",
527
- method,
528
- params
529
- };
530
- const message = {
531
- from: this.instanceId,
532
- to,
533
- metadata: { ...metadata, timestamp: Date.now() },
534
- payload: rpcNotification
535
- };
536
- this._sendMessage(message);
537
- }
538
- _validateMessage(data) {
539
- if (!data || typeof data !== "object") return false;
540
- const env = data;
541
- if (typeof env.from !== "string") return false;
542
- if (env.to !== void 0 && typeof env.to !== "string") return false;
543
- if (env.metadata !== void 0 && typeof env.metadata !== "object") return false;
544
- const payload = env.payload;
545
- if (!payload || typeof payload !== "object") return false;
546
- if (payload.jsonrpc !== "2.0") return false;
547
- const isRequest = "method" in payload;
548
- const isResponse = "result" in payload || "error" in payload;
549
- if (!isRequest && !isResponse) return false;
550
- return true;
551
- }
552
- async _handleIncoming(data) {
553
- if (!this._validateMessage(data)) {
554
- this.logger.error("Invalid message format received", { data });
555
- this.errorHandler?.(new Error("Invalid message format received"), { data });
556
- this.metrics.messagesFailed++;
557
- return;
558
- }
559
- const envelope = data;
560
- const payload = envelope.payload;
561
- if (envelope.to && envelope.to !== this.instanceId) {
562
- this.logger.debug("Message filtered: not for this instance", {
563
- messageId: "id" in payload ? payload.id : void 0,
564
- to: envelope.to,
565
- instanceId: this.instanceId
566
- });
567
- return;
568
- }
569
- if ("result" in payload || "error" in payload) {
570
- const response = payload;
571
- const id = String(response.id);
572
- if (this.pendingTasks.has(id)) {
573
- const { resolve, reject, timer, timestamp } = this.pendingTasks.get(id);
574
- clearTimeout(timer);
575
- this.pendingTasks.delete(id);
576
- const latency = Date.now() - timestamp;
577
- this.metrics.messagesReceived++;
578
- this.metrics.pendingMessages--;
579
- this.metrics.totalLatency += latency;
580
- this.metrics.averageLatency = this.metrics.totalLatency / this.metrics.messagesReceived;
581
- this.logger.debug("Response received", { messageId: id, latency });
582
- if (response.error) {
583
- const err = new Error(response.error.message);
584
- err.code = response.error.code;
585
- err.data = response.error.data;
586
- reject(err);
587
- } else {
588
- resolve(response.result);
589
- }
590
- this._notifyMetrics();
591
- } else {
592
- this.logger.warn("Orphaned response received", { messageId: id });
593
- }
594
- return;
595
- }
596
- if ("method" in payload) {
597
- if ("id" in payload) {
598
- const request = payload;
599
- const id = String(request.id);
600
- this.logger.debug("Invoke message received", {
601
- messageId: id,
602
- type: request.method,
603
- from: envelope.from
604
- });
605
- const context = {
606
- messageId: id,
607
- from: envelope.from,
608
- to: envelope.to,
609
- metadata: envelope.metadata
610
- };
611
- const handler = this.invokeHandlers.get(request.method);
612
- if (handler) {
613
- try {
614
- const result = await handler(request.params, context);
615
- this._reply(id, envelope.from, result);
616
- } catch (error) {
617
- this._replyError(id, envelope.from, error);
618
- }
619
- } else {
620
- const err = new Error(`Method not found: ${request.method}`);
621
- err.code = -32601;
622
- this._replyError(id, envelope.from, err);
623
- }
624
- } else {
625
- const notification = payload;
626
- this.logger.debug("Notification message received", {
627
- type: notification.method,
628
- from: envelope.from
629
- });
630
- const context = {
631
- from: envelope.from,
632
- to: envelope.to,
633
- metadata: envelope.metadata
634
- };
635
- const handlers = this.notificationHandlers.get(notification.method);
636
- if (handlers) {
637
- handlers.forEach((handler) => {
638
- try {
639
- handler(notification.params, context);
640
- } catch (error) {
641
- this.logger.error("Error in notification handler", { error: String(error) });
642
- }
643
- });
644
- }
645
- }
646
- }
647
- }
648
- getMetrics() {
649
- return { ...this.metrics, pendingMessages: this.pendingTasks.size };
650
- }
651
- onMetrics(callback) {
652
- this.metricsCallbacks.add(callback);
653
- return () => this.metricsCallbacks.delete(callback);
654
- }
655
- _notifyMetrics() {
656
- const metrics = this.getMetrics();
657
- this.metricsCallbacks.forEach((callback) => callback(metrics));
658
- }
659
- handle(method, handler) {
660
- if (this.invokeHandlers.has(method)) {
661
- this.logger.warn(`Overriding existing handler for method: ${method}`);
662
- }
663
- this.invokeHandlers.set(method, handler);
664
- return () => this.invokeHandlers.delete(method);
665
- }
666
- removeHandler(method) {
667
- this.invokeHandlers.delete(method);
668
- }
669
- onNotification(method, handler) {
670
- if (!this.notificationHandlers.has(method)) {
671
- this.notificationHandlers.set(method, /* @__PURE__ */ new Set());
672
- }
673
- this.notificationHandlers.get(method).add(handler);
674
- return () => this.offNotification(method, handler);
675
- }
676
- offNotification(method, handler) {
677
- const handlers = this.notificationHandlers.get(method);
678
- if (handlers) {
679
- handlers.delete(handler);
680
- if (handlers.size === 0) {
681
- this.notificationHandlers.delete(method);
682
- }
683
- }
684
- }
685
- _reply(messageId, to, payload) {
686
- const rpcResponse = {
687
- jsonrpc: "2.0",
688
- id: messageId,
689
- result: payload
690
- };
691
- const message = {
692
- from: this.instanceId,
693
- to,
694
- payload: rpcResponse
695
- };
696
- this.driver.send(message);
697
- }
698
- _replyError(messageId, to, error) {
699
- const err = error instanceof Error ? error : new Error(String(error));
700
- const rpcResponse = {
701
- jsonrpc: "2.0",
702
- id: messageId,
703
- error: {
704
- code: err.code || -32e3,
705
- message: err.message,
706
- data: err.data
707
- }
708
- };
709
- const message = {
710
- from: this.instanceId,
711
- to,
712
- payload: rpcResponse
713
- };
714
- this.driver.send(message);
715
- }
716
- destroy() {
717
- this.logger.info("MessageNexus destroying", {
718
- instanceId: this.instanceId,
719
- pendingMessages: this.pendingTasks.size,
720
- queuedMessages: this.messageQueue.length,
721
- metrics: this.getMetrics()
722
- });
723
- this.driver.destroy?.();
724
- if (this.cleanupInterval) {
725
- clearInterval(this.cleanupInterval);
726
- this.cleanupInterval = null;
727
- }
728
- this.invokeHandlers.clear();
729
- this.notificationHandlers.clear();
730
- this.metricsCallbacks.clear();
731
- }
732
- };
733
- // Annotate the CommonJS export names for ESM import in node:
734
- 0 && (module.exports = {
735
- BaseDriver,
736
- BroadcastDriver,
737
- MittDriver,
738
- PostMessageDriver,
739
- WebSocketDriver,
740
- createEmitter
741
- });
1
+ "use strict";var J=Object.create;var R=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var q=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var F=(r,t)=>{for(var e in t)R(r,e,{get:t[e],enumerable:!0})},x=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of W(t))!j.call(r,n)&&n!==e&&R(r,n,{get:()=>t[n],enumerable:!(s=P(t,n))||s.enumerable});return r};var U=(r,t,e)=>(e=r!=null?J(q(r)):{},x(t||!r||!r.__esModule?R(e,"default",{value:r,enumerable:!0}):e,r)),Q=r=>x(R({},"__esModule",{value:!0}),r);var $={};F($,{BaseDriver:()=>c,BroadcastDriver:()=>p,LogLevel:()=>k,MittDriver:()=>f,PostMessageDriver:()=>v,WebSocketDriver:()=>y,createEmitter:()=>T,default:()=>b});module.exports=Q($);var c=class{constructor(){this.onMessage=null}send(t){throw new Error("Not implemented")}destroy(){}};var S="message-nexus-v1";function G(r){return typeof r=="object"&&r!==null&&"__messageBridge"in r&&r.__messageBridge===S}var p=class extends c{constructor(e){super();this.messageHandler=null;if(!e.channel)throw new Error("BroadcastDriver requires a channel name");this.channel=new BroadcastChannel(e.channel),this.messageHandler=s=>{if(!G(s.data))return;let{__messageBridge:n,...i}=s.data;this.onMessage?.(i)},this.channel.addEventListener("message",this.messageHandler)}send(e){let s={...e,__messageBridge:S};this.channel.postMessage(s)}destroy(){this.channel&&this.channel.close(),this.messageHandler&&(this.channel.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var E=Symbol("message_nexus_internal"),f=class extends c{constructor(t){super(),this.emitter=t,this.listener=()=>{};let e=s=>{s&&this.onMessage?.(s)};this.emitter.on(E,e),this.listener=()=>{this.emitter.off(E,e)}}send(t){this.emitter.emit(E,t)}destroy(){this.listener(),this.onMessage=null}};var I="message-nexus-v1";function A(r){return typeof r=="object"&&r!==null&&"__messageBridge"in r&&r.__messageBridge===I}var v=class extends c{constructor(e,s){super();this.messageHandler=null;if(!s||s==="*")throw new Error('PostMessageDriver requires explicit targetOrigin for security. Do not use "*" as it allows any origin.');this.targetWindow=e,this.targetOrigin=s,this.messageHandler=n=>{if(n.origin!==this.targetOrigin||!A(n.data))return;let{__messageBridge:i,...a}=n.data;this.onMessage?.(a)},window.addEventListener("message",this.messageHandler)}send(e){let s={...e,__messageBridge:I};this.targetWindow.postMessage(s,this.targetOrigin)}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var k=(n=>(n.DEBUG="debug",n.INFO="info",n.WARN="warn",n.ERROR="error",n))(k||{});function H(r){if(r==null||typeof r!="object")return!1;let t=r;return typeof t.debug=="function"&&typeof t.info=="function"&&typeof t.warn=="function"&&typeof t.error=="function"&&typeof t.addHandler=="function"&&typeof t.setMinLevel=="function"&&typeof t.enable=="function"&&typeof t.disable=="function"&&typeof t.isEnabled=="function"}function _(r){if(r==null||typeof r!="object")return!1;let t=r;return typeof t.debug=="function"&&typeof t.info=="function"&&typeof t.warn=="function"&&typeof t.error=="function"}var m=class{constructor(t,e="info",s=!1){this.handlers=[];this.context=t,this.minLevel=e,this.enabled=s}enable(){this.enabled=!0}disable(){this.enabled=!1}isEnabled(){return this.enabled}addHandler(t){this.handlers.push(t)}setMinLevel(t){this.minLevel=t}shouldLog(t){let e=["debug","info","warn","error"];return e.indexOf(t)>=e.indexOf(this.minLevel)}log(t,e,s){if(!this.enabled||!this.shouldLog(t))return;let n={level:t,timestamp:Date.now(),message:e,metadata:s,context:this.context};this.handlers.forEach(i=>i(n))}debug(t,e){this.log("debug",t,e)}info(t,e){this.log("info",t,e)}warn(t,e){this.log("warn",t,e)}error(t,e){this.log("error",t,e)}};function z(){return new Date().toLocaleTimeString("zh-CN",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}var w=()=>r=>{let e=`[${z()}] [${r.level.toUpperCase()}] [${r.context||"app"}]`,s=r.level==="debug"?console.debug:r.level==="info"?console.info:r.level==="warn"?console.warn:console.error;r.metadata?s(e,r.message,r.metadata):s(e,r.message)};var C="message-nexus-v1",y=class extends c{constructor(e){super();this.ws=null;this.retryCount=0;this.reconnectTimer=null;this.isManuallyClosed=!1;this.url=e.url,this.reconnectEnabled=e.reconnect!==!1,this.maxRetries=(typeof e.reconnect=="object"?e.reconnect.maxRetries:void 0)??1/0,this.retryInterval=(typeof e.reconnect=="object"?e.reconnect.retryInterval:void 0)??5e3,this.logger=e.logger||new m("WebSocketDriver"),this.logger.addHandler(w()),this.onStatusChange=e.onStatusChange,this.connect()}connect(){this.onStatusChange?.("connecting"),this.ws=new WebSocket(this.url),this.ws.addEventListener("open",()=>{this.logger.info("WebSocket connected",{url:this.url}),this.retryCount=0,this.onStatusChange?.("connected")}),this.ws.addEventListener("message",e=>{try{let s=JSON.parse(e.data);if(typeof s=="object"&&s!==null&&"__messageBridge"in s&&s.__messageBridge===C){let{__messageBridge:n,...i}=s;this.logger.debug("Message received",{data:i}),this.onMessage?.(i)}else this.logger.debug("Ignored non-bridge message",{data:s})}catch(s){this.logger.error("Failed to parse WebSocket message",{error:s,data:e.data})}}),this.ws.addEventListener("error",e=>{this.logger.error("WebSocket error",{event:e}),this.onStatusChange?.("error")}),this.ws.addEventListener("close",()=>{this.logger.info("WebSocket connection closed",{manuallyClosed:this.isManuallyClosed,retryCount:this.retryCount,maxRetries:this.maxRetries}),!this.isManuallyClosed&&this.reconnectEnabled&&this.retryCount<this.maxRetries?this.scheduleReconnect():this.onStatusChange?.("disconnected")})}scheduleReconnect(){this.retryCount++;let e=this.retryInterval*this.retryCount;this.logger.info("Reconnecting scheduled",{delay:e,attempt:this.retryCount,maxRetries:this.maxRetries,url:this.url}),this.onStatusChange?.("connecting"),this.reconnectTimer=window.setTimeout(()=>{this.connect()},e)}send(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw this.logger.error("WebSocket is not open",{state:this.ws?.readyState,url:this.url}),new Error("WebSocket is not open");this.logger.debug("Sending message",{data:e});let s={...e,__messageBridge:C};this.ws.send(JSON.stringify(s))}close(){this.logger.info("Closing WebSocket connection",{url:this.url}),this.isManuallyClosed=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(),this.ws=null),this.onStatusChange?.("disconnected")}destroy(){this.close(),this.onMessage=null}};var N=U(require("mitt"),1);function T(){return(0,N.default)()}var b=class{constructor(t,e){this.cleanupInterval=null;this.messageQueue=[];this.maxQueueSize=100;this.errorHandler=null;this.metrics={messagesSent:0,messagesReceived:0,messagesFailed:0,pendingMessages:0,queuedMessages:0,totalLatency:0,averageLatency:0};this.metricsCallbacks=new Set;this.driver=t,this.instanceId=e?.instanceId||crypto.randomUUID(),this.timeout=e?.timeout??1e4;let s=e?.logLevel??"info",n=e?.loggerEnabled??!1;if(e?.logger&&H(e.logger))this.logger=e.logger;else if(this.logger=new m("MessageNexus",s,n),e?.logger&&_(e.logger)){let i=e.logger;this.logger.addHandler(a=>{let{level:g,message:o,metadata:d}=a;g==="debug"?i.debug(o,d):g==="info"?i.info(o,d):g==="warn"?i.warn(o,d):g==="error"&&i.error(o,d)})}else n&&this.logger.addHandler(w());n&&(this.logger.enable(),this.logger.info("MessageNexus initialized",{instanceId:this.instanceId,timeout:this.timeout,logLevel:s})),this.pendingTasks=new Map,this.invokeHandlers=new Map,this.notificationHandlers=new Map,this.cleanupInterval=null,this.driver.onMessage=i=>this._handleIncoming(i)}async invoke(t){let e=crypto.randomUUID(),s,n,i,a,g,o=0,d=1e3;if(typeof t=="string")s=t,n=void 0,i=void 0,a={},g=this.timeout;else{let l=t;s=l.method,n=l.params,i=l.to,a=l.metadata||{},g=l.timeout??this.timeout,o=l.retryCount??0,d=l.retryDelay??1e3}let h=async l=>new Promise((L,M)=>{let O=setTimeout(()=>{this.pendingTasks.delete(e),this.metrics.messagesFailed++,this.metrics.pendingMessages--,M(new Error(`Message timeout: ${s} (${e})`))},g);this.pendingTasks.set(e,{resolve:L,reject:M,timer:O,timestamp:Date.now()});let B={jsonrpc:"2.0",method:s,params:n,id:e},D={from:this.instanceId,to:i,metadata:{...a,timestamp:Date.now()},payload:B};this._sendMessage(D)}).catch(L=>{if(l<o)return new Promise(M=>setTimeout(()=>M(h(l+1)),d*(l+1)));throw this.metrics.messagesFailed++,this.metrics.pendingMessages--,L});return h(0)}_sendMessage(t){let e=t.payload,s="method"in e,n="id"in e?String(e.id):void 0,i=s?e.method:"RESPONSE";try{this.driver.send(t),this.metrics.messagesSent++,s&&n!==void 0&&this.metrics.pendingMessages++,this.logger.debug("Message sent",{messageId:n,type:i})}catch(a){let g=a instanceof Error?a:new Error(String(a));this.metrics.messagesFailed++,this.logger.error("Failed to send message",{error:g.message,messageId:n}),this.errorHandler?.(g,{message:t}),this.messageQueue.length<this.maxQueueSize?(this.messageQueue.push(t),this.logger.debug("Message queued",{messageId:n,queueSize:this.messageQueue.length+1})):(this.logger.warn("Message queue full, dropping oldest message",{queueSize:this.messageQueue.length}),this.messageQueue.shift(),this.messageQueue.push(t))}this.metrics.queuedMessages=this.messageQueue.length,this._notifyMetrics()}onError(t){return this.errorHandler=t,()=>{this.errorHandler=null}}flushQueue(){for(;this.messageQueue.length>0;){let t=this.messageQueue.shift();if(t)try{this.driver.send(t)}catch{this.messageQueue.unshift(t);break}}}notify(t){let e,s,n,i;if(typeof t=="string")e=t,s=void 0,n=void 0,i={};else{let o=t;e=o.method,s=o.params,n=o.to,i=o.metadata||{}}let a={jsonrpc:"2.0",method:e,params:s},g={from:this.instanceId,to:n,metadata:{...i,timestamp:Date.now()},payload:a};this._sendMessage(g)}_validateMessage(t){if(!t||typeof t!="object")return!1;let e=t;if(typeof e.from!="string"||e.to!==void 0&&typeof e.to!="string"||e.metadata!==void 0&&typeof e.metadata!="object")return!1;let s=e.payload;if(!s||typeof s!="object"||s.jsonrpc!=="2.0")return!1;let n="method"in s,i="result"in s||"error"in s;return!(!n&&!i)}async _handleIncoming(t){if(!this._validateMessage(t)){this.logger.error("Invalid message format received",{data:t}),this.errorHandler?.(new Error("Invalid message format received"),{data:t}),this.metrics.messagesFailed++;return}let e=t,s=e.payload;if(e.to&&e.to!==this.instanceId){this.logger.debug("Message filtered: not for this instance",{messageId:"id"in s?s.id:void 0,to:e.to,instanceId:this.instanceId});return}if("result"in s||"error"in s){let n=s,i=String(n.id);if(this.pendingTasks.has(i)){let{resolve:a,reject:g,timer:o,timestamp:d}=this.pendingTasks.get(i);clearTimeout(o),this.pendingTasks.delete(i);let h=Date.now()-d;if(this.metrics.messagesReceived++,this.metrics.pendingMessages--,this.metrics.totalLatency+=h,this.metrics.averageLatency=this.metrics.totalLatency/this.metrics.messagesReceived,this.logger.debug("Response received",{messageId:i,latency:h}),n.error){let l=new Error(n.error.message);l.code=n.error.code,l.data=n.error.data,g(l)}else a(n.result);this._notifyMetrics()}else this.logger.warn("Orphaned response received",{messageId:i});return}if("method"in s)if("id"in s){let n=s,i=String(n.id);this.logger.debug("Invoke message received",{messageId:i,type:n.method,from:e.from});let a={messageId:i,from:e.from,to:e.to,metadata:e.metadata},g=this.invokeHandlers.get(n.method);if(g)try{let o=await g(n.params,a);this._reply(i,e.from,o)}catch(o){this._replyError(i,e.from,o)}else{let o=new Error(`Method not found: ${n.method}`);o.code=-32601,this._replyError(i,e.from,o)}}else{let n=s;this.logger.debug("Notification message received",{type:n.method,from:e.from});let i={from:e.from,to:e.to,metadata:e.metadata},a=this.notificationHandlers.get(n.method);a&&a.forEach(g=>{try{g(n.params,i)}catch(o){this.logger.error("Error in notification handler",{error:String(o)})}})}}getMetrics(){return{...this.metrics,pendingMessages:this.pendingTasks.size}}onMetrics(t){return this.metricsCallbacks.add(t),()=>this.metricsCallbacks.delete(t)}_notifyMetrics(){let t=this.getMetrics();this.metricsCallbacks.forEach(e=>e(t))}handle(t,e){return this.invokeHandlers.has(t)&&this.logger.warn(`Overriding existing handler for method: ${t}`),this.invokeHandlers.set(t,e),()=>this.invokeHandlers.delete(t)}removeHandler(t){this.invokeHandlers.delete(t)}onNotification(t,e){return this.notificationHandlers.has(t)||this.notificationHandlers.set(t,new Set),this.notificationHandlers.get(t).add(e),()=>this.offNotification(t,e)}offNotification(t,e){let s=this.notificationHandlers.get(t);s&&(s.delete(e),s.size===0&&this.notificationHandlers.delete(t))}_reply(t,e,s){let n={jsonrpc:"2.0",id:t,result:s},i={from:this.instanceId,to:e,payload:n};this.driver.send(i)}_replyError(t,e,s){let n=s instanceof Error?s:new Error(String(s)),i={jsonrpc:"2.0",id:t,error:{code:n.code||-32e3,message:n.message,data:n.data}},a={from:this.instanceId,to:e,payload:i};this.driver.send(a)}destroy(){this.logger.info("MessageNexus destroying",{instanceId:this.instanceId,pendingMessages:this.pendingTasks.size,queuedMessages:this.messageQueue.length,metrics:this.getMetrics()}),this.driver.destroy?.(),this.cleanupInterval&&(clearInterval(this.cleanupInterval),this.cleanupInterval=null),this.invokeHandlers.clear(),this.notificationHandlers.clear(),this.metricsCallbacks.clear()}};0&&(module.exports={BaseDriver,BroadcastDriver,LogLevel,MittDriver,PostMessageDriver,WebSocketDriver,createEmitter});