message-nexus 1.1.3 → 1.2.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 CHANGED
@@ -22,8 +22,9 @@ pnpm add message-nexus
22
22
  - **Retry Mechanism**: Automatic retry on request failure, configurable retry counts and delays
23
23
  - **Message Validation**: Runtime message format validation to prevent illegal messages
24
24
  - **Monitoring Metrics**: Built-in message statistics and performance monitoring
25
+ - **Message Interceptors**: Hook into the outgoing request and incoming response pipelines to modify payloads, inject metadata, or implement global logging
26
+ - **Cross-Context Errors**: Preserves error `name` and `stack` traces across communication bridges for seamless debugging
25
27
  - **Structured Logging**: Supports adjustable log levels (DEBUG, INFO, WARN, ERROR) and custom log handlers or simple loggers (like `console`) for easy debugging and production monitoring.
26
-
27
28
  - **Resource Management**: All drivers support the `destroy()` method to properly clean up resources.
28
29
 
29
30
  ## Quick Start
@@ -346,18 +347,50 @@ nexus.onError((error, context) => {
346
347
  })
347
348
  ```
348
349
 
350
+ ##### useRequestInterceptor()
351
+
352
+ Register a hook to intercept and potentially modify outgoing messages before they are sent to the driver. Interceptors can be synchronous or asynchronous.
353
+
354
+ ```typescript
355
+ nexus.useRequestInterceptor(
356
+ (message: Message) => Message | Promise<Message>
357
+ ): () => void
358
+ ```
359
+
360
+ **Example:**
361
+
362
+ ```typescript
363
+ const unsubscribe = nexus.useRequestInterceptor(async (message) => {
364
+ // Inject an authentication token into the metadata
365
+ message.metadata = { ...message.metadata, token: await getAuthToken() }
366
+ return message
367
+ })
368
+ ```
369
+
370
+ ##### useResponseInterceptor()
371
+
372
+ Register a hook to intercept and modify incoming messages before they are processed by handlers or resolve pending invokes.
373
+
374
+ ```typescript
375
+ nexus.useResponseInterceptor(
376
+ (message: Message) => Message | Promise<Message>
377
+ ): () => void
378
+ ```
379
+
349
380
  #### Errors
350
381
 
351
382
  MessageNexus provides a structured error system based on the JSON-RPC 2.0 specification.
352
383
 
353
384
  ##### NexusError
354
385
 
355
- A custom error class that includes a numeric code and optional data.
386
+ A custom error class that includes a numeric code and optional data. `MessageNexus` automatically preserves and serializes the `name` and `stack` properties of errors thrown in handlers, enabling seamless cross-context debugging.
356
387
 
357
388
  ```typescript
358
389
  class NexusError<D = any> extends Error {
359
390
  code: number // JSON-RPC or Nexus-specific error code
360
391
  data?: D // Optional additional error information
392
+ name: string // Preserved error name from original context
393
+ stack?: string // Preserved stack trace from original context
361
394
  }
