message-nexus 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
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});
1
+ "use strict";var K=Object.create;var S=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var z=Object.getPrototypeOf,G=Object.prototype.hasOwnProperty;var A=(i,e)=>{for(var t in e)S(i,t,{get:e[t],enumerable:!0})},_=(i,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of W(e))!G.call(i,n)&&n!==t&&S(i,n,{get:()=>e[n],enumerable:!(s=F(e,n))||s.enumerable});return i};var Q=(i,e,t)=>(t=i!=null?K(z(i)):{},_(e||!i||!i.__esModule?S(t,"default",{value:i,enumerable:!0}):t,i)),U=i=>_(S({},"__esModule",{value:!0}),i);var Y={};A(Y,{BaseDriver:()=>m,BroadcastDriver:()=>k,EventRouter:()=>w,LogLevel:()=>C,MessageQueue:()=>E,MiddlewarePipeline:()=>M,MittDriver:()=>b,NexusError:()=>g,NexusErrorCode:()=>v,PostMessageDriver:()=>x,RpcScheduler:()=>I,WebSocketDriver:()=>R,createEmitter:()=>B,default:()=>H});module.exports=U(Y);var m=class{constructor(){this.onMessage=null,this.onConnect=null,this.onDisconnect=null}send(e){throw new Error("Not implemented")}destroy(){}};var h="message-nexus-v1";var v=(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[o.InstanceDestroyed=-32004]="InstanceDestroyed",o))(v||{}),g=class i extends Error{constructor(e,t=-32603,s,n,r){super(e),this.name=n||"NexusError",this.code=t,this.data=s,r&&(this.stack=r),Object.setPrototypeOf(this,i.prototype)}};function X(i){return typeof i=="object"&&i!==null&&"__messageBridge"in i&&i.__messageBridge===h}var k=class extends m{constructor(t){super();this.messageHandler=null;if(!t.channel)throw new Error("BroadcastDriver requires a channel name");this.channel=new BroadcastChannel(t.channel),this.messageHandler=s=>{if(!X(s.data))return;let{__messageBridge:n,...r}=s.data;this.onMessage?.(r)},this.channel.addEventListener("message",this.messageHandler)}send(t){let s={...t,__messageBridge:h};try{this.channel.postMessage(s)}catch(n){throw typeof DOMException<"u"&&n instanceof DOMException&&n.name==="DataCloneError"?new g("Message payload cannot be cloned",-32602):n}}destroy(){this.channel&&this.channel.close(),this.messageHandler&&(this.channel.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var D=Symbol("message_nexus_internal"),b=class extends m{constructor(e){super(),this.emitter=e,this.listener=()=>{};let t=s=>{s&&this.onMessage?.(s)};this.emitter.on(D,t),this.listener=()=>{this.emitter.off(D,t)}}send(e){this.emitter.emit(D,e)}destroy(){this.listener(),this.onMessage=null}};function $(i){return typeof i=="object"&&i!==null&&"__messageBridge"in i&&i.__messageBridge===h}var x=class extends m{constructor(t,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=t,this.targetOrigin=s,this.messageHandler=n=>{if(n.origin!==this.targetOrigin||!$(n.data))return;let{__messageBridge:r,...a}=n.data;this.onMessage?.(a)},window.addEventListener("message",this.messageHandler)}send(t){let s={...t,__messageBridge:h};try{this.targetWindow.postMessage(s,this.targetOrigin)}catch(n){throw typeof DOMException<"u"&&n instanceof DOMException&&n.name==="DataCloneError"?new g("Message payload cannot be cloned",-32602):n}}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var C=(n=>(n.DEBUG="debug",n.INFO="info",n.WARN="warn",n.ERROR="error",n))(C||{});function q(i){if(i==null||typeof i!="object")return!1;let e=i;return typeof e.debug=="function"&&typeof e.info=="function"&&typeof e.warn=="function"&&typeof e.error=="function"&&typeof e.addHandler=="function"&&typeof e.setMinLevel=="function"&&typeof e.enable=="function"&&typeof e.disable=="function"&&typeof e.isEnabled=="function"}function P(i){if(i==null||typeof i!="object")return!1;let e=i;return typeof e.debug=="function"&&typeof e.info=="function"&&typeof e.warn=="function"&&typeof e.error=="function"}var y=class{constructor(e,t="info",s=!1){this.handlers=[];this.context=e,this.minLevel=t,this.enabled=s}enable(){this.enabled=!0}disable(){this.enabled=!1}isEnabled(){return this.enabled}addHandler(e){this.handlers.push(e)}setMinLevel(e){this.minLevel=e}shouldLog(e){let t=["debug","info","warn","error"];return t.indexOf(e)>=t.indexOf(this.minLevel)}log(e,t,s){if(!this.enabled||!this.shouldLog(e))return;let n={level:e,timestamp:Date.now(),message:t,metadata:s,context:this.context};this.handlers.forEach(r=>r(n))}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}};function V(){return new Date().toLocaleTimeString("zh-CN",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}var T=()=>i=>{let t=`[${V()}] [${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(t,i.message,i.metadata):s(t,i.message)};var R=class extends m{constructor(t){super();this.ws=null;this.retryCount=0;this.reconnectTimer=null;this.isManuallyClosed=!1;this.url=t.url,this.reconnectEnabled=t.reconnect!==!1,this.maxRetries=(typeof t.reconnect=="object"?t.reconnect.maxRetries:void 0)??1/0,this.retryInterval=(typeof t.reconnect=="object"?t.reconnect.retryInterval:void 0)??5e3,this.logger=t.logger||new y("WebSocketDriver"),this.logger.addHandler(T()),this.onStatusChange=t.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",t=>{try{let s=JSON.parse(t.data);if(typeof s=="object"&&s!==null&&"__messageBridge"in s&&s.__messageBridge===h){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:t.data})}}),this.ws.addEventListener("error",t=>{this.logger.error("WebSocket error",{event:t}),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 t=3e4,s=this.retryInterval*Math.pow(2,this.retryCount),n=Math.min(s,t)+Math.random()*1e3;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(t){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:t});let s={...t,__messageBridge:h};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=Q(require("mitt"),1);function B(){return(0,O.default)()}function f(i,e){try{return i()}catch(t){if(e)try{e(t)}catch(s){console.error("Safe execution error handler failed:",s)}return}}var E=class{constructor(e={}){this.queue=[];this.maxQueueSize=e.maxQueueSize??100,this.logger=e.logger,this.onMessageDropped=e.onMessageDropped}enqueue(e){if(this.queue.length<this.maxQueueSize)this.queue.push(e),this.logger&&"debug"in this.logger&&this.logger.debug("Message queued",{queueSize:this.queue.length});else{this.logger&&"warn"in this.logger&&this.logger.warn("Message queue full, dropping oldest message",{queueSize:this.queue.length});let t=this.queue.shift();t&&this.onMessageDropped&&this.onMessageDropped(t),this.queue.push(e)}}dequeue(){return this.queue.shift()}unshift(e){this.queue.unshift(e)}get length(){return this.queue.length}get isFull(){return this.queue.length>=this.maxQueueSize}get isEmpty(){return this.queue.length===0}clear(){this.queue=[]}};var I=class{constructor(e={}){this.pendingTasks=new Map;this.defaultTimeout=e.defaultTimeout??1e4}createTask(e,t,s=this.defaultTimeout,n){return new Promise((r,a)=>{let l=setTimeout(()=>{this.pendingTasks.delete(e),n&&n(e),a(new g(`Message timeout: ${t} (${e})`,-32001))},s);this.pendingTasks.set(e,{resolve:r,reject:a,timer:l,timestamp:Date.now()})})}hasTask(e){return this.pendingTasks.has(e)}resolveTask(e,t){let s=this.pendingTasks.get(e);if(s){clearTimeout(s.timer),this.pendingTasks.delete(e);let n=Date.now()-s.timestamp;return s.resolve(t),{latency:n}}return null}rejectTask(e,t){let s=this.pendingTasks.get(e);if(s){clearTimeout(s.timer),this.pendingTasks.delete(e);let n=Date.now()-s.timestamp;return s.reject(t),{latency:n}}return null}get size(){return this.pendingTasks.size}clearTasks(e){for(let[t,s]of this.pendingTasks.entries())clearTimeout(s.timer),e!==void 0&&s.reject(e);this.pendingTasks.clear()}};var M=class{constructor(){this.middlewares=[]}use(e){return this.middlewares.push(e),()=>{this.middlewares=this.middlewares.filter(t=>t!==e)}}async execute(e){let t=-1,s=async n=>{if(n<=t)throw new Error("next() called multiple times");t=n;let r=this.middlewares[n];n!==this.middlewares.length&&r&&await r(e,s.bind(null,n+1))};await s(0)}clear(){this.middlewares=[]}get isEmpty(){return this.middlewares.length===0}};var w=class{constructor(){this.invokeHandlers=new Map;this.notificationHandlers=new Map}handle(e,t){return this.invokeHandlers.set(e,t),()=>this.invokeHandlers.delete(e)}removeHandler(e){return this.invokeHandlers.delete(e)}onNotification(e,t){return this.notificationHandlers.has(e)||this.notificationHandlers.set(e,new Set),this.notificationHandlers.get(e).add(t),()=>this.offNotification(e,t)}offNotification(e,t){let s=this.notificationHandlers.get(e);s&&(s.delete(t),s.size===0&&this.notificationHandlers.delete(e))}hasInvokeHandler(e){return this.invokeHandlers.has(e)}getInvokeHandler(e){return this.invokeHandlers.get(e)}getNotificationHandlers(e){return this.notificationHandlers.get(e)}get invokeHandlersCount(){return this.invokeHandlers.size}get notificationHandlersCount(){return this.notificationHandlers.size}clear(e){(!e||e==="invoke")&&this.invokeHandlers.clear(),(!e||e==="notification")&&this.notificationHandlers.clear()}static validateMessage(e){if(!e||typeof e!="object")return!1;let t=e;if(typeof t.from!="string"||t.from.trim()===""||t.to!==void 0&&(typeof t.to!="string"||t.to.trim()==="")||t.metadata!==void 0&&(typeof t.metadata!="object"||t.metadata===null))return!1;let s=t.payload;if(!s||typeof s!="object"||Array.isArray(s)||s.jsonrpc!=="2.0")return!1;if("id"in s){let l=s.id;if(l!==null&&typeof l!="string"&&typeof l!="number")return!1}let n=typeof s.method=="string"&&s.method.trim()!=="",r="result"in s,a=s.error!==void 0&&typeof s.error=="object"&&s.error!==null;return n?!(r||a):r||a?!(!("id"in s)||r&&a||a&&(typeof s.error.code!="number"||typeof s.error.message!="string")):!1}};var H=class{constructor(e,t){this.errorHandler=null;this.metrics={messagesSent:0,messagesReceived:0,messagesFailed:0,pendingMessages:0,queuedMessages:0,totalLatency:0,averageLatency:0};this.metricsCallbacks=new Set;this.metricsThrottleTimer=null;this._isDestroyed=!1;this.driver=e,this.instanceId=t?.instanceId||crypto.randomUUID(),this.timeout=t?.timeout??1e4;let s=t?.logLevel??"info",n=t?.loggerEnabled??!1;if(t?.logger&&q(t.logger))this.logger=t.logger;else if(this.logger=new y("MessageNexus",s,n),t?.logger&&P(t.logger)){let r=t.logger;this.logger.addHandler(a=>{let{level:l,message:c,metadata:o}=a;l==="debug"?r.debug(c,o):l==="info"?r.info(c,o):l==="warn"?r.warn(c,o):l==="error"&&r.error(c,o)})}else n&&this.logger.addHandler(T());this.queue=new E({maxQueueSize:t?.maxQueueSize??100,logger:this.logger,onMessageDropped:r=>{let a=r.payload;if(a&&"id"in a){let l=String(a.id);this.scheduler.rejectTask(l,new g("Message dropped due to queue overflow",-32002))}}}),this.scheduler=new I({defaultTimeout:this.timeout}),this.requestPipeline=new M,this.responsePipeline=new M,this.router=new w,n&&(this.logger.enable(),this.logger.info("MessageNexus initialized",{instanceId:this.instanceId,timeout:this.timeout,logLevel:s})),this.driver.onMessage=r=>this._handleIncoming(r),this.driver.onConnect=()=>{this.logger.info("Driver connected, flushing message queue"),this.flushQueue()}}async invoke(e){let t=crypto.randomUUID(),s,n,r,a,l,c=0,o=1e3;if(typeof e=="string")s=e,n=void 0,r=void 0,a={},l=this.timeout;else{let d=e;s=d.method,n=d.params,r=d.to,a=d.metadata||{},l=d.timeout??this.timeout,c=d.retryCount??0,o=d.retryDelay??1e3}let p=async d=>{try{let L=this.scheduler.createTask(t,s,l,()=>{this.metrics.messagesFailed++}),N={jsonrpc:"2.0",method:s,params:n,id:t},j={from:this.instanceId,to:r,metadata:{...a,timestamp:Date.now()},payload:N},J=d>=c;return this._sendMessage(j,!J).catch(()=>{}),await L}catch(L){if(this._isDestroyed)throw L;if(d<c)return new Promise(N=>setTimeout(()=>N(p(d+1)),o*(d+1)));throw this.metrics.messagesFailed++,L}};return p(0)}async _sendMessage(e,t=!1){let s={message:e,direction:"outbound",nexusInstanceId:this.instanceId};try{this.requestPipeline.isEmpty||await this.requestPipeline.execute(s)}catch(o){this.logger.error("Request interceptor failed",{error:String(o)}),this.metrics.messagesFailed++;let p=o instanceof Error?o:new Error(String(o));f(()=>this.errorHandler?.(p,{message:s.message}));let d=s.message.payload;d&&"id"in d&&this.scheduler.rejectTask(String(d.id),o instanceof Error?o:new Error(String(o)));return}let n=s.message,r=n.payload,a="method"in r,l="id"in r?String(r.id):void 0,c=a?r.method:"RESPONSE";try{this.driver.send(n),this.metrics.messagesSent++,this.logger.debug("Message sent",{messageId:l,type:c})}catch(o){let p=o instanceof Error?o:new Error(String(o));this.metrics.messagesFailed++,this.logger.error("Failed to send message",{error:p.message,messageId:l}),f(()=>this.errorHandler?.(p,{message:n}));let d=p.name==="NexusError"&&p.code===-32602&&p.message==="Message payload cannot be cloned";!t&&!d?this.queue.enqueue(n):this.logger.debug(d?"Message dropped due to data error":"Message failed but skipQueue is true (likely retrying)",{messageId:l})}this._notifyMetrics()}useRequestInterceptor(e){return this.requestPipeline.use(async(t,s)=>{t.message=await Promise.race([e(t.message),new Promise((n,r)=>setTimeout(()=>r(new Error("Request interceptor timed out")),3e3))]),await s()})}useResponseInterceptor(e){return this.responsePipeline.use(async(t,s)=>{t.message=await Promise.race([e(t.message),new Promise((n,r)=>setTimeout(()=>r(new Error("Response interceptor timed out")),3e3))]),await s()})}onError(e){return this.errorHandler=e,()=>{this.errorHandler=null}}flushQueue(){for(;!this.queue.isEmpty;){let e=this.queue.dequeue();if(e)try{this.driver.send(e)}catch(t){let s=t instanceof Error?t:new Error(String(t));if(s.name==="NexusError"&&s.code===-32602&&s.message==="Message payload cannot be cloned"){this.logger.error("Message payload cannot be cloned during flush, dropping",{error:s.message});continue}this.queue.unshift(e);break}}this._notifyMetrics(!0)}async notify(e){let t,s,n,r;if(typeof e=="string")t=e,s=void 0,n=void 0,r={};else{let c=e;t=c.method,s=c.params,n=c.to,r=c.metadata||{}}let a={jsonrpc:"2.0",method:t,params:s},l={from:this.instanceId,to:n,metadata:{...r,timestamp:Date.now()},payload:a};await this._sendMessage(l)}async _handleIncoming(e){if(!w.validateMessage(e)){this.logger.error("Invalid message format received",{data:e}),f(()=>this.errorHandler?.(new Error("Invalid message format received"),{data:e})),this.metrics.messagesFailed++;return}let t={message:e,direction:"inbound",nexusInstanceId:this.instanceId};try{this.responsePipeline.isEmpty||await this.responsePipeline.execute(t)}catch(r){this.logger.error("Response interceptor failed",{error:String(r)}),this.metrics.messagesFailed++;let a=r instanceof Error?r:new Error(String(r));f(()=>this.errorHandler?.(a,{message:t.message}));return}let s=t.message,n=s.payload;if(s.to&&s.to!==this.instanceId){this.logger.debug("Message filtered: not for this instance",{messageId:"id"in n?n.id:void 0,to:s.to,instanceId:this.instanceId});return}if("result"in n||"error"in n){let r=n,a=String(r.id);if(this.scheduler.hasTask(a)){let l;if(r.error){let c=new g(r.error.message,r.error.code,r.error.data,r.error.name,r.error.stack);l=this.scheduler.rejectTask(a,c)}else l=this.scheduler.resolveTask(a,r.result);l&&(this.metrics.messagesReceived++,this.metrics.totalLatency+=l.latency,this.metrics.averageLatency=this.metrics.totalLatency/this.metrics.messagesReceived,this.logger.debug("Response received",{messageId:a,latency:l.latency}),this._notifyMetrics())}else this.logger.warn("Orphaned response received",{messageId:a});return}if("method"in n)if("id"in n){let r=n,a=String(r.id);this.logger.debug("Invoke message received",{messageId:a,type:r.method,from:s.from});let l={messageId:a,from:s.from,to:s.to,metadata:s.metadata},c=this.router.getInvokeHandler(r.method);if(c)try{let o=await c(r.params,l);await this._reply(a,s.from,o)}catch(o){await this._replyError(a,s.from,o)}else{let o=new g(`Method not found: ${r.method}`,-32601);await this._replyError(a,s.from,o)}}else{let r=n;this.logger.debug("Notification message received",{type:r.method,from:s.from});let a={from:s.from,to:s.to,metadata:s.metadata},l=this.router.getNotificationHandlers(r.method);l&&l.forEach(c=>{f(()=>c(r.params,a),o=>{this.logger.error("Error in notification handler",{error:String(o)})})})}}getMetrics(){return{...this.metrics,pendingMessages:this.scheduler.size,queuedMessages:this.queue.length}}getQueueLength(){return this.queue.length}getQueueSnapshot(){return this.queue.queue.slice()}getPendingTasksCount(){return this.scheduler.size}getHandlersCount(){return this.router.invokeHandlersCount}getNotificationMethodsCount(){return this.router.notificationHandlersCount}hasHandler(e){return this.router.hasInvokeHandler(e)}onMetrics(e){return this.metricsCallbacks.add(e),()=>this.metricsCallbacks.delete(e)}_notifyMetrics(e=!1){if(e){this.metricsThrottleTimer&&(clearTimeout(this.metricsThrottleTimer),this.metricsThrottleTimer=null);let t=this.getMetrics();this.metricsCallbacks.forEach(s=>{f(()=>s(t),n=>{this.logger.error("Error in metrics callback",{error:String(n)})})});return}this.metricsThrottleTimer||(this.metricsThrottleTimer=setTimeout(()=>{this.metricsThrottleTimer=null;let t=this.getMetrics();this.metricsCallbacks.forEach(s=>{f(()=>s(t),n=>{this.logger.error("Error in metrics callback",{error:String(n)})})})},100))}handle(e,t){return this.router.hasInvokeHandler(e)&&this.logger.warn(`Overriding existing handler for method: ${e}`),this.router.handle(e,t)}removeHandler(e){this.router.removeHandler(e)}onNotification(e,t){return this.router.onNotification(e,t)}offNotification(e,t){this.router.offNotification(e,t)}async _reply(e,t,s){let n={jsonrpc:"2.0",id:e,result:s},r={from:this.instanceId,to:t,payload:n};await this._sendMessage(r)}async _replyError(e,t,s){let n=s instanceof g?s:s instanceof Error?new g(s.message,-32603,void 0,s.name,s.stack):new g(String(s),-32603),r={jsonrpc:"2.0",id:e,error:{code:n.code,message:n.message,data:n.data,name:n.name,stack:n.stack}},a={from:this.instanceId,to:t,payload:r};await this._sendMessage(a)}clearHandlers(e="all"){e==="all"?this.router.clear():this.router.clear(e),this.logger.info(`Cleared handlers of type: ${e}`)}destroy(){this._isDestroyed=!0,this.metricsThrottleTimer&&(clearTimeout(this.metricsThrottleTimer),this.metricsThrottleTimer=null),this._notifyMetrics(!0),this.logger.info("MessageNexus destroying",{instanceId:this.instanceId,pendingMessages:this.scheduler.size,queuedMessages:this.queue.length,metrics:this.getMetrics()}),this.driver.destroy?.(),this.scheduler.clearTasks(new g("MessageNexus instance destroyed",-32004)),this.queue.clear(),this.router.clear(),this.requestPipeline.clear(),this.responsePipeline.clear(),this.metricsCallbacks.clear()}};0&&(module.exports={BaseDriver,BroadcastDriver,EventRouter,LogLevel,MessageQueue,MiddlewarePipeline,MittDriver,NexusError,NexusErrorCode,PostMessageDriver,RpcScheduler,WebSocketDriver,createEmitter});
package/dist/index.d.cts CHANGED
@@ -146,9 +146,112 @@ declare class WebSocketDriver extends BaseDriver {
146
146
 
147
147
  declare function createEmitter(): Emitter<Record<EventType, Message>>;
148
148
 
149
+ /**
150
+ * Standard JSON-RPC 2.0 and Nexus-specific error codes.
151
+ */
152
+ declare enum NexusErrorCode {
153
+ ParseError = -32700,
154
+ InvalidRequest = -32600,
155
+ MethodNotFound = -32601,
156
+ InvalidParams = -32602,
157
+ InternalError = -32603,
158
+ Timeout = -32001,
159
+ SendFailed = -32002,
160
+ InvalidResponse = -32003,
161
+ InstanceDestroyed = -32004
162
+ }
163
+ /**
164
+ * Custom error class for MessageNexus.
165
+ */
166
+ declare class NexusError<D = any> extends Error {
167
+ readonly code: number;
168
+ readonly data?: D;
169
+ constructor(message: string, code?: number, data?: D, name?: string, stack?: string);
170
+ }
171
+
172
+ interface MessageQueueOptions {
173
+ maxQueueSize?: number;
174
+ logger?: LoggerInterface | SimpleLogger;
175
+ onMessageDropped?: (message: Message) => void;
176
+ }
177
+ declare class MessageQueue {
178
+ private queue;
179
+ private maxQueueSize;
180
+ private logger?;
181
+ private onMessageDropped?;
182
+ constructor(options?: MessageQueueOptions);
183
+ enqueue(message: Message): void;
184
+ dequeue(): Message | undefined;
185
+ unshift(message: Message): void;
186
+ get length(): number;
187
+ get isFull(): boolean;
188
+ get isEmpty(): boolean;
189
+ clear(): void;
190
+ }
191
+
192
+ interface RpcSchedulerOptions {
193
+ defaultTimeout?: number;
194
+ }
195
+ declare class RpcScheduler {
196
+ private pendingTasks;
197
+ private defaultTimeout;
198
+ constructor(options?: RpcSchedulerOptions);
199
+ createTask<R = unknown>(id: string, method: string, timeout?: number, onTimeout?: (id: string) => void): Promise<R>;
200
+ hasTask(id: string): boolean;
201
+ resolveTask(id: string, result: unknown): {
202
+ latency: number;
203
+ } | null;
204
+ rejectTask(id: string, error: unknown): {
205
+ latency: number;
206
+ } | null;
207
+ get size(): number;
208
+ clearTasks(error?: unknown): void;
209
+ }
210
+
211
+ type MiddlewareContext = {
212
+ message: Message;
213
+ direction: 'inbound' | 'outbound';
214
+ nexusInstanceId: string;
215
+ [key: string]: unknown;
216
+ };
217
+ type Next = () => Promise<void>;
218
+ type Middleware = (ctx: MiddlewareContext, next: Next) => Promise<void>;
219
+ declare class MiddlewarePipeline {
220
+ private middlewares;
221
+ use(middleware: Middleware): () => void;
222
+ execute(ctx: MiddlewareContext): Promise<void>;
223
+ clear(): void;
224
+ get isEmpty(): boolean;
225
+ }
226
+
227
+ interface InvokeContext {
228
+ messageId?: string;
229
+ from: string;
230
+ to?: string;
231
+ metadata?: Record<string, unknown>;
232
+ }
233
+ type InvokeHandler<P = unknown, R = unknown> = (params: P, context: InvokeContext) => R | Promise<R>;
234
+ type NotificationHandler<P = unknown> = (params: P, context: InvokeContext) => void;
235
+ declare class EventRouter<InvokeMap extends object = Record<string, any>, NotificationMap extends object = Record<string, any>> {
236
+ private invokeHandlers;
237
+ private notificationHandlers;
238
+ handle<K extends keyof InvokeMap>(method: string, handler: InvokeHandler<any, any>): () => boolean;
239
+ removeHandler(method: string): boolean;
240
+ onNotification<K extends keyof NotificationMap>(method: string, handler: NotificationHandler<any>): () => void;
241
+ offNotification<K extends keyof NotificationMap>(method: string, handler: NotificationHandler<any>): void;
242
+ hasInvokeHandler(method: string): boolean;
243
+ getInvokeHandler(method: string): InvokeHandler | undefined;
244
+ getNotificationHandlers(method: string): Set<NotificationHandler> | undefined;
245
+ get invokeHandlersCount(): number;
246
+ get notificationHandlersCount(): number;
247
+ clear(type?: 'invoke' | 'notification'): void;
248
+ static validateMessage(data: unknown): data is Message;
249
+ }
250
+
149
251
  interface MessageNexusOptions {
150
252
  instanceId?: string;
151
253
  timeout?: number;
254
+ maxQueueSize?: number;
152
255
  logger?: LoggerInterface | SimpleLogger;
153
256
  loggerEnabled?: boolean;
154
257
  logLevel?: LogLevel;
@@ -165,21 +268,21 @@ interface MethodSchema {
165
268
  */
166
269
  type DefaultRegistry = Record<string, MethodSchema>;
167
270
  /**
168
- * Helper to extract params from a schema or return any if not present.
271
+ * Helper to extract params from a schema or return unknown if not present.
169
272
  */
170
273
  type GetParams<T> = T extends {
171
274
  params: infer P;
172
- } ? P : any;
275
+ } ? P : unknown;
173
276
  /**
174
- * Helper to extract result from a schema or return any if not present.
277
+ * Helper to extract result from a schema or return unknown if not present.
175
278
  */
176
279
  type GetResult<T> = T extends {
177
280
  result: infer R;
178
- } ? R : any;
281
+ } ? R : unknown;
179
282
  /**
180
283
  * Options for invoking a method.
181
284
  */
182
- interface InvokeOptions<K extends string = string, P = any> {
285
+ interface InvokeOptions<K extends string = string, P = unknown> {
183
286
  method: K;
184
287
  params?: P;
185
288
  to?: string;
@@ -191,47 +294,12 @@ interface InvokeOptions<K extends string = string, P = any> {
191
294
  /**
192
295
  * Options for sending a notification.
193
296
  */
194
- interface NotificationOptions<K extends string = string, P = any> {
297
+ interface NotificationOptions<K extends string = string, P = unknown> {
195
298
  method: K;
196
299
  params?: P;
197
300
  to?: string;
198
301
  metadata?: Record<string, unknown>;
199
302
  }
200
- interface InvokeContext {
201
- messageId?: string;
202
- from: string;
203
- to?: string;
204
- metadata?: Record<string, unknown>;
205
- }
206
- /**
207
- * Handler for an invoked method.
208
- */
209
- type InvokeHandler<P = any, R = any> = (params: P, context: InvokeContext) => R | Promise<R>;
210
- /**
211
- * Handler for a notification.
212
- */
213
- type NotificationHandler<P = any> = (params: P, context: InvokeContext) => void;
214
- /**
215
- * Standard JSON-RPC 2.0 and Nexus-specific error codes.
216
- */
217
- declare enum NexusErrorCode {
218
- ParseError = -32700,
219
- InvalidRequest = -32600,
220
- MethodNotFound = -32601,
221
- InvalidParams = -32602,
222
- InternalError = -32603,
223
- Timeout = -32001,
224
- SendFailed = -32002,
225
- InvalidResponse = -32003
226
- }
227
- /**
228
- * Custom error class for MessageNexus.
229
- */
230
- declare class NexusError<D = any> extends Error {
231
- readonly code: number;
232
- readonly data?: D;
233
- constructor(message: string, code?: number, data?: D, name?: string, stack?: string);
234
- }
235
303
  type ErrorHandler = (error: Error | NexusError, context?: Record<string, unknown>) => void;
236
304
  type RequestInterceptor = (message: Message) => Message | Promise<Message>;
237
305
  type ResponseInterceptor = (message: Message) => Message | Promise<Message>;
@@ -247,25 +315,19 @@ interface Metrics {
247
315
  type MetricsCallback = (metrics: Metrics) => void;
248
316
  declare class MessageNexus<InvokeMap extends object = DefaultRegistry, NotificationMap extends object = Record<string, any>> {
249
317
  driver: BaseDriver;
250
- pendingTasks: Map<string, {
251
- resolve: (value: any) => void;
252
- reject: (reason?: unknown) => void;
253
- timer: ReturnType<typeof setTimeout>;
254
- to?: string;
255
- timestamp: number;
256
- }>;
257
- invokeHandlers: Map<string, InvokeHandler>;
258
- notificationHandlers: Map<string, Set<NotificationHandler>>;
259
318
  timeout: number;
260
319
  instanceId: string;
261
- private messageQueue;
262
- private maxQueueSize;
263
- private requestInterceptors;
264
- private responseInterceptors;
320
+ private queue;
321
+ private scheduler;
322
+ private requestPipeline;
323
+ private responsePipeline;
324
+ private router;
265
325
  private errorHandler;
266
326
  private logger;
267
327
  private metrics;
268
328
  private metricsCallbacks;
329
+ private metricsThrottleTimer;
330
+ private _isDestroyed;
269
331
  constructor(driver: BaseDriver, options?: MessageNexusOptions);
270
332
  invoke<K extends keyof InvokeMap>(methodOrOptions: K | InvokeOptions<K & string, GetParams<InvokeMap[K]>>): Promise<GetResult<InvokeMap[K]>>;
271
333
  private _sendMessage;
@@ -274,9 +336,32 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
274
336
  onError(handler: ErrorHandler): () => void;
275
337
  flushQueue(): void;
276
338
  notify<K extends keyof NotificationMap>(methodOrOptions: K | NotificationOptions<K & string, NotificationMap[K]>): Promise<void>;
277
- private _validateMessage;
278
339
  _handleIncoming(data: unknown): Promise<void>;
279
340
  getMetrics(): Metrics;
341
+ /**
342
+ * Returns the number of messages currently in the offline queue.
343
+ */
344
+ getQueueLength(): number;
345
+ /**
346
+ * Returns a snapshot of the current message queue.
347
+ */
348
+ getQueueSnapshot(): Message[];
349
+ /**
350
+ * Returns the number of pending RPC tasks waiting for a response.
351
+ */
352
+ getPendingTasksCount(): number;
353
+ /**
354
+ * Returns the number of registered invoke handlers.
355
+ */
356
+ getHandlersCount(): number;
357
+ /**
358
+ * Returns the number of registered notification methods.
359
+ */
360
+ getNotificationMethodsCount(): number;
361
+ /**
362
+ * Returns true if a specific invoke handler is registered.
363
+ */
364
+ hasHandler(method: string): boolean;
280
365
  onMetrics(callback: MetricsCallback): () => boolean;
281
366
  private _notifyMetrics;
282
367
  handle<K extends keyof InvokeMap>(method: K, handler: InvokeHandler<GetParams<InvokeMap[K]>, GetResult<InvokeMap[K]>>): () => boolean;
@@ -285,7 +370,12 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
285
370
  offNotification<K extends keyof NotificationMap>(method: K, handler: NotificationHandler<NotificationMap[K]>): void;
286
371
  private _reply;
287
372
  private _replyError;
373
+ /**
374
+ * Clears registered handlers by type.
375
+ * @param type The type of handlers to clear: 'invoke', 'notification', or 'all' (default).
376
+ */
377
+ clearHandlers(type?: 'invoke' | 'notification' | 'all'): void;
288
378
  destroy(): void;
289
379
  }
290
380
 
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 };
381
+ export { BaseDriver, BroadcastDriver, type DefaultRegistry, type ErrorHandler, EventRouter, type InvokeContext, type InvokeHandler, type InvokeOptions, LogLevel, type LoggerInterface, type Message, type MessageNexusOptions, MessageQueue, type MethodSchema, type Metrics, type MetricsCallback, type MiddlewareContext, MiddlewarePipeline, MittDriver, NexusError, NexusErrorCode, type NotificationHandler, type NotificationOptions, PostMessageDriver, type RequestInterceptor, type ResponseInterceptor, RpcScheduler, type SimpleLogger, WebSocketDriver, createEmitter, MessageNexus as default };
package/dist/index.d.ts CHANGED
@@ -146,9 +146,112 @@ declare class WebSocketDriver extends BaseDriver {
146
146
 
147
147
  declare function createEmitter(): Emitter<Record<EventType, Message>>;
148
148
 
149
+ /**
150
+ * Standard JSON-RPC 2.0 and Nexus-specific error codes.
151
+ */
152
+ declare enum NexusErrorCode {
153
+ ParseError = -32700,
154
+ InvalidRequest = -32600,
155
+ MethodNotFound = -32601,
156
+ InvalidParams = -32602,
157
+ InternalError = -32603,
158
+ Timeout = -32001,
159
+ SendFailed = -32002,
160
+ InvalidResponse = -32003,
161
+ InstanceDestroyed = -32004
162
+ }
163
+ /**
164
+ * Custom error class for MessageNexus.
165
+ */
166
+ declare class NexusError<D = any> extends Error {
167
+ readonly code: number;
168
+ readonly data?: D;
169
+ constructor(message: string, code?: number, data?: D, name?: string, stack?: string);
170
+ }
171
+
172
+ interface MessageQueueOptions {
173
+ maxQueueSize?: number;
174
+ logger?: LoggerInterface | SimpleLogger;
175
+ onMessageDropped?: (message: Message) => void;
176
+ }
177
+ declare class MessageQueue {
178
+ private queue;
179
+ private maxQueueSize;
180
+ private logger?;
181
+ private onMessageDropped?;
182
+ constructor(options?: MessageQueueOptions);
183
+ enqueue(message: Message): void;
184
+ dequeue(): Message | undefined;
185
+ unshift(message: Message): void;
186
+ get length(): number;
187
+ get isFull(): boolean;
188
+ get isEmpty(): boolean;
189
+ clear(): void;
190
+ }
191
+
192
+ interface RpcSchedulerOptions {
193
+ defaultTimeout?: number;
194
+ }
195
+ declare class RpcScheduler {
196
+ private pendingTasks;
197
+ private defaultTimeout;
198
+ constructor(options?: RpcSchedulerOptions);
199
+ createTask<R = unknown>(id: string, method: string, timeout?: number, onTimeout?: (id: string) => void): Promise<R>;
200
+ hasTask(id: string): boolean;
201
+ resolveTask(id: string, result: unknown): {
202
+ latency: number;
203
+ } | null;
204
+ rejectTask(id: string, error: unknown): {
205
+ latency: number;
206
+ } | null;
207
+ get size(): number;
208
+ clearTasks(error?: unknown): void;
209
+ }
210
+
211
+ type MiddlewareContext = {
212
+ message: Message;
213
+ direction: 'inbound' | 'outbound';
214
+ nexusInstanceId: string;
215
+ [key: string]: unknown;
216
+ };
217
+ type Next = () => Promise<void>;
218
+ type Middleware = (ctx: MiddlewareContext, next: Next) => Promise<void>;
219
+ declare class MiddlewarePipeline {
220
+ private middlewares;
221
+ use(middleware: Middleware): () => void;
222
+ execute(ctx: MiddlewareContext): Promise<void>;
223
+ clear(): void;
224
+ get isEmpty(): boolean;
225
+ }
226
+
227
+ interface InvokeContext {
228
+ messageId?: string;
229
+ from: string;
230
+ to?: string;
231
+ metadata?: Record<string, unknown>;
232
+ }
233
+ type InvokeHandler<P = unknown, R = unknown> = (params: P, context: InvokeContext) => R | Promise<R>;
234
+ type NotificationHandler<P = unknown> = (params: P, context: InvokeContext) => void;
235
+ declare class EventRouter<InvokeMap extends object = Record<string, any>, NotificationMap extends object = Record<string, any>> {
236
+ private invokeHandlers;
237
+ private notificationHandlers;
238
+ handle<K extends keyof InvokeMap>(method: string, handler: InvokeHandler<any, any>): () => boolean;
239
+ removeHandler(method: string): boolean;
240
+ onNotification<K extends keyof NotificationMap>(method: string, handler: NotificationHandler<any>): () => void;
241
+ offNotification<K extends keyof NotificationMap>(method: string, handler: NotificationHandler<any>): void;
242
+ hasInvokeHandler(method: string): boolean;
243
+ getInvokeHandler(method: string): InvokeHandler | undefined;
244
+ getNotificationHandlers(method: string): Set<NotificationHandler> | undefined;
245
+ get invokeHandlersCount(): number;
246
+ get notificationHandlersCount(): number;
247
+ clear(type?: 'invoke' | 'notification'): void;
248
+ static validateMessage(data: unknown): data is Message;
249
+ }
250
+
149
251
  interface MessageNexusOptions {
150
252
  instanceId?: string;
151
253
  timeout?: number;
254
+ maxQueueSize?: number;
152
255
  logger?: LoggerInterface | SimpleLogger;
153
256
  loggerEnabled?: boolean;
154
257
  logLevel?: LogLevel;
@@ -165,21 +268,21 @@ interface MethodSchema {
165
268
  */
166
269
  type DefaultRegistry = Record<string, MethodSchema>;
167
270
  /**
168
- * Helper to extract params from a schema or return any if not present.
271
+ * Helper to extract params from a schema or return unknown if not present.
169
272
  */
170
273
  type GetParams<T> = T extends {
171
274
  params: infer P;
172
- } ? P : any;
275
+ } ? P : unknown;
173
276
  /**
174
- * Helper to extract result from a schema or return any if not present.
277
+ * Helper to extract result from a schema or return unknown if not present.
175
278
  */
176
279
  type GetResult<T> = T extends {
177
280
  result: infer R;
178
- } ? R : any;
281
+ } ? R : unknown;
179
282
  /**
180
283
  * Options for invoking a method.
181
284
  */
182
- interface InvokeOptions<K extends string = string, P = any> {
285
+ interface InvokeOptions<K extends string = string, P = unknown> {
183
286
  method: K;
184
287
  params?: P;
185
288
  to?: string;
@@ -191,47 +294,12 @@ interface InvokeOptions<K extends string = string, P = any> {
191
294
  /**
192
295
  * Options for sending a notification.
193
296
  */
194
- interface NotificationOptions<K extends string = string, P = any> {
297
+ interface NotificationOptions<K extends string = string, P = unknown> {
195
298
  method: K;
196
299
  params?: P;
197
300
  to?: string;
198
301
  metadata?: Record<string, unknown>;
199
302
  }
200
- interface InvokeContext {
201
- messageId?: string;
202
- from: string;
203
- to?: string;
204
- metadata?: Record<string, unknown>;
205
- }
206
- /**
207
- * Handler for an invoked method.
208
- */
209
- type InvokeHandler<P = any, R = any> = (params: P, context: InvokeContext) => R | Promise<R>;
210
- /**
211
- * Handler for a notification.
212
- */
213
- type NotificationHandler<P = any> = (params: P, context: InvokeContext) => void;
214
- /**
215
- * Standard JSON-RPC 2.0 and Nexus-specific error codes.
216
- */
217
- declare enum NexusErrorCode {
218
- ParseError = -32700,
219
- InvalidRequest = -32600,
220
- MethodNotFound = -32601,
221
- InvalidParams = -32602,
222
- InternalError = -32603,
223
- Timeout = -32001,
224
- SendFailed = -32002,
225
- InvalidResponse = -32003
226
- }
227
- /**
228
- * Custom error class for MessageNexus.
229
- */
230
- declare class NexusError<D = any> extends Error {
231
- readonly code: number;
232
- readonly data?: D;
233
- constructor(message: string, code?: number, data?: D, name?: string, stack?: string);
234
- }
235
303
  type ErrorHandler = (error: Error | NexusError, context?: Record<string, unknown>) => void;
236
304
  type RequestInterceptor = (message: Message) => Message | Promise<Message>;
237
305
  type ResponseInterceptor = (message: Message) => Message | Promise<Message>;
@@ -247,25 +315,19 @@ interface Metrics {
247
315
  type MetricsCallback = (metrics: Metrics) => void;
248
316
  declare class MessageNexus<InvokeMap extends object = DefaultRegistry, NotificationMap extends object = Record<string, any>> {
249
317
  driver: BaseDriver;
250
- pendingTasks: Map<string, {
251
- resolve: (value: any) => void;
252
- reject: (reason?: unknown) => void;
253
- timer: ReturnType<typeof setTimeout>;
254
- to?: string;
255
- timestamp: number;
256
- }>;
257
- invokeHandlers: Map<string, InvokeHandler>;
258
- notificationHandlers: Map<string, Set<NotificationHandler>>;
259
318
  timeout: number;
260
319
  instanceId: string;
261
- private messageQueue;
262
- private maxQueueSize;
263
- private requestInterceptors;
264
- private responseInterceptors;
320
+ private queue;
321
+ private scheduler;
322
+ private requestPipeline;
323
+ private responsePipeline;
324
+ private router;
265
325
  private errorHandler;
266
326
  private logger;
267
327
  private metrics;
268
328
  private metricsCallbacks;
329
+ private metricsThrottleTimer;
330
+ private _isDestroyed;
269
331
  constructor(driver: BaseDriver, options?: MessageNexusOptions);
270
332
  invoke<K extends keyof InvokeMap>(methodOrOptions: K | InvokeOptions<K & string, GetParams<InvokeMap[K]>>): Promise<GetResult<InvokeMap[K]>>;
271
333
  private _sendMessage;
@@ -274,9 +336,32 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
274
336
  onError(handler: ErrorHandler): () => void;
275
337
  flushQueue(): void;
276
338
  notify<K extends keyof NotificationMap>(methodOrOptions: K | NotificationOptions<K & string, NotificationMap[K]>): Promise<void>;
277
- private _validateMessage;
278
339
  _handleIncoming(data: unknown): Promise<void>;
279
340
  getMetrics(): Metrics;
341
+ /**
342
+ * Returns the number of messages currently in the offline queue.
343
+ */
344
+ getQueueLength(): number;
345
+ /**
346
+ * Returns a snapshot of the current message queue.
347
+ */
348
+ getQueueSnapshot(): Message[];
349
+ /**
350
+ * Returns the number of pending RPC tasks waiting for a response.
351
+ */
352
+ getPendingTasksCount(): number;
353
+ /**
354
+ * Returns the number of registered invoke handlers.
355
+ */
356
+ getHandlersCount(): number;
357
+ /**
358
+ * Returns the number of registered notification methods.
359
+ */
360
+ getNotificationMethodsCount(): number;
361
+ /**
362
+ * Returns true if a specific invoke handler is registered.
363
+ */
364
+ hasHandler(method: string): boolean;
280
365
  onMetrics(callback: MetricsCallback): () => boolean;
281
366
  private _notifyMetrics;
282
367
  handle<K extends keyof InvokeMap>(method: K, handler: InvokeHandler<GetParams<InvokeMap[K]>, GetResult<InvokeMap[K]>>): () => boolean;
@@ -285,7 +370,12 @@ declare class MessageNexus<InvokeMap extends object = DefaultRegistry, Notificat
285
370
  offNotification<K extends keyof NotificationMap>(method: K, handler: NotificationHandler<NotificationMap[K]>): void;
286
371
  private _reply;
287
372
  private _replyError;
373
+ /**
374
+ * Clears registered handlers by type.
375
+ * @param type The type of handlers to clear: 'invoke', 'notification', or 'all' (default).
376
+ */
377
+ clearHandlers(type?: 'invoke' | 'notification' | 'all'): void;
288
378
  destroy(): void;
289
379
  }
290
380
 
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 };
381
+ export { BaseDriver, BroadcastDriver, type DefaultRegistry, type ErrorHandler, EventRouter, type InvokeContext, type InvokeHandler, type InvokeOptions, LogLevel, type LoggerInterface, type Message, type MessageNexusOptions, MessageQueue, type MethodSchema, type Metrics, type MetricsCallback, type MiddlewareContext, MiddlewarePipeline, MittDriver, NexusError, NexusErrorCode, type NotificationHandler, type NotificationOptions, PostMessageDriver, type RequestInterceptor, type ResponseInterceptor, RpcScheduler, type SimpleLogger, WebSocketDriver, createEmitter, MessageNexus as default };
package/dist/index.js CHANGED
@@ -1 +1 @@
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};
1
+ var p=class{constructor(){this.onMessage=null,this.onConnect=null,this.onDisconnect=null}send(e){throw new Error("Not implemented")}destroy(){}};var f="message-nexus-v1";var M=(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[i.InstanceDestroyed=-32004]="InstanceDestroyed",i))(M||{}),g=class o extends Error{constructor(e,t=-32603,s,n,r){super(e),this.name=n||"NexusError",this.code=t,this.data=s,r&&(this.stack=r),Object.setPrototypeOf(this,o.prototype)}};function B(o){return typeof o=="object"&&o!==null&&"__messageBridge"in o&&o.__messageBridge===f}var x=class extends p{constructor(t){super();this.messageHandler=null;if(!t.channel)throw new Error("BroadcastDriver requires a channel name");this.channel=new BroadcastChannel(t.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(t){let s={...t,__messageBridge:f};try{this.channel.postMessage(s)}catch(n){throw typeof DOMException<"u"&&n instanceof DOMException&&n.name==="DataCloneError"?new g("Message payload cannot be cloned",-32602):n}}destroy(){this.channel&&this.channel.close(),this.messageHandler&&(this.channel.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var N=Symbol("message_nexus_internal"),R=class extends p{constructor(e){super(),this.emitter=e,this.listener=()=>{};let t=s=>{s&&this.onMessage?.(s)};this.emitter.on(N,t),this.listener=()=>{this.emitter.off(N,t)}}send(e){this.emitter.emit(N,e)}destroy(){this.listener(),this.onMessage=null}};function j(o){return typeof o=="object"&&o!==null&&"__messageBridge"in o&&o.__messageBridge===f}var E=class extends p{constructor(t,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=t,this.targetOrigin=s,this.messageHandler=n=>{if(n.origin!==this.targetOrigin||!j(n.data))return;let{__messageBridge:r,...a}=n.data;this.onMessage?.(a)},window.addEventListener("message",this.messageHandler)}send(t){let s={...t,__messageBridge:f};try{this.targetWindow.postMessage(s,this.targetOrigin)}catch(n){throw typeof DOMException<"u"&&n instanceof DOMException&&n.name==="DataCloneError"?new g("Message payload cannot be cloned",-32602):n}}destroy(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.onMessage=null}};var C=(n=>(n.DEBUG="debug",n.INFO="info",n.WARN="warn",n.ERROR="error",n))(C||{});function _(o){if(o==null||typeof o!="object")return!1;let e=o;return typeof e.debug=="function"&&typeof e.info=="function"&&typeof e.warn=="function"&&typeof e.error=="function"&&typeof e.addHandler=="function"&&typeof e.setMinLevel=="function"&&typeof e.enable=="function"&&typeof e.disable=="function"&&typeof e.isEnabled=="function"}function q(o){if(o==null||typeof o!="object")return!1;let e=o;return typeof e.debug=="function"&&typeof e.info=="function"&&typeof e.warn=="function"&&typeof e.error=="function"}var y=class{constructor(e,t="info",s=!1){this.handlers=[];this.context=e,this.minLevel=t,this.enabled=s}enable(){this.enabled=!0}disable(){this.enabled=!1}isEnabled(){return this.enabled}addHandler(e){this.handlers.push(e)}setMinLevel(e){this.minLevel=e}shouldLog(e){let t=["debug","info","warn","error"];return t.indexOf(e)>=t.indexOf(this.minLevel)}log(e,t,s){if(!this.enabled||!this.shouldLog(e))return;let n={level:e,timestamp:Date.now(),message:t,metadata:s,context:this.context};this.handlers.forEach(r=>r(n))}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}};function J(){return new Date().toLocaleTimeString("zh-CN",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3})}var I=()=>o=>{let t=`[${J()}] [${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(t,o.message,o.metadata):s(t,o.message)};var L=class extends p{constructor(t){super();this.ws=null;this.retryCount=0;this.reconnectTimer=null;this.isManuallyClosed=!1;this.url=t.url,this.reconnectEnabled=t.reconnect!==!1,this.maxRetries=(typeof t.reconnect=="object"?t.reconnect.maxRetries:void 0)??1/0,this.retryInterval=(typeof t.reconnect=="object"?t.reconnect.retryInterval:void 0)??5e3,this.logger=t.logger||new y("WebSocketDriver"),this.logger.addHandler(I()),this.onStatusChange=t.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",t=>{try{let s=JSON.parse(t.data);if(typeof s=="object"&&s!==null&&"__messageBridge"in s&&s.__messageBridge===f){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:t.data})}}),this.ws.addEventListener("error",t=>{this.logger.error("WebSocket error",{event:t}),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 t=3e4,s=this.retryInterval*Math.pow(2,this.retryCount),n=Math.min(s,t)+Math.random()*1e3;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(t){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:t});let s={...t,__messageBridge:f};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 K from"mitt";function F(){return K()}function v(o,e){try{return o()}catch(t){if(e)try{e(t)}catch(s){console.error("Safe execution error handler failed:",s)}return}}var S=class{constructor(e={}){this.queue=[];this.maxQueueSize=e.maxQueueSize??100,this.logger=e.logger,this.onMessageDropped=e.onMessageDropped}enqueue(e){if(this.queue.length<this.maxQueueSize)this.queue.push(e),this.logger&&"debug"in this.logger&&this.logger.debug("Message queued",{queueSize:this.queue.length});else{this.logger&&"warn"in this.logger&&this.logger.warn("Message queue full, dropping oldest message",{queueSize:this.queue.length});let t=this.queue.shift();t&&this.onMessageDropped&&this.onMessageDropped(t),this.queue.push(e)}}dequeue(){return this.queue.shift()}unshift(e){this.queue.unshift(e)}get length(){return this.queue.length}get isFull(){return this.queue.length>=this.maxQueueSize}get isEmpty(){return this.queue.length===0}clear(){this.queue=[]}};var T=class{constructor(e={}){this.pendingTasks=new Map;this.defaultTimeout=e.defaultTimeout??1e4}createTask(e,t,s=this.defaultTimeout,n){return new Promise((r,a)=>{let l=setTimeout(()=>{this.pendingTasks.delete(e),n&&n(e),a(new g(`Message timeout: ${t} (${e})`,-32001))},s);this.pendingTasks.set(e,{resolve:r,reject:a,timer:l,timestamp:Date.now()})})}hasTask(e){return this.pendingTasks.has(e)}resolveTask(e,t){let s=this.pendingTasks.get(e);if(s){clearTimeout(s.timer),this.pendingTasks.delete(e);let n=Date.now()-s.timestamp;return s.resolve(t),{latency:n}}return null}rejectTask(e,t){let s=this.pendingTasks.get(e);if(s){clearTimeout(s.timer),this.pendingTasks.delete(e);let n=Date.now()-s.timestamp;return s.reject(t),{latency:n}}return null}get size(){return this.pendingTasks.size}clearTasks(e){for(let[t,s]of this.pendingTasks.entries())clearTimeout(s.timer),e!==void 0&&s.reject(e);this.pendingTasks.clear()}};var w=class{constructor(){this.middlewares=[]}use(e){return this.middlewares.push(e),()=>{this.middlewares=this.middlewares.filter(t=>t!==e)}}async execute(e){let t=-1,s=async n=>{if(n<=t)throw new Error("next() called multiple times");t=n;let r=this.middlewares[n];n!==this.middlewares.length&&r&&await r(e,s.bind(null,n+1))};await s(0)}clear(){this.middlewares=[]}get isEmpty(){return this.middlewares.length===0}};var k=class{constructor(){this.invokeHandlers=new Map;this.notificationHandlers=new Map}handle(e,t){return this.invokeHandlers.set(e,t),()=>this.invokeHandlers.delete(e)}removeHandler(e){return this.invokeHandlers.delete(e)}onNotification(e,t){return this.notificationHandlers.has(e)||this.notificationHandlers.set(e,new Set),this.notificationHandlers.get(e).add(t),()=>this.offNotification(e,t)}offNotification(e,t){let s=this.notificationHandlers.get(e);s&&(s.delete(t),s.size===0&&this.notificationHandlers.delete(e))}hasInvokeHandler(e){return this.invokeHandlers.has(e)}getInvokeHandler(e){return this.invokeHandlers.get(e)}getNotificationHandlers(e){return this.notificationHandlers.get(e)}get invokeHandlersCount(){return this.invokeHandlers.size}get notificationHandlersCount(){return this.notificationHandlers.size}clear(e){(!e||e==="invoke")&&this.invokeHandlers.clear(),(!e||e==="notification")&&this.notificationHandlers.clear()}static validateMessage(e){if(!e||typeof e!="object")return!1;let t=e;if(typeof t.from!="string"||t.from.trim()===""||t.to!==void 0&&(typeof t.to!="string"||t.to.trim()==="")||t.metadata!==void 0&&(typeof t.metadata!="object"||t.metadata===null))return!1;let s=t.payload;if(!s||typeof s!="object"||Array.isArray(s)||s.jsonrpc!=="2.0")return!1;if("id"in s){let l=s.id;if(l!==null&&typeof l!="string"&&typeof l!="number")return!1}let n=typeof s.method=="string"&&s.method.trim()!=="",r="result"in s,a=s.error!==void 0&&typeof s.error=="object"&&s.error!==null;return n?!(r||a):r||a?!(!("id"in s)||r&&a||a&&(typeof s.error.code!="number"||typeof s.error.message!="string")):!1}};var D=class{constructor(e,t){this.errorHandler=null;this.metrics={messagesSent:0,messagesReceived:0,messagesFailed:0,pendingMessages:0,queuedMessages:0,totalLatency:0,averageLatency:0};this.metricsCallbacks=new Set;this.metricsThrottleTimer=null;this._isDestroyed=!1;this.driver=e,this.instanceId=t?.instanceId||crypto.randomUUID(),this.timeout=t?.timeout??1e4;let s=t?.logLevel??"info",n=t?.loggerEnabled??!1;if(t?.logger&&_(t.logger))this.logger=t.logger;else if(this.logger=new y("MessageNexus",s,n),t?.logger&&q(t.logger)){let r=t.logger;this.logger.addHandler(a=>{let{level:l,message:c,metadata:i}=a;l==="debug"?r.debug(c,i):l==="info"?r.info(c,i):l==="warn"?r.warn(c,i):l==="error"&&r.error(c,i)})}else n&&this.logger.addHandler(I());this.queue=new S({maxQueueSize:t?.maxQueueSize??100,logger:this.logger,onMessageDropped:r=>{let a=r.payload;if(a&&"id"in a){let l=String(a.id);this.scheduler.rejectTask(l,new g("Message dropped due to queue overflow",-32002))}}}),this.scheduler=new T({defaultTimeout:this.timeout}),this.requestPipeline=new w,this.responsePipeline=new w,this.router=new k,n&&(this.logger.enable(),this.logger.info("MessageNexus initialized",{instanceId:this.instanceId,timeout:this.timeout,logLevel:s})),this.driver.onMessage=r=>this._handleIncoming(r),this.driver.onConnect=()=>{this.logger.info("Driver connected, flushing message queue"),this.flushQueue()}}async invoke(e){let t=crypto.randomUUID(),s,n,r,a,l,c=0,i=1e3;if(typeof e=="string")s=e,n=void 0,r=void 0,a={},l=this.timeout;else{let d=e;s=d.method,n=d.params,r=d.to,a=d.metadata||{},l=d.timeout??this.timeout,c=d.retryCount??0,i=d.retryDelay??1e3}let h=async d=>{try{let b=this.scheduler.createTask(t,s,l,()=>{this.metrics.messagesFailed++}),H={jsonrpc:"2.0",method:s,params:n,id:t},P={from:this.instanceId,to:r,metadata:{...a,timestamp:Date.now()},payload:H},O=d>=c;return this._sendMessage(P,!O).catch(()=>{}),await b}catch(b){if(this._isDestroyed)throw b;if(d<c)return new Promise(H=>setTimeout(()=>H(h(d+1)),i*(d+1)));throw this.metrics.messagesFailed++,b}};return h(0)}async _sendMessage(e,t=!1){let s={message:e,direction:"outbound",nexusInstanceId:this.instanceId};try{this.requestPipeline.isEmpty||await this.requestPipeline.execute(s)}catch(i){this.logger.error("Request interceptor failed",{error:String(i)}),this.metrics.messagesFailed++;let h=i instanceof Error?i:new Error(String(i));v(()=>this.errorHandler?.(h,{message:s.message}));let d=s.message.payload;d&&"id"in d&&this.scheduler.rejectTask(String(d.id),i instanceof Error?i:new Error(String(i)));return}let n=s.message,r=n.payload,a="method"in r,l="id"in r?String(r.id):void 0,c=a?r.method:"RESPONSE";try{this.driver.send(n),this.metrics.messagesSent++,this.logger.debug("Message sent",{messageId:l,type:c})}catch(i){let h=i instanceof Error?i:new Error(String(i));this.metrics.messagesFailed++,this.logger.error("Failed to send message",{error:h.message,messageId:l}),v(()=>this.errorHandler?.(h,{message:n}));let d=h.name==="NexusError"&&h.code===-32602&&h.message==="Message payload cannot be cloned";!t&&!d?this.queue.enqueue(n):this.logger.debug(d?"Message dropped due to data error":"Message failed but skipQueue is true (likely retrying)",{messageId:l})}this._notifyMetrics()}useRequestInterceptor(e){return this.requestPipeline.use(async(t,s)=>{t.message=await Promise.race([e(t.message),new Promise((n,r)=>setTimeout(()=>r(new Error("Request interceptor timed out")),3e3))]),await s()})}useResponseInterceptor(e){return this.responsePipeline.use(async(t,s)=>{t.message=await Promise.race([e(t.message),new Promise((n,r)=>setTimeout(()=>r(new Error("Response interceptor timed out")),3e3))]),await s()})}onError(e){return this.errorHandler=e,()=>{this.errorHandler=null}}flushQueue(){for(;!this.queue.isEmpty;){let e=this.queue.dequeue();if(e)try{this.driver.send(e)}catch(t){let s=t instanceof Error?t:new Error(String(t));if(s.name==="NexusError"&&s.code===-32602&&s.message==="Message payload cannot be cloned"){this.logger.error("Message payload cannot be cloned during flush, dropping",{error:s.message});continue}this.queue.unshift(e);break}}this._notifyMetrics(!0)}async notify(e){let t,s,n,r;if(typeof e=="string")t=e,s=void 0,n=void 0,r={};else{let c=e;t=c.method,s=c.params,n=c.to,r=c.metadata||{}}let a={jsonrpc:"2.0",method:t,params:s},l={from:this.instanceId,to:n,metadata:{...r,timestamp:Date.now()},payload:a};await this._sendMessage(l)}async _handleIncoming(e){if(!k.validateMessage(e)){this.logger.error("Invalid message format received",{data:e}),v(()=>this.errorHandler?.(new Error("Invalid message format received"),{data:e})),this.metrics.messagesFailed++;return}let t={message:e,direction:"inbound",nexusInstanceId:this.instanceId};try{this.responsePipeline.isEmpty||await this.responsePipeline.execute(t)}catch(r){this.logger.error("Response interceptor failed",{error:String(r)}),this.metrics.messagesFailed++;let a=r instanceof Error?r:new Error(String(r));v(()=>this.errorHandler?.(a,{message:t.message}));return}let s=t.message,n=s.payload;if(s.to&&s.to!==this.instanceId){this.logger.debug("Message filtered: not for this instance",{messageId:"id"in n?n.id:void 0,to:s.to,instanceId:this.instanceId});return}if("result"in n||"error"in n){let r=n,a=String(r.id);if(this.scheduler.hasTask(a)){let l;if(r.error){let c=new g(r.error.message,r.error.code,r.error.data,r.error.name,r.error.stack);l=this.scheduler.rejectTask(a,c)}else l=this.scheduler.resolveTask(a,r.result);l&&(this.metrics.messagesReceived++,this.metrics.totalLatency+=l.latency,this.metrics.averageLatency=this.metrics.totalLatency/this.metrics.messagesReceived,this.logger.debug("Response received",{messageId:a,latency:l.latency}),this._notifyMetrics())}else this.logger.warn("Orphaned response received",{messageId:a});return}if("method"in n)if("id"in n){let r=n,a=String(r.id);this.logger.debug("Invoke message received",{messageId:a,type:r.method,from:s.from});let l={messageId:a,from:s.from,to:s.to,metadata:s.metadata},c=this.router.getInvokeHandler(r.method);if(c)try{let i=await c(r.params,l);await this._reply(a,s.from,i)}catch(i){await this._replyError(a,s.from,i)}else{let i=new g(`Method not found: ${r.method}`,-32601);await this._replyError(a,s.from,i)}}else{let r=n;this.logger.debug("Notification message received",{type:r.method,from:s.from});let a={from:s.from,to:s.to,metadata:s.metadata},l=this.router.getNotificationHandlers(r.method);l&&l.forEach(c=>{v(()=>c(r.params,a),i=>{this.logger.error("Error in notification handler",{error:String(i)})})})}}getMetrics(){return{...this.metrics,pendingMessages:this.scheduler.size,queuedMessages:this.queue.length}}getQueueLength(){return this.queue.length}getQueueSnapshot(){return this.queue.queue.slice()}getPendingTasksCount(){return this.scheduler.size}getHandlersCount(){return this.router.invokeHandlersCount}getNotificationMethodsCount(){return this.router.notificationHandlersCount}hasHandler(e){return this.router.hasInvokeHandler(e)}onMetrics(e){return this.metricsCallbacks.add(e),()=>this.metricsCallbacks.delete(e)}_notifyMetrics(e=!1){if(e){this.metricsThrottleTimer&&(clearTimeout(this.metricsThrottleTimer),this.metricsThrottleTimer=null);let t=this.getMetrics();this.metricsCallbacks.forEach(s=>{v(()=>s(t),n=>{this.logger.error("Error in metrics callback",{error:String(n)})})});return}this.metricsThrottleTimer||(this.metricsThrottleTimer=setTimeout(()=>{this.metricsThrottleTimer=null;let t=this.getMetrics();this.metricsCallbacks.forEach(s=>{v(()=>s(t),n=>{this.logger.error("Error in metrics callback",{error:String(n)})})})},100))}handle(e,t){return this.router.hasInvokeHandler(e)&&this.logger.warn(`Overriding existing handler for method: ${e}`),this.router.handle(e,t)}removeHandler(e){this.router.removeHandler(e)}onNotification(e,t){return this.router.onNotification(e,t)}offNotification(e,t){this.router.offNotification(e,t)}async _reply(e,t,s){let n={jsonrpc:"2.0",id:e,result:s},r={from:this.instanceId,to:t,payload:n};await this._sendMessage(r)}async _replyError(e,t,s){let n=s instanceof g?s:s instanceof Error?new g(s.message,-32603,void 0,s.name,s.stack):new g(String(s),-32603),r={jsonrpc:"2.0",id:e,error:{code:n.code,message:n.message,data:n.data,name:n.name,stack:n.stack}},a={from:this.instanceId,to:t,payload:r};await this._sendMessage(a)}clearHandlers(e="all"){e==="all"?this.router.clear():this.router.clear(e),this.logger.info(`Cleared handlers of type: ${e}`)}destroy(){this._isDestroyed=!0,this.metricsThrottleTimer&&(clearTimeout(this.metricsThrottleTimer),this.metricsThrottleTimer=null),this._notifyMetrics(!0),this.logger.info("MessageNexus destroying",{instanceId:this.instanceId,pendingMessages:this.scheduler.size,queuedMessages:this.queue.length,metrics:this.getMetrics()}),this.driver.destroy?.(),this.scheduler.clearTasks(new g("MessageNexus instance destroyed",-32004)),this.queue.clear(),this.router.clear(),this.requestPipeline.clear(),this.responsePipeline.clear(),this.metricsCallbacks.clear()}};export{p as BaseDriver,x as BroadcastDriver,k as EventRouter,C as LogLevel,S as MessageQueue,w as MiddlewarePipeline,R as MittDriver,g as NexusError,M as NexusErrorCode,E as PostMessageDriver,T as RpcScheduler,L as WebSocketDriver,F as createEmitter,D as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "message-nexus",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "description": "A unified, type-safe, multi-protocol cross-context message communication library",
6
6
  "author": "wuyax",