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 +1 -1
- package/dist/index.d.cts +146 -56
- package/dist/index.d.ts +146 -56
- package/dist/index.js +1 -1
- package/package.json +1 -1
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
|
|
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 :
|
|
275
|
+
} ? P : unknown;
|
|
173
276
|
/**
|
|
174
|
-
* Helper to extract result from a schema or return
|
|
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 :
|
|
281
|
+
} ? R : unknown;
|
|
179
282
|
/**
|
|
180
283
|
* Options for invoking a method.
|
|
181
284
|
*/
|
|
182
|
-
interface InvokeOptions<K extends string = string, P =
|
|
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 =
|
|
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
|
|
262
|
-
private
|
|
263
|
-
private
|
|
264
|
-
private
|
|
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
|
|
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 :
|
|
275
|
+
} ? P : unknown;
|
|
173
276
|
/**
|
|
174
|
-
* Helper to extract result from a schema or return
|
|
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 :
|
|
281
|
+
} ? R : unknown;
|
|
179
282
|
/**
|
|
180
283
|
* Options for invoking a method.
|
|
181
284
|
*/
|
|
182
|
-
interface InvokeOptions<K extends string = string, P =
|
|
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 =
|
|
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
|
|
262
|
-
private
|
|
263
|
-
private
|
|
264
|
-
private
|
|
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};
|