362
395
  ```
363
396
 
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var K=Object.create;var b=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var q=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var G=(r,t)=>{for(var e in t)b(r,e,{get:t[e],enumerable:!0})},E=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of j(t))!F.call(r,n)&&n!==e&&b(r,n,{get:()=>t[n],enumerable:!(s=W(t,n))||s.enumerable});return r};var U=(r,t,e)=>(e=r!=null?K(q(r)):{},E(t||!r||!r.__esModule?b(e,"default",{value:r,enumerable:!0}):e,r)),Q=r=>E(b({},"__esModule",{value:!0}),r);var X={};G(X,{BaseDriver:()=>c,BroadcastDriver:()=>f,LogLevel:()=>I,MittDriver:()=>v,NexusError:()=>u,NexusErrorCode:()=>B,PostMessageDriver:()=>y,WebSocketDriver:()=>M,createEmitter:()=>C,default:()=>k});module.exports=Q(X);var c=class{constructor(){this.onMessage=null}send(t){throw new Error("Not implemented")}destroy(){}};var S="message-nexus-v1";function A(r){return typeof r=="object"&&r!==null&&"__messageBridge"in r&&r.__messageBridge===S}var f=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(!A(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 x=Symbol("message_nexus_internal"),v=class extends c{constructor(t){super(),this.emitter=t,this.listener=()=>{};let e=s=>{s&&this.onMessage?.(s)};this.emitter.on(x,e),this.listener=()=>{this.emitter.off(x,e)}}send(t){this.emitter.emit(x,t)}destroy(){this.listener(),this.onMessage=null}};var H="message-nexus-v1";function z(r){return typeof r=="object"&&r!==null&&"__messageBridge"in r&&r.__messageBridge===H}var y=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||!z(n.data))return;let{__messageBridge:i,...a}=n.data;this.onMessage?.(a)},window.addEventListener("message",this.messageHandler)}send(e){let s={...e,__messageBridge:H};this.targetWindow.postMessage(s,this.targetOrigin)}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var I=(n=>(n.DEBUG="debug",n.INFO="info",n.WARN="warn",n.ERROR="error",n))(I||{});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"&&typeof t.addHandler=="function"&&typeof t.setMinLevel=="function"&&typeof t.enable=="function"&&typeof t.disable=="function"&&typeof t.isEnabled=="function"}function N(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 p=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 $(){return new Date().toLocaleTimeString("zh-CN",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}var w=()=>r=>{let e=`[${$()}] [${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 O="message-nexus-v1",M=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 p("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===O){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:O};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 T=U(require("mitt"),1);function C(){return(0,T.default)()}var B=(o=>(o[o.ParseError=-32700]="ParseError",o[o.InvalidRequest=-32600]="InvalidRequest",o[o.MethodNotFound=-32601]="MethodNotFound",o[o.InvalidParams=-32602]="InvalidParams",o[o.InternalError=-32603]="InternalError",o[o.Timeout=-32001]="Timeout",o[o.SendFailed=-32002]="SendFailed",o[o.InvalidResponse=-32003]="InvalidResponse",o))(B||{}),u=class r extends Error{constructor(t,e=-32603,s){super(t),this.name="NexusError",this.code=e,this.data=s,Object.setPrototypeOf(this,r.prototype)}},k=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&&_(e.logger))this.logger=e.logger;else if(this.logger=new p("MessageNexus",s,n),e?.logger&&N(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,R)=>{let D=setTimeout(()=>{this.pendingTasks.delete(e),this.metrics.messagesFailed++,this.metrics.pendingMessages--,R(new u(`Message timeout: ${s} (${e})`,-32001))},g);this.pendingTasks.set(e,{resolve:L,reject:R,timer:D,timestamp:Date.now()});let P={jsonrpc:"2.0",method:s,params:n,id:e},J={from:this.instanceId,to:i,metadata:{...a,timestamp:Date.now()},payload:P};this._sendMessage(J)}).catch(L=>{if(l<o)return new Promise(R=>setTimeout(()=>R(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 u(n.error.message,n.error.code,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 u(`Method not found: ${n.method}`,-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 u?s:s instanceof Error?new u(s.message,-32603):new u(String(s),-32603),i={jsonrpc:"2.0",id:t,error:{code:n.code,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,NexusError,NexusErrorCode,PostMessageDriver,WebSocketDriver,createEmitter});
1
+ "use strict";var K=Object.create;var w=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var F=Object.getPrototypeOf,G=Object.prototype.hasOwnProperty;var Q=(i,t)=>{for(var e in t)w(i,e,{get:t[e],enumerable:!0})},x=(i,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of j(t))!G.call(i,n)&&n!==e&&w(i,n,{get:()=>t[n],enumerable:!(s=W(t,n))||s.enumerable});return i};var U=(i,t,e)=>(e=i!=null?K(F(i)):{},x(t||!i||!i.__esModule?w(e,"default",{value:i,enumerable:!0}):e,i)),A=i=>x(w({},"__esModule",{value:!0}),i);var V={};Q(V,{BaseDriver:()=>l,BroadcastDriver:()=>f,LogLevel:()=>L,MittDriver:()=>v,NexusError:()=>u,NexusErrorCode:()=>T,PostMessageDriver:()=>y,WebSocketDriver:()=>M,createEmitter:()=>C,default:()=>k});module.exports=A(V);var l=class{constructor(){this.onMessage=null,this.onConnect=null,this.onDisconnect=null}send(t){throw new Error("Not implemented")}destroy(){}};var S="message-nexus-v1";function z(i){return typeof i=="object"&&i!==null&&"__messageBridge"in i&&i.__messageBridge===S}var f=class extends l{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(!z(s.data))return;let{__messageBridge:n,...o}=s.data;this.onMessage?.(o)},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 I=Symbol("message_nexus_internal"),v=class extends l{constructor(t){super(),this.emitter=t,this.listener=()=>{};let e=s=>{s&&this.onMessage?.(s)};this.emitter.on(I,e),this.listener=()=>{this.emitter.off(I,e)}}send(t){this.emitter.emit(I,t)}destroy(){this.listener(),this.onMessage=null}};var H="message-nexus-v1";function X(i){return typeof i=="object"&&i!==null&&"__messageBridge"in i&&i.__messageBridge===H}var y=class extends l{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||!X(n.data))return;let{__messageBridge:o,...a}=n.data;this.onMessage?.(a)},window.addEventListener("message",this.messageHandler)}send(e){let s={...e,__messageBridge:H};this.targetWindow.postMessage(s,this.targetOrigin)}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var L=(n=>(n.DEBUG="debug",n.INFO="info",n.WARN="warn",n.ERROR="error",n))(L||{});function _(i){if(i==null||typeof i!="object")return!1;let t=i;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 D(i){if(i==null||typeof i!="object")return!1;let t=i;return typeof t.debug=="function"&&typeof t.info=="function"&&typeof t.warn=="function"&&typeof t.error=="function"}var h=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(o=>o(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 $(){return new Date().toLocaleTimeString("zh-CN",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}var b=()=>i=>{let e=`[${$()}] [${i.level.toUpperCase()}] [${i.context||"app"}]`,s=i.level==="debug"?console.debug:i.level==="info"?console.info:i.level==="warn"?console.warn:console.error;i.metadata?s(e,i.message,i.metadata):s(e,i.message)};var N="message-nexus-v1",M=class extends l{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 h("WebSocketDriver"),this.logger.addHandler(b()),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.onConnect?.()}),this.ws.addEventListener("message",e=>{try{let s=JSON.parse(e.data);if(typeof s=="object"&&s!==null&&"__messageBridge"in s&&s.__messageBridge===N){let{__messageBridge:n,...o}=s;this.logger.debug("Message received",{data:o}),this.onMessage?.(o)}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"),this.onDisconnect?.()})}scheduleReconnect(){this.retryCount++;let e=3e4,s=this.retryInterval*Math.pow(2,this.retryCount),n=Math.min(s,e);this.logger.info("Reconnecting scheduled",{delay:n,attempt:this.retryCount,maxRetries:this.maxRetries,url:this.url}),this.onStatusChange?.("connecting"),this.reconnectTimer=window.setTimeout(()=>{this.connect()},n)}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:N};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"),this.onDisconnect?.()}destroy(){this.close(),this.onMessage=null,this.onConnect=null,this.onDisconnect=null}};var O=U(require("mitt"),1);function C(){return(0,O.default)()}var T=(r=>(r[r.ParseError=-32700]="ParseError",r[r.InvalidRequest=-32600]="InvalidRequest",r[r.MethodNotFound=-32601]="MethodNotFound",r[r.InvalidParams=-32602]="InvalidParams",r[r.InternalError=-32603]="InternalError",r[r.Timeout=-32001]="Timeout",r[r.SendFailed=-32002]="SendFailed",r[r.InvalidResponse=-32003]="InvalidResponse",r))(T||{}),u=class i extends Error{constructor(t,e=-32603,s,n,o){super(t),this.name=n||"NexusError",this.code=e,this.data=s,o&&(this.stack=o),Object.setPrototypeOf(this,i.prototype)}},k=class{constructor(t,e){this.messageQueue=[];this.maxQueueSize=100;this.requestInterceptors=[];this.responseInterceptors=[];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&&_(e.logger))this.logger=e.logger;else if(this.logger=new h("MessageNexus",s,n),e?.logger&&D(e.logger)){let o=e.logger;this.logger.addHandler(a=>{let{level:g,message:r,metadata:d}=a;g==="debug"?o.debug(r,d):g==="info"?o.info(r,d):g==="warn"?o.warn(r,d):g==="error"&&o.error(r,d)})}else n&&this.logger.addHandler(b());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.driver.onMessage=o=>this._handleIncoming(o),this.driver.onConnect=()=>{this.logger.info("Driver connected, flushing message queue"),this.flushQueue()}}async invoke(t){let e=crypto.randomUUID(),s,n,o,a,g,r=0,d=1e3;if(typeof t=="string")s=t,n=void 0,o=void 0,a={},g=this.timeout;else{let c=t;s=c.method,n=c.params,o=c.to,a=c.metadata||{},g=c.timeout??this.timeout,r=c.retryCount??0,d=c.retryDelay??1e3}let m=async c=>new Promise((E,R)=>{let B=setTimeout(()=>{this.pendingTasks.delete(e),this.metrics.messagesFailed++,this.metrics.pendingMessages--,R(new u(`Message timeout: ${s} (${e})`,-32001))},g);this.pendingTasks.set(e,{resolve:E,reject:R,timer:B,timestamp:Date.now()});let q={jsonrpc:"2.0",method:s,params:n,id:e},P={from:this.instanceId,to:o,metadata:{...a,timestamp:Date.now()},payload:q},J=c>=r;this._sendMessage(P,!J).catch(()=>{})}).catch(E=>{if(c<r)return new Promise(R=>setTimeout(()=>R(m(c+1)),d*(c+1)));throw this.metrics.messagesFailed++,this.metrics.pendingMessages--,E});return m(0)}async _sendMessage(t,e=!1){let s=t;try{for(let r of this.requestInterceptors)s=await r(s)}catch(r){this.logger.error("Request interceptor failed",{error:String(r)}),this.metrics.messagesFailed++,this.errorHandler?.(r instanceof Error?r:new Error(String(r)),{message:s});return}let n=s.payload,o="method"in n,a="id"in n?String(n.id):void 0,g=o?n.method:"RESPONSE";try{this.driver.send(s),this.metrics.messagesSent++,o&&a!==void 0&&this.metrics.pendingMessages++,this.logger.debug("Message sent",{messageId:a,type:g})}catch(r){let d=r instanceof Error?r:new Error(String(r));this.metrics.messagesFailed++,this.logger.error("Failed to send message",{error:d.message,messageId:a}),this.errorHandler?.(d,{message:s});let m=typeof DOMException<"u"&&r instanceof DOMException&&r.name==="DataCloneError";!e&&!m?this.messageQueue.length<this.maxQueueSize?(this.messageQueue.push(s),this.logger.debug("Message queued",{messageId:a,queueSize:this.messageQueue.length+1})):(this.logger.warn("Message queue full, dropping oldest message",{queueSize:this.messageQueue.length}),this.messageQueue.shift(),this.messageQueue.push(s)):this.logger.debug(m?"Message dropped due to data error":"Message failed but skipQueue is true (likely retrying)",{messageId:a})}this.metrics.queuedMessages=this.messageQueue.length,this._notifyMetrics()}useRequestInterceptor(t){return this.requestInterceptors.push(t),()=>{this.requestInterceptors=this.requestInterceptors.filter(e=>e!==t)}}useResponseInterceptor(t){return this.responseInterceptors.push(t),()=>{this.responseInterceptors=this.responseInterceptors.filter(e=>e!==t)}}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(e){if(typeof DOMException<"u"&&e instanceof DOMException&&e.name==="DataCloneError"){this.logger.error("Message payload cannot be cloned during flush, dropping",{error:e.message});continue}this.messageQueue.unshift(t);break}}this.metrics.queuedMessages=this.messageQueue.length,this._notifyMetrics()}async notify(t){let e,s,n,o;if(typeof t=="string")e=t,s=void 0,n=void 0,o={};else{let r=t;e=r.method,s=r.params,n=r.to,o=r.metadata||{}}let a={jsonrpc:"2.0",method:e,params:s},g={from:this.instanceId,to:n,metadata:{...o,timestamp:Date.now()},payload:a};await 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,o="result"in s||"error"in s;return!(!n&&!o)}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;try{for(let n of this.responseInterceptors)e=await n(e)}catch(n){this.logger.error("Response interceptor failed",{error:String(n)}),this.metrics.messagesFailed++,this.errorHandler?.(n instanceof Error?n:new Error(String(n)),{message:e});return}let 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,o=String(n.id);if(this.pendingTasks.has(o)){let{resolve:a,reject:g,timer:r,timestamp:d}=this.pendingTasks.get(o);clearTimeout(r),this.pendingTasks.delete(o);let m=Date.now()-d;if(this.metrics.messagesReceived++,this.metrics.pendingMessages--,this.metrics.totalLatency+=m,this.metrics.averageLatency=this.metrics.totalLatency/this.metrics.messagesReceived,this.logger.debug("Response received",{messageId:o,latency:m}),n.error){let c=new u(n.error.message,n.error.code,n.error.data,n.error.name,n.error.stack);g(c)}else a(n.result);this._notifyMetrics()}else this.logger.warn("Orphaned response received",{messageId:o});return}if("method"in s)if("id"in s){let n=s,o=String(n.id);this.logger.debug("Invoke message received",{messageId:o,type:n.method,from:e.from});let a={messageId:o,from:e.from,to:e.to,metadata:e.metadata},g=this.invokeHandlers.get(n.method);if(g)try{let r=await g(n.params,a);await this._reply(o,e.from,r)}catch(r){await this._replyError(o,e.from,r)}else{let r=new u(`Method not found: ${n.method}`,-32601);await this._replyError(o,e.from,r)}}else{let n=s;this.logger.debug("Notification message received",{type:n.method,from:e.from});let o={from:e.from,to:e.to,metadata:e.metadata},a=this.notificationHandlers.get(n.method);a&&a.forEach(g=>{try{g(n.params,o)}catch(r){this.logger.error("Error in notification handler",{error:String(r)})}})}}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))}async _reply(t,e,s){let n={jsonrpc:"2.0",id:t,result:s},o={from:this.instanceId,to:e,payload:n};await this._sendMessage(o)}async _replyError(t,e,s){let n=s instanceof u?s:s instanceof Error?new u(s.message,-32603,void 0,s.name,s.stack):new u(String(s),-32603),o={jsonrpc:"2.0",id:t,error:{code:n.code,message:n.message,data:n.data,name:n.name,stack:n.stack}},a={from:this.instanceId,to:e,payload:o};await this._sendMessage(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.invokeHandlers.clear(),this.notificationHandlers.clear(),this.metricsCallbacks.clear()}};0&&(module.exports={BaseDriver,BroadcastDriver,LogLevel,MittDriver,NexusError,NexusErrorCode,PostMessageDriver,WebSocketDriver,createEmitter});
package/dist/index.d.cts CHANGED
@@ -19,6 +19,8 @@ interface JsonRpcResponse {
19
19
  code: number;
20
20
  message: string;
21
21
  data?: unknown;
22
+ name?: string;
23
+ stack?: string;
22
24
  };
23
25
  id: JsonRpcId;
24
26
  }
@@ -31,6 +33,8 @@ interface NexusEnvelope<T = JsonRpcRequest | JsonRpcResponse | JsonRpcNotificati
31
33
  type Message = NexusEnvelope;
32
34
  declare class BaseDriver {
33
35
  onMessage: ((data: Message) => void) | null;
36
+ onConnect: (() => void) | null;
37
+ onDisconnect: (() => void) | null;
34
38
  constructor();
35
39
  send(data: Message): void;
36
40
  destroy(): void;
@@ -226,9 +230,11 @@ declare enum NexusErrorCode {
226
230
  declare class NexusError<D = any> extends Error {
227
231
  readonly code: number;
228
232
  readonly data?: D;
229
- constructor(message: string, code?: number, data?: D);
233
+ constructor(message: string, code?: number, data?: D, name?: string, stack?: string);
230
234
  }
231
235
  type ErrorHandler = (error: Error | NexusError, context?: Record<string, unknown>) => void;
236
+ type RequestInterceptor = (message: Message) => Message | Promise<Message>;
237
+ type ResponseInterceptor = (message: Message) => Message | Promise<Message>;
232
238
  interface Metrics {
233
239
  messagesSent: number;
234
240
  messagesReceived: number;
@@ -252,9 +258,10 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
252
258
  notificationHandlers: Map<string, Set<NotificationHandler>>;
253
259
  timeout: number;
254
260
  instanceId: string;
255
- private cleanupInterval;
256
261
  private messageQueue;
257
262
  private maxQueueSize;
263
+ private requestInterceptors;
264
+ private responseInterceptors;
258
265
  private errorHandler;
259
266
  private logger;
260
267
  private metrics;
@@ -262,9 +269,11 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
262
269
  constructor(driver: BaseDriver, options?: MessageNexusOptions);
263
270
  invoke<K extends keyof InvokeMap>(methodOrOptions: K | InvokeOptions<K & string, GetParams<InvokeMap[K]>>): Promise<GetResult<InvokeMap[K]>>;
264
271
  private _sendMessage;
272
+ useRequestInterceptor(interceptor: RequestInterceptor): () => void;
273
+ useResponseInterceptor(interceptor: ResponseInterceptor): () => void;
265
274
  onError(handler: ErrorHandler): () => void;
266
275
  flushQueue(): void;
267
- notify<K extends keyof NotificationMap>(methodOrOptions: K | NotificationOptions<K & string, NotificationMap[K]>): void;
276
+ notify<K extends keyof NotificationMap>(methodOrOptions: K | NotificationOptions<K & string, NotificationMap[K]>): Promise<void>;
268
277
  private _validateMessage;
269
278
  _handleIncoming(data: unknown): Promise<void>;
270
279
  getMetrics(): Metrics;
@@ -279,4 +288,4 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
279
288
  destroy(): void;
280
289
  }
281
290
 
282
- export { BaseDriver, BroadcastDriver, type DefaultRegistry, type ErrorHandler, type InvokeContext, type InvokeHandler, type InvokeOptions, LogLevel, type LoggerInterface, type Message, type MessageNexusOptions, type MethodSchema, type Metrics, type MetricsCallback, MittDriver, NexusError, NexusErrorCode, type NotificationHandler, type NotificationOptions, PostMessageDriver, type SimpleLogger, WebSocketDriver, createEmitter, MessageNexus as default };
291
+ export { BaseDriver, BroadcastDriver, type DefaultRegistry, type ErrorHandler, type InvokeContext, type InvokeHandler, type InvokeOptions, LogLevel, type LoggerInterface, type Message, type MessageNexusOptions, type MethodSchema, type Metrics, type MetricsCallback, MittDriver, NexusError, NexusErrorCode, type NotificationHandler, type NotificationOptions, PostMessageDriver, type RequestInterceptor, type ResponseInterceptor, type SimpleLogger, WebSocketDriver, createEmitter, MessageNexus as default };
package/dist/index.d.ts CHANGED
@@ -19,6 +19,8 @@ interface JsonRpcResponse {
19
19
  code: number;
20
20
  message: string;
21
21
  data?: unknown;
22
+ name?: string;
23
+ stack?: string;
22
24
  };
23
25
  id: JsonRpcId;
24
26
  }
@@ -31,6 +33,8 @@ interface NexusEnvelope<T = JsonRpcRequest | JsonRpcResponse | JsonRpcNotificati
31
33
  type Message = NexusEnvelope;
32
34
  declare class BaseDriver {
33
35
  onMessage: ((data: Message) => void) | null;
36
+ onConnect: (() => void) | null;
37
+ onDisconnect: (() => void) | null;
34
38
  constructor();
35
39
  send(data: Message): void;
36
40
  destroy(): void;
@@ -226,9 +230,11 @@ declare enum NexusErrorCode {
226
230
  declare class NexusError<D = any> extends Error {
227
231
  readonly code: number;
228
232
  readonly data?: D;
229
- constructor(message: string, code?: number, data?: D);
233
+ constructor(message: string, code?: number, data?: D, name?: string, stack?: string);
230
234
  }
231
235
  type ErrorHandler = (error: Error | NexusError, context?: Record<string, unknown>) => void;
236
+ type RequestInterceptor = (message: Message) => Message | Promise<Message>;
237
+ type ResponseInterceptor = (message: Message) => Message | Promise<Message>;
232
238
  interface Metrics {
233
239
  messagesSent: number;
234
240
  messagesReceived: number;
@@ -252,9 +258,10 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
252
258
  notificationHandlers: Map<string, Set<NotificationHandler>>;
253
259
  timeout: number;
254
260
  instanceId: string;
255
- private cleanupInterval;
256
261
  private messageQueue;
257
262
  private maxQueueSize;
263
+ private requestInterceptors;
264
+ private responseInterceptors;
258
265
  private errorHandler;
259
266
  private logger;
260
267
  private metrics;
@@ -262,9 +269,11 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
262
269
  constructor(driver: BaseDriver, options?: MessageNexusOptions);
263
270
  invoke<K extends keyof InvokeMap>(methodOrOptions: K | InvokeOptions<K & string, GetParams<InvokeMap[K]>>): Promise<GetResult<InvokeMap[K]>>;
264
271
  private _sendMessage;
272
+ useRequestInterceptor(interceptor: RequestInterceptor): () => void;
273
+ useResponseInterceptor(interceptor: ResponseInterceptor): () => void;
265
274
  onError(handler: ErrorHandler): () => void;
266
275
  flushQueue(): void;
267
- notify<K extends keyof NotificationMap>(methodOrOptions: K | NotificationOptions<K & string, NotificationMap[K]>): void;
276
+ notify<K extends keyof NotificationMap>(methodOrOptions: K | NotificationOptions<K & string, NotificationMap[K]>): Promise<void>;
268
277
  private _validateMessage;
269
278
  _handleIncoming(data: unknown): Promise<void>;
270
279
  getMetrics(): Metrics;
@@ -279,4 +288,4 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
279
288
  destroy(): void;
280
289
  }
281
290
 
282
- export { BaseDriver, BroadcastDriver, type DefaultRegistry, type ErrorHandler, type InvokeContext, type InvokeHandler, type InvokeOptions, LogLevel, type LoggerInterface, type Message, type MessageNexusOptions, type MethodSchema, type Metrics, type MetricsCallback, MittDriver, NexusError, NexusErrorCode, type NotificationHandler, type NotificationOptions, PostMessageDriver, type SimpleLogger, WebSocketDriver, createEmitter, MessageNexus as default };
291
+ export { BaseDriver, BroadcastDriver, type DefaultRegistry, type ErrorHandler, type InvokeContext, type InvokeHandler, type InvokeOptions, LogLevel, type LoggerInterface, type Message, type MessageNexusOptions, type MethodSchema, type Metrics, type MetricsCallback, MittDriver, NexusError, NexusErrorCode, type NotificationHandler, type NotificationOptions, PostMessageDriver, type RequestInterceptor, type ResponseInterceptor, type SimpleLogger, WebSocketDriver, createEmitter, MessageNexus as default };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var c=class{constructor(){this.onMessage=null}send(t){throw new Error("Not implemented")}destroy(){}};var I="message-nexus-v1";function B(o){return typeof o=="object"&&o!==null&&"__messageBridge"in o&&o.__messageBridge===I}var y=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(!B(s.data))return;let{__messageBridge:n,...r}=s.data;this.onMessage?.(r)},this.channel.addEventListener("message",this.messageHandler)}send(e){let s={...e,__messageBridge:I};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 L=Symbol("message_nexus_internal"),M=class extends c{constructor(t){super(),this.emitter=t,this.listener=()=>{};let e=s=>{s&&this.onMessage?.(s)};this.emitter.on(L,e),this.listener=()=>{this.emitter.off(L,e)}}send(t){this.emitter.emit(L,t)}destroy(){this.listener(),this.onMessage=null}};var E="message-nexus-v1";function D(o){return typeof o=="object"&&o!==null&&"__messageBridge"in o&&o.__messageBridge===E}var R=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||!D(n.data))return;let{__messageBridge:r,...a}=n.data;this.onMessage?.(a)},window.addEventListener("message",this.messageHandler)}send(e){let s={...e,__messageBridge:E};this.targetWindow.postMessage(s,this.targetOrigin)}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var S=(n=>(n.DEBUG="debug",n.INFO="info",n.WARN="warn",n.ERROR="error",n))(S||{});function H(o){if(o==null||typeof o!="object")return!1;let t=o;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 _(o){if(o==null||typeof o!="object")return!1;let t=o;return typeof t.debug=="function"&&typeof t.info=="function"&&typeof t.warn=="function"&&typeof t.error=="function"}var h=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(r=>r(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 P(){return new Date().toLocaleTimeString("zh-CN",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}var b=()=>o=>{let e=`[${P()}] [${o.level.toUpperCase()}] [${o.context||"app"}]`,s=o.level==="debug"?console.debug:o.level==="info"?console.info:o.level==="warn"?console.warn:console.error;o.metadata?s(e,o.message,o.metadata):s(e,o.message)};var N="message-nexus-v1",w=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 h("WebSocketDriver"),this.logger.addHandler(b()),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===N){let{__messageBridge:n,...r}=s;this.logger.debug("Message received",{data:r}),this.onMessage?.(r)}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:N};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}};import J from"mitt";function K(){return J()}var W=(i=>(i[i.ParseError=-32700]="ParseError",i[i.InvalidRequest=-32600]="InvalidRequest",i[i.MethodNotFound=-32601]="MethodNotFound",i[i.InvalidParams=-32602]="InvalidParams",i[i.InternalError=-32603]="InternalError",i[i.Timeout=-32001]="Timeout",i[i.SendFailed=-32002]="SendFailed",i[i.InvalidResponse=-32003]="InvalidResponse",i))(W||{}),u=class o extends Error{constructor(t,e=-32603,s){super(t),this.name="NexusError",this.code=e,this.data=s,Object.setPrototypeOf(this,o.prototype)}},x=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 h("MessageNexus",s,n),e?.logger&&_(e.logger)){let r=e.logger;this.logger.addHandler(a=>{let{level:g,message:i,metadata:d}=a;g==="debug"?r.debug(i,d):g==="info"?r.info(i,d):g==="warn"?r.warn(i,d):g==="error"&&r.error(i,d)})}else n&&this.logger.addHandler(b());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=r=>this._handleIncoming(r)}async invoke(t){let e=crypto.randomUUID(),s,n,r,a,g,i=0,d=1e3;if(typeof t=="string")s=t,n=void 0,r=void 0,a={},g=this.timeout;else{let l=t;s=l.method,n=l.params,r=l.to,a=l.metadata||{},g=l.timeout??this.timeout,i=l.retryCount??0,d=l.retryDelay??1e3}let f=async l=>new Promise((k,v)=>{let O=setTimeout(()=>{this.pendingTasks.delete(e),this.metrics.messagesFailed++,this.metrics.pendingMessages--,v(new u(`Message timeout: ${s} (${e})`,-32001))},g);this.pendingTasks.set(e,{resolve:k,reject:v,timer:O,timestamp:Date.now()});let T={jsonrpc:"2.0",method:s,params:n,id:e},C={from:this.instanceId,to:r,metadata:{...a,timestamp:Date.now()},payload:T};this._sendMessage(C)}).catch(k=>{if(l<i)return new Promise(v=>setTimeout(()=>v(f(l+1)),d*(l+1)));throw this.metrics.messagesFailed++,this.metrics.pendingMessages--,k});return f(0)}_sendMessage(t){let e=t.payload,s="method"in e,n="id"in e?String(e.id):void 0,r=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:r})}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,r;if(typeof t=="string")e=t,s=void 0,n=void 0,r={};else{let i=t;e=i.method,s=i.params,n=i.to,r=i.metadata||{}}let a={jsonrpc:"2.0",method:e,params:s},g={from:this.instanceId,to:n,metadata:{...r,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,r="result"in s||"error"in s;return!(!n&&!r)}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,r=String(n.id);if(this.pendingTasks.has(r)){let{resolve:a,reject:g,timer:i,timestamp:d}=this.pendingTasks.get(r);clearTimeout(i),this.pendingTasks.delete(r);let f=Date.now()-d;if(this.metrics.messagesReceived++,this.metrics.pendingMessages--,this.metrics.totalLatency+=f,this.metrics.averageLatency=this.metrics.totalLatency/this.metrics.messagesReceived,this.logger.debug("Response received",{messageId:r,latency:f}),n.error){let l=new u(n.error.message,n.error.code,n.error.data);g(l)}else a(n.result);this._notifyMetrics()}else this.logger.warn("Orphaned response received",{messageId:r});return}if("method"in s)if("id"in s){let n=s,r=String(n.id);this.logger.debug("Invoke message received",{messageId:r,type:n.method,from:e.from});let a={messageId:r,from:e.from,to:e.to,metadata:e.metadata},g=this.invokeHandlers.get(n.method);if(g)try{let i=await g(n.params,a);this._reply(r,e.from,i)}catch(i){this._replyError(r,e.from,i)}else{let i=new u(`Method not found: ${n.method}`,-32601);this._replyError(r,e.from,i)}}else{let n=s;this.logger.debug("Notification message received",{type:n.method,from:e.from});let r={from:e.from,to:e.to,metadata:e.metadata},a=this.notificationHandlers.get(n.method);a&&a.forEach(g=>{try{g(n.params,r)}catch(i){this.logger.error("Error in notification handler",{error:String(i)})}})}}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},r={from:this.instanceId,to:e,payload:n};this.driver.send(r)}_replyError(t,e,s){let n=s instanceof u?s:s instanceof Error?new u(s.message,-32603):new u(String(s),-32603),r={jsonrpc:"2.0",id:t,error:{code:n.code,message:n.message,data:n.data}},a={from:this.instanceId,to:e,payload:r};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()}};export{c as BaseDriver,y as BroadcastDriver,S as LogLevel,M as MittDriver,u as NexusError,W as NexusErrorCode,R as PostMessageDriver,w as WebSocketDriver,K as createEmitter,x as default};
1
+ var d=class{constructor(){this.onMessage=null,this.onConnect=null,this.onDisconnect=null}send(t){throw new Error("Not implemented")}destroy(){}};var L="message-nexus-v1";function B(o){return typeof o=="object"&&o!==null&&"__messageBridge"in o&&o.__messageBridge===L}var y=class extends d{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(!B(s.data))return;let{__messageBridge:n,...i}=s.data;this.onMessage?.(i)},this.channel.addEventListener("message",this.messageHandler)}send(e){let s={...e,__messageBridge:L};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"),M=class extends d{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 x="message-nexus-v1";function q(o){return typeof o=="object"&&o!==null&&"__messageBridge"in o&&o.__messageBridge===x}var R=class extends d{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||!q(n.data))return;let{__messageBridge:i,...a}=n.data;this.onMessage?.(a)},window.addEventListener("message",this.messageHandler)}send(e){let s={...e,__messageBridge:x};this.targetWindow.postMessage(s,this.targetOrigin)}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var S=(n=>(n.DEBUG="debug",n.INFO="info",n.WARN="warn",n.ERROR="error",n))(S||{});function H(o){if(o==null||typeof o!="object")return!1;let t=o;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 _(o){if(o==null||typeof o!="object")return!1;let t=o;return typeof t.debug=="function"&&typeof t.info=="function"&&typeof t.warn=="function"&&typeof t.error=="function"}var f=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 P(){return new Date().toLocaleTimeString("zh-CN",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}var w=()=>o=>{let e=`[${P()}] [${o.level.toUpperCase()}] [${o.context||"app"}]`,s=o.level==="debug"?console.debug:o.level==="info"?console.info:o.level==="warn"?console.warn:console.error;o.metadata?s(e,o.message,o.metadata):s(e,o.message)};var D="message-nexus-v1",b=class extends d{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 f("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.onConnect?.()}),this.ws.addEventListener("message",e=>{try{let s=JSON.parse(e.data);if(typeof s=="object"&&s!==null&&"__messageBridge"in s&&s.__messageBridge===D){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"),this.onDisconnect?.()})}scheduleReconnect(){this.retryCount++;let e=3e4,s=this.retryInterval*Math.pow(2,this.retryCount),n=Math.min(s,e);this.logger.info("Reconnecting scheduled",{delay:n,attempt:this.retryCount,maxRetries:this.maxRetries,url:this.url}),this.onStatusChange?.("connecting"),this.reconnectTimer=window.setTimeout(()=>{this.connect()},n)}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:D};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"),this.onDisconnect?.()}destroy(){this.close(),this.onMessage=null,this.onConnect=null,this.onDisconnect=null}};import J from"mitt";function K(){return J()}var W=(r=>(r[r.ParseError=-32700]="ParseError",r[r.InvalidRequest=-32600]="InvalidRequest",r[r.MethodNotFound=-32601]="MethodNotFound",r[r.InvalidParams=-32602]="InvalidParams",r[r.InternalError=-32603]="InternalError",r[r.Timeout=-32001]="Timeout",r[r.SendFailed=-32002]="SendFailed",r[r.InvalidResponse=-32003]="InvalidResponse",r))(W||{}),m=class o extends Error{constructor(t,e=-32603,s,n,i){super(t),this.name=n||"NexusError",this.code=e,this.data=s,i&&(this.stack=i),Object.setPrototypeOf(this,o.prototype)}},I=class{constructor(t,e){this.messageQueue=[];this.maxQueueSize=100;this.requestInterceptors=[];this.responseInterceptors=[];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 f("MessageNexus",s,n),e?.logger&&_(e.logger)){let i=e.logger;this.logger.addHandler(a=>{let{level:g,message:r,metadata:l}=a;g==="debug"?i.debug(r,l):g==="info"?i.info(r,l):g==="warn"?i.warn(r,l):g==="error"&&i.error(r,l)})}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.driver.onMessage=i=>this._handleIncoming(i),this.driver.onConnect=()=>{this.logger.info("Driver connected, flushing message queue"),this.flushQueue()}}async invoke(t){let e=crypto.randomUUID(),s,n,i,a,g,r=0,l=1e3;if(typeof t=="string")s=t,n=void 0,i=void 0,a={},g=this.timeout;else{let c=t;s=c.method,n=c.params,i=c.to,a=c.metadata||{},g=c.timeout??this.timeout,r=c.retryCount??0,l=c.retryDelay??1e3}let u=async c=>new Promise((k,v)=>{let N=setTimeout(()=>{this.pendingTasks.delete(e),this.metrics.messagesFailed++,this.metrics.pendingMessages--,v(new m(`Message timeout: ${s} (${e})`,-32001))},g);this.pendingTasks.set(e,{resolve:k,reject:v,timer:N,timestamp:Date.now()});let O={jsonrpc:"2.0",method:s,params:n,id:e},C={from:this.instanceId,to:i,metadata:{...a,timestamp:Date.now()},payload:O},T=c>=r;this._sendMessage(C,!T).catch(()=>{})}).catch(k=>{if(c<r)return new Promise(v=>setTimeout(()=>v(u(c+1)),l*(c+1)));throw this.metrics.messagesFailed++,this.metrics.pendingMessages--,k});return u(0)}async _sendMessage(t,e=!1){let s=t;try{for(let r of this.requestInterceptors)s=await r(s)}catch(r){this.logger.error("Request interceptor failed",{error:String(r)}),this.metrics.messagesFailed++,this.errorHandler?.(r instanceof Error?r:new Error(String(r)),{message:s});return}let n=s.payload,i="method"in n,a="id"in n?String(n.id):void 0,g=i?n.method:"RESPONSE";try{this.driver.send(s),this.metrics.messagesSent++,i&&a!==void 0&&this.metrics.pendingMessages++,this.logger.debug("Message sent",{messageId:a,type:g})}catch(r){let l=r instanceof Error?r:new Error(String(r));this.metrics.messagesFailed++,this.logger.error("Failed to send message",{error:l.message,messageId:a}),this.errorHandler?.(l,{message:s});let u=typeof DOMException<"u"&&r instanceof DOMException&&r.name==="DataCloneError";!e&&!u?this.messageQueue.length<this.maxQueueSize?(this.messageQueue.push(s),this.logger.debug("Message queued",{messageId:a,queueSize:this.messageQueue.length+1})):(this.logger.warn("Message queue full, dropping oldest message",{queueSize:this.messageQueue.length}),this.messageQueue.shift(),this.messageQueue.push(s)):this.logger.debug(u?"Message dropped due to data error":"Message failed but skipQueue is true (likely retrying)",{messageId:a})}this.metrics.queuedMessages=this.messageQueue.length,this._notifyMetrics()}useRequestInterceptor(t){return this.requestInterceptors.push(t),()=>{this.requestInterceptors=this.requestInterceptors.filter(e=>e!==t)}}useResponseInterceptor(t){return this.responseInterceptors.push(t),()=>{this.responseInterceptors=this.responseInterceptors.filter(e=>e!==t)}}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(e){if(typeof DOMException<"u"&&e instanceof DOMException&&e.name==="DataCloneError"){this.logger.error("Message payload cannot be cloned during flush, dropping",{error:e.message});continue}this.messageQueue.unshift(t);break}}this.metrics.queuedMessages=this.messageQueue.length,this._notifyMetrics()}async notify(t){let e,s,n,i;if(typeof t=="string")e=t,s=void 0,n=void 0,i={};else{let r=t;e=r.method,s=r.params,n=r.to,i=r.metadata||{}}let a={jsonrpc:"2.0",method:e,params:s},g={from:this.instanceId,to:n,metadata:{...i,timestamp:Date.now()},payload:a};await 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;try{for(let n of this.responseInterceptors)e=await n(e)}catch(n){this.logger.error("Response interceptor failed",{error:String(n)}),this.metrics.messagesFailed++,this.errorHandler?.(n instanceof Error?n:new Error(String(n)),{message:e});return}let 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:r,timestamp:l}=this.pendingTasks.get(i);clearTimeout(r),this.pendingTasks.delete(i);let u=Date.now()-l;if(this.metrics.messagesReceived++,this.metrics.pendingMessages--,this.metrics.totalLatency+=u,this.metrics.averageLatency=this.metrics.totalLatency/this.metrics.messagesReceived,this.logger.debug("Response received",{messageId:i,latency:u}),n.error){let c=new m(n.error.message,n.error.code,n.error.data,n.error.name,n.error.stack);g(c)}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 r=await g(n.params,a);await this._reply(i,e.from,r)}catch(r){await this._replyError(i,e.from,r)}else{let r=new m(`Method not found: ${n.method}`,-32601);await this._replyError(i,e.from,r)}}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(r){this.logger.error("Error in notification handler",{error:String(r)})}})}}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))}async _reply(t,e,s){let n={jsonrpc:"2.0",id:t,result:s},i={from:this.instanceId,to:e,payload:n};await this._sendMessage(i)}async _replyError(t,e,s){let n=s instanceof m?s:s instanceof Error?new m(s.message,-32603,void 0,s.name,s.stack):new m(String(s),-32603),i={jsonrpc:"2.0",id:t,error:{code:n.code,message:n.message,data:n.data,name:n.name,stack:n.stack}},a={from:this.instanceId,to:e,payload:i};await this._sendMessage(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.invokeHandlers.clear(),this.notificationHandlers.clear(),this.metricsCallbacks.clear()}};export{d as BaseDriver,y as BroadcastDriver,S as LogLevel,M as MittDriver,m as NexusError,W as NexusErrorCode,R as PostMessageDriver,b as WebSocketDriver,K as createEmitter,I as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "message-nexus",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "A unified, type-safe, multi-protocol cross-context message communication library",
6
6
  "author": "wuyax",
@@ -37,6 +37,7 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^24.10.9",
40
+ "@vitest/coverage-v8": "^4.0.18",
40
41
  "tsup": "^8.4.0",
41
42
  "typescript": "~5.9.3",
42
43
  "vitest": "^4.0.18",
@@ -58,6 +59,7 @@
58
59
  "dev": "tsup --watch",
59
60
  "test": "vitest",
60
61
  "test:run": "vitest run",
62
+ "test:coverage": "vitest run --coverage",
61
63
  "type-check": "tsc --noEmit"
62
64
  }
63
65
  }