@xentobias/worker-rpc 1.0.14 → 1.0.15
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/endpoint.d.ts +11 -0
- package/dist/index.js +1 -1
- package/dist/proxy.d.ts +2 -0
- package/package.json +1 -1
package/dist/endpoint.d.ts
CHANGED
|
@@ -80,8 +80,12 @@ export declare class Endpoint {
|
|
|
80
80
|
protected remoteCallbacks: Map<`cb:${string}`, Function>;
|
|
81
81
|
/** Message handler bound to this instance */
|
|
82
82
|
private boundMessageHandler;
|
|
83
|
+
/** Close/error handler bound to this instance (detects worker death) */
|
|
84
|
+
private boundCloseHandler;
|
|
83
85
|
/** Whether this endpoint has been released */
|
|
84
86
|
protected released: boolean;
|
|
87
|
+
/** Release handlers from returned objects with [CALLBACK_RELEASE] */
|
|
88
|
+
protected releaseHandlers: Set<() => void>;
|
|
85
89
|
constructor(target: MessageTarget, options?: EndpointOptions);
|
|
86
90
|
/**
|
|
87
91
|
* Generate a unique call ID for this endpoint
|
|
@@ -99,6 +103,13 @@ export declare class Endpoint {
|
|
|
99
103
|
* Detach the message listener from the target
|
|
100
104
|
*/
|
|
101
105
|
private detachListener;
|
|
106
|
+
/**
|
|
107
|
+
* Handle target close/error events (worker death or port disconnection).
|
|
108
|
+
* This triggers the same cleanup as receiving an EndpointRelease message,
|
|
109
|
+
* but is invoked by transport-level events when the remote side dies
|
|
110
|
+
* without sending a proper release message.
|
|
111
|
+
*/
|
|
112
|
+
private handleTargetClose;
|
|
102
113
|
/**
|
|
103
114
|
* Log a debug message
|
|
104
115
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var B;((K)=>{K[K.Call=0]="Call";K[K.Result=1]="Result";K[K.Error=2]="Error";K[K.Callback=3]="Callback";K[K.CallbackRelease=4]="CallbackRelease";K[K.EndpointRelease=5]="EndpointRelease"})(B||={});var h=new Set(["message","name","stack","cause"]);function Y(q){let G={e:q.message,n:q.name,s:q.stack},Q={};for(let Z of Object.keys(q))if(!h.has(Z))Q[Z]=q[Z];if(Object.keys(Q).length>0)G.d=Q;if(q.cause instanceof Error)G.c=Y(q.cause);else if(q.cause!==void 0)G.c={e:String(q.cause),d:{cause:q.cause}};return G}function H(q){let G=q.c?{cause:H(q.c)}:void 0,Q=Error(q.e,G);if(q.n)Q.name=q.n;if(q.s)Q.stack=q.s;if(q.d)Object.assign(Q,q.d);return Q}var O=30000;class L{static generateRandomId(){return Math.random().toString(36).slice(2,8)}static extractEndpointId(q){return q.split(":")[1]??""}target;options;exposedApi=null;exposeOptions={};id;callCounter=0;callbackCounter=0;pendingCalls=new Map;callbacks=new Map;remoteCallbacks=new Map;boundMessageHandler;released=!1;constructor(q,G={}){this.target=q,this.id=G.id??L.generateRandomId(),this.options={id:this.id,timeout:G.timeout??O,onError:G.onError??console.error,debug:G.debug??!1,onRelease:G.onRelease??(()=>{})},this.boundMessageHandler=this.handleMessage.bind(this),this.attachListener()}generateCallId(){return`c:${this.id}:${++this.callCounter}`}generateCallbackId(){return`cb:${this.id}:${++this.callbackCounter}`}attachListener(){if(this.target.addEventListener)this.target.addEventListener("message",this.boundMessageHandler);else if(this.target.onmessage!==void 0)this.target.onmessage=this.boundMessageHandler}detachListener(){if(this.target.removeEventListener)this.target.removeEventListener("message",this.boundMessageHandler);else if(this.target.onmessage!==void 0)this.target.onmessage=null}debug(...q){if(this.options.debug)console.log("[worker-rpc]",...q)}expose(q,G={}){this.exposedApi=q,this.exposeOptions={maxDepth:G.maxDepth??10}}handleMessage(q){let G=q.data;if(typeof G!=="object"||G===null||!("t"in G))return;switch(this.debug("received",B[G.t],G),G.t){case 0:this.handleCall(G);break;case 1:this.handleResult(G);break;case 2:this.handleError(G);break;case 3:this.handleCallback(G);break;case 4:this.handleCallbackRelease(G);break;case 5:this.handleEndpointRelease();break}}async handleCall(q){let{id:G,p:Q,a:Z,c:$}=q;try{let{method:J,thisArg:K}=this.resolveMethod(Q);if(typeof J!=="function")throw Error(`Method not found: ${Q.join(".")}`);let W=Z.map((C,X)=>{let x=$?.[X];if(x)return this.createRemoteCallback(x);return C}),V=await J.apply(K,W),{rawValue:U,callbackMap:S}=this.processResult(V);this.send({t:1,id:G,v:U,...Object.keys(S).length>0&&{cm:S}})}catch(J){let K=J instanceof Error?J:Error(String(J));this.send({t:2,id:G,...Y(K)})}}resolveMethod(q){if(!this.exposedApi)throw Error("No API exposed");let G=this.exposedApi,Q=null;for(let Z=0;Z<q.length;Z++){let $=q[Z];if(G===null||G===void 0)throw Error(`Cannot access property '${$}' of ${G}`);if($===void 0)throw Error(`Invalid path at index ${Z}`);if(Q=G,G=G[$],Z>=(this.exposeOptions.maxDepth??10))throw Error("Maximum nesting depth exceeded")}return{target:this.exposedApi,method:G,thisArg:Q}}handleResult(q){let G=this.pendingCalls.get(q.id);if(!G){this.debug("Received result for unknown call:",q.id);return}if(this.pendingCalls.delete(q.id),G.timer)clearTimeout(G.timer);if(q.cm&&Object.keys(q.cm).length>0)G.resolve(this.reconstructResult(q.v,q.cm));else G.resolve(q.v)}handleError(q){let G=this.pendingCalls.get(q.id);if(!G){this.debug("Received error for unknown call:",q.id);return}if(this.pendingCalls.delete(q.id),G.timer)clearTimeout(G.timer);G.reject(H(q))}async handleCallback(q){let{id:G,c:Q,a:Z,cb:$}=q,J=this.callbacks.get(Q);if(!J){this.send({t:2,id:G,e:`Callback not found: ${Q}`});return}try{let K=Z.map((S,C)=>{let X=$?.[C];if(X)return this.createRemoteCallback(X);return S}),W=await J.fn(...K);if(J.remaining>0){if(J.remaining--,J.remaining===0)this.callbacks.delete(Q)}let{rawValue:V,callbackMap:U}=this.processResult(W);this.send({t:1,id:G,v:V,...Object.keys(U).length>0&&{cm:U}})}catch(K){let W=K instanceof Error?K:Error(String(K));this.send({t:2,id:G,...Y(W)})}}handleCallbackRelease(q){for(let G of q.c)this.callbacks.delete(G)}handleEndpointRelease(){this.debug("Remote endpoint released"),this.options.onRelease(),this.release({silent:!0})}registerCallback(q,G=-1){let Q=this.generateCallbackId();return this.callbacks.set(Q,{fn:q,remaining:G}),Q}createRemoteCallback(q){let G=this.remoteCallbacks.get(q);if(G)return G;return G=async(...Q)=>{return this.invokeCallback(q,Q)},this.remoteCallbacks.set(q,G),G}async invokeCallback(q,G){let Q=this.generateCallId(),Z=[],$={},J=[];for(let K=0;K<G.length;K++){let W=G[K];if(j(W))$[K]=this.registerCallback(W),Z.push(null);else if(D(W))J.push(W),Z.push(W);else Z.push(W)}return new Promise((K,W)=>{let V=setTimeout(()=>{this.pendingCalls.delete(Q),W(Error(`Callback invocation timed out: ${q}`))},this.options.timeout);this.pendingCalls.set(Q,{resolve:K,reject:W,timer:V});let U={t:3,id:Q,c:q,a:Z,...Object.keys($).length>0&&{cb:$}};this.send(U,J)})}processArgs(q){let G=[],Q={},Z=[];for(let $=0;$<q.length;$++){let J=q[$];if(j(J))Q[$]=this.registerCallback(J),G.push(null);else if(D(J))Z.push(J),G.push(J);else G.push(J)}return{rawArgs:G,callbackMap:Q,transferables:Z}}processResult(q){if(j(q))return{rawValue:null,callbackMap:{"":this.registerCallback(q)}};if(q!==null&&typeof q==="object"&&!Array.isArray(q)&&!D(q)&&!(q instanceof ArrayBuffer)){let G={},Q={};for(let Z in q){let $=q[Z];if(j($))G[Z]=this.registerCallback($);else Q[Z]=$}if(Object.keys(G).length>0)return{rawValue:Q,callbackMap:G}}return{rawValue:q,callbackMap:{}}}reconstructResult(q,G){if(""in G)return this.createRemoteCallback(G[""]);let Q=q&&typeof q==="object"?{...q}:{};for(let Z in G){let $=G[Z];if($)Q[Z]=this.createRemoteCallback($)}return Q}buildCallMessage(q,G,Q,Z){return{t:0,id:q,p:G,a:Q,...Object.keys(Z).length>0&&{c:Z}}}call(q,G){if(this.released)return Promise.reject(Error("Endpoint has been released"));let{rawArgs:Q,callbackMap:Z,transferables:$}=this.processArgs(G),J=this.generateCallId(),K=this.buildCallMessage(J,q,Q,Z);return new Promise((W,V)=>{let U=setTimeout(()=>{this.pendingCalls.delete(J),V(Error(`Call timed out: ${q.join(".")}`))},this.options.timeout);this.pendingCalls.set(J,{resolve:W,reject:V,timer:U}),this.send(K,$)})}send(q,G=[]){this.debug("sending",B[q.t],q),this.target.postMessage(q,G)}getPendingCallCount(){return this.pendingCalls.size}hasPendingCalls(){return this.pendingCalls.size>0}async shutdown(q={}){let{timeout:G=O}=q;if(this.released)return{success:!0,timeout:!1};if(!this.hasPendingCalls())return this.release(),{success:!0,timeout:!1};return new Promise((Q)=>{let Z=!1,$=(K)=>{if(Z)return;if(Z=!0,clearTimeout(J),!this.released)this.release();Q(K)},J=setTimeout(()=>{$({success:!1,timeout:!0})},G);for(let[,K]of this.pendingCalls){let{resolve:W,reject:V}=K;K.resolve=(U)=>{if(W(U),!this.hasPendingCalls())$({success:!0,timeout:!1})},K.reject=(U)=>{if(V(U),!this.hasPendingCalls())$({success:!0,timeout:!1})}}})}release(q={}){if(this.released)return;this.released=!0;for(let[,Q]of this.pendingCalls){if(Q.timer)clearTimeout(Q.timer);Q.reject(Error("Endpoint released"))}this.pendingCalls.clear();let{silent:G=!1}=q;if(!G){if(this.remoteCallbacks.size>0){let Q=[...this.remoteCallbacks.keys()];this.send({t:4,id:this.generateCallId(),c:Q})}this.send({t:5,id:this.generateCallId()})}this.remoteCallbacks.clear(),this.callbacks.clear(),this.detachListener()}getTarget(){return this.target}}function j(q){return typeof q==="function"}function D(q){return typeof MessagePort<"u"&&q instanceof MessagePort}function _(q,G){return new L(q,G)}var z=Symbol("rpc:path"),F=Symbol("rpc:proxy-endpoint"),N=Symbol.for("rpc:remote-proxy"),R=Symbol.for("rpc:endpoint"),f=Symbol.for("rpc:release"),E={get(q,G){if(G===N)return!0;if(G===R)return q[F];if(G===f)return()=>{q[F].release()};if(G==="then"){if(q[z].length===0)return;let Q=q[F],Z=q[z],$=Q.call(Z,[]);return $.then.bind($)}if(G==="toJSON")return;if(G===Symbol.toStringTag||G===Symbol.iterator||G===Symbol.asyncIterator||G==="constructor"||G==="prototype")return;if(typeof G==="string"){let Q=q[F],$=[...q[z],G];return A(Q,$)}},apply(q,G,Q){let Z=q[F],$=q[z];return Z.call($,Q)},set(){throw Error("Cannot set properties on a remote proxy")},deleteProperty(){throw Error("Cannot delete properties on a remote proxy")},getPrototypeOf(){return Function.prototype},has(q,G){return G===N||G===R||G===f}};function A(q,G){let Q=function(){};return Q[F]=q,Q[z]=G,new Proxy(Q,E)}function P(q){return A(q,[])}function w(q){if(q===null||q===void 0)return!1;try{return q[N]===!0}catch{return!1}}function y(q){if(w(q))return q[R]}function u(q){if(w(q))q[f]()}class I extends L{pendingBroadcastCalls=new Map;get broadcastTarget(){return this.target}call(q,G){if(this.released)return Promise.reject(Error("Endpoint has been released"));let{rawArgs:Q,callbackMap:Z,transferables:$}=this.processArgs(G),J=this.generateCallId(),K=this.buildCallMessage(J,q,Q,Z);return this.sendBroadcast(J,K,$,q)}sendBroadcast(q,G,Q,Z){return this.debug("sending broadcast",B[G.t],G),new Promise(($,J)=>{let K={resolve:$,reject:J,timer:setTimeout(()=>this.handleTimeout(q,Z),this.options.timeout),expectedCount:1/0,results:[],errors:[]};this.pendingBroadcastCalls.set(q,K),Promise.resolve(this.broadcastTarget.postMessage(G,Q)).then((W)=>this.handleSubscriberCount(q,W))})}handleTimeout(q,G){let Q=this.pendingBroadcastCalls.get(q);if(!Q)return;this.pendingBroadcastCalls.delete(q);let Z=Q.results.length+Q.errors.length,$=Q.expectedCount===1/0?0:Q.expectedCount-Z;if($>0){let J=Error(`Broadcast timed out: ${G.join(".")}`);J.name="TimeoutError";for(let K=0;K<$;K++)Q.errors.push(J)}if(this.debug(`Broadcast timed out with ${Q.results.length} results, ${Q.errors.length} errors (${$} timed out)`),Q.results.length>0||Q.errors.length>0)Q.resolve({results:Q.results,errors:Q.errors});else Q.reject(Error(`Broadcast timed out: ${G.join(".")}`))}handleSubscriberCount(q,G){let Q=this.pendingBroadcastCalls.get(q);if(!Q)return;if(G===0){this.completeBroadcast(q,Q);return}Q.expectedCount=G,this.checkCompletion(q,Q)}checkCompletion(q,G){if(G.results.length+G.errors.length>=G.expectedCount)this.completeBroadcast(q,G)}completeBroadcast(q,G){this.pendingBroadcastCalls.delete(q),clearTimeout(G.timer),G.resolve({results:G.results,errors:G.errors})}handleResult(q){let G=this.pendingBroadcastCalls.get(q.id);if(!G){super.handleResult(q);return}if(q.cm&&Object.keys(q.cm).length>0)G.results.push(this.reconstructResult(q.v,q.cm));else G.results.push(q.v);this.debug(`Broadcast received ${G.results.length}/${G.expectedCount} responses`),this.checkCompletion(q.id,G)}handleError(q){let G=this.pendingBroadcastCalls.get(q.id);if(!G){super.handleError(q);return}G.errors.push(H(q)),this.debug(`Broadcast received error (${G.results.length} results, ${G.errors.length} errors / ${G.expectedCount} expected)`),this.checkCompletion(q.id,G)}getPendingCallCount(){return super.getPendingCallCount()+this.pendingBroadcastCalls.size}hasPendingCalls(){return super.hasPendingCalls()||this.pendingBroadcastCalls.size>0}release(q={}){for(let[,G]of this.pendingBroadcastCalls)clearTimeout(G.timer),G.reject(Error("Endpoint released"));this.pendingBroadcastCalls.clear(),super.release(q)}}function o(q,G){return new I(q,G)}function n(q,G){let Z=_(globalThis,G);return Z.expose(q),Z}function s(q,G){let Q=_(q,G);return P(Q)}export{P as wrap,Y as serializeError,s as remote,u as releaseProxy,w as isProxy,D as isMessagePort,j as isFunction,y as getEndpoint,n as expose,H as deserializeError,_ as createEndpoint,o as createBroadcastEndpoint,N as REMOTE_PROXY,f as RELEASE,F as PROXY_ENDPOINT,z as PATH,B as MessageType,L as Endpoint,R as ENDPOINT,I as BroadcastEndpoint};
|
|
2
|
+
var B;(($)=>{$[$.Call=0]="Call";$[$.Result=1]="Result";$[$.Error=2]="Error";$[$.Callback=3]="Callback";$[$.CallbackRelease=4]="CallbackRelease";$[$.EndpointRelease=5]="EndpointRelease"})(B||={});var E=new Set(["message","name","stack","cause"]);function Y(q){let G={e:q.message,n:q.name,s:q.stack},Q={};for(let Z of Object.keys(q))if(!E.has(Z))Q[Z]=q[Z];if(Object.keys(Q).length>0)G.d=Q;if(q.cause instanceof Error)G.c=Y(q.cause);else if(q.cause!==void 0)G.c={e:String(q.cause),d:{cause:q.cause}};return G}function z(q){let G=q.c?{cause:z(q.c)}:void 0,Q=Error(q.e,G);if(q.n)Q.name=q.n;if(q.s)Q.stack=q.s;if(q.d)Object.assign(Q,q.d);return Q}var H=Symbol("rpc:path"),F=Symbol("rpc:proxy-endpoint"),_=Symbol.for("rpc:remote-proxy"),C=Symbol.for("rpc:endpoint"),N=Symbol.for("rpc:release"),x=Symbol.for("rpc:callback-release"),T={get(q,G){if(G===_)return!0;if(G===C)return q[F];if(G===N)return()=>{q[F].release()};if(G==="then"){if(q[H].length===0)return;let Q=q[F],Z=q[H],J=Q.call(Z,[]);return J.then.bind(J)}if(G==="toJSON")return;if(G===Symbol.toStringTag||G===Symbol.iterator||G===Symbol.asyncIterator||G==="constructor"||G==="prototype")return;if(typeof G==="string"){let Q=q[F],J=[...q[H],G];return P(Q,J)}},apply(q,G,Q){let Z=q[F],J=q[H];return Z.call(J,Q)},set(){throw Error("Cannot set properties on a remote proxy")},deleteProperty(){throw Error("Cannot delete properties on a remote proxy")},getPrototypeOf(){return Function.prototype},has(q,G){return G===_||G===C||G===N}};function P(q,G){let Q=function(){};return Q[F]=q,Q[H]=G,new Proxy(Q,T)}function w(q){return P(q,[])}function A(q){if(q===null||q===void 0)return!1;try{return q[_]===!0}catch{return!1}}function M(q){if(A(q))return q[C]}function b(q){if(A(q))q[N]()}var I=30000;class S{static generateRandomId(){return Math.random().toString(36).slice(2,8)}static extractEndpointId(q){return q.split(":")[1]??""}target;options;exposedApi=null;exposeOptions={};id;callCounter=0;callbackCounter=0;pendingCalls=new Map;callbacks=new Map;remoteCallbacks=new Map;boundMessageHandler;boundCloseHandler;released=!1;releaseHandlers=new Set;constructor(q,G={}){this.target=q,this.id=G.id??S.generateRandomId(),this.options={id:this.id,timeout:G.timeout??I,onError:G.onError??console.error,debug:G.debug??!1,onRelease:G.onRelease??(()=>{})},this.boundMessageHandler=this.handleMessage.bind(this),this.boundCloseHandler=this.handleTargetClose.bind(this),this.attachListener()}generateCallId(){return`c:${this.id}:${++this.callCounter}`}generateCallbackId(){return`cb:${this.id}:${++this.callbackCounter}`}attachListener(){if(this.target.addEventListener)this.target.addEventListener("message",this.boundMessageHandler),this.target.addEventListener("close",this.boundCloseHandler),this.target.addEventListener("error",this.boundCloseHandler);else if(this.target.onmessage!==void 0)this.target.onmessage=this.boundMessageHandler}detachListener(){if(this.target.removeEventListener)this.target.removeEventListener("message",this.boundMessageHandler),this.target.removeEventListener("close",this.boundCloseHandler),this.target.removeEventListener("error",this.boundCloseHandler);else if(this.target.onmessage!==void 0)this.target.onmessage=null}handleTargetClose(){if(this.released)return;this.debug("Target closed or errored (remote endpoint likely died)");for(let[q,G]of this.pendingCalls){if(G.timer)clearTimeout(G.timer);G.reject(Error("Remote endpoint disconnected"))}this.pendingCalls.clear();for(let q of this.releaseHandlers)try{q()}catch{}this.releaseHandlers.clear(),this.options.onRelease(),this.released=!0,this.remoteCallbacks.clear(),this.callbacks.clear(),this.detachListener()}debug(...q){if(this.options.debug)console.log("[worker-rpc]",...q)}expose(q,G={}){this.exposedApi=q,this.exposeOptions={maxDepth:G.maxDepth??10}}handleMessage(q){let G=q.data;if(typeof G!=="object"||G===null||!("t"in G))return;switch(this.debug("received",B[G.t],G),G.t){case 0:this.handleCall(G);break;case 1:this.handleResult(G);break;case 2:this.handleError(G);break;case 3:this.handleCallback(G);break;case 4:this.handleCallbackRelease(G);break;case 5:this.handleEndpointRelease();break}}async handleCall(q){let{id:G,p:Q,a:Z,c:J}=q;try{let{method:K,thisArg:$}=this.resolveMethod(Q);if(typeof K!=="function")throw Error(`Method not found: ${Q.join(".")}`);let W=Z.map((D,X)=>{let O=J?.[X];if(O)return this.createRemoteCallback(O);return D}),V=await K.apply($,W),{rawValue:U,callbackMap:L}=this.processResult(V);this.send({t:1,id:G,v:U,...Object.keys(L).length>0&&{cm:L}})}catch(K){let $=K instanceof Error?K:Error(String(K));this.send({t:2,id:G,...Y($)})}}resolveMethod(q){if(!this.exposedApi)throw Error("No API exposed");let G=this.exposedApi,Q=null;for(let Z=0;Z<q.length;Z++){let J=q[Z];if(G===null||G===void 0)throw Error(`Cannot access property '${J}' of ${G}`);if(J===void 0)throw Error(`Invalid path at index ${Z}`);if(Q=G,G=G[J],Z>=(this.exposeOptions.maxDepth??10))throw Error("Maximum nesting depth exceeded")}return{target:this.exposedApi,method:G,thisArg:Q}}handleResult(q){let G=this.pendingCalls.get(q.id);if(!G){this.debug("Received result for unknown call:",q.id);return}if(this.pendingCalls.delete(q.id),G.timer)clearTimeout(G.timer);if(q.cm&&Object.keys(q.cm).length>0)G.resolve(this.reconstructResult(q.v,q.cm));else G.resolve(q.v)}handleError(q){let G=this.pendingCalls.get(q.id);if(!G){this.debug("Received error for unknown call:",q.id);return}if(this.pendingCalls.delete(q.id),G.timer)clearTimeout(G.timer);G.reject(z(q))}async handleCallback(q){let{id:G,c:Q,a:Z,cb:J}=q,K=this.callbacks.get(Q);if(!K){this.send({t:2,id:G,e:`Callback not found: ${Q}`});return}try{let $=Z.map((L,D)=>{let X=J?.[D];if(X)return this.createRemoteCallback(X);return L}),W=await K.fn(...$);if(K.remaining>0){if(K.remaining--,K.remaining===0)this.callbacks.delete(Q)}let{rawValue:V,callbackMap:U}=this.processResult(W);this.send({t:1,id:G,v:V,...Object.keys(U).length>0&&{cm:U}})}catch($){let W=$ instanceof Error?$:Error(String($));this.send({t:2,id:G,...Y(W)})}}handleCallbackRelease(q){for(let G of q.c)this.callbacks.delete(G)}handleEndpointRelease(){this.debug("Remote endpoint released");for(let q of this.releaseHandlers)try{q()}catch{}this.releaseHandlers.clear(),this.options.onRelease(),this.release({silent:!0})}registerCallback(q,G=-1){let Q=this.generateCallbackId();return this.callbacks.set(Q,{fn:q,remaining:G}),Q}createRemoteCallback(q){let G=this.remoteCallbacks.get(q);if(G)return G;return G=async(...Q)=>{return this.invokeCallback(q,Q)},this.remoteCallbacks.set(q,G),G}async invokeCallback(q,G){let Q=this.generateCallId(),Z=[],J={},K=[];for(let $=0;$<G.length;$++){let W=G[$];if(j(W))J[$]=this.registerCallback(W),Z.push(null);else if(R(W))K.push(W),Z.push(W);else Z.push(W)}return new Promise(($,W)=>{let V=setTimeout(()=>{this.pendingCalls.delete(Q),W(Error(`Callback invocation timed out: ${q}`))},this.options.timeout);this.pendingCalls.set(Q,{resolve:$,reject:W,timer:V});let U={t:3,id:Q,c:q,a:Z,...Object.keys(J).length>0&&{cb:J}};this.send(U,K)})}processArgs(q){let G=[],Q={},Z=[];for(let J=0;J<q.length;J++){let K=q[J];if(j(K))Q[J]=this.registerCallback(K),G.push(null);else if(R(K))Z.push(K),G.push(K);else G.push(K)}return{rawArgs:G,callbackMap:Q,transferables:Z}}processResult(q){if(j(q))return{rawValue:null,callbackMap:{"":this.registerCallback(q)}};if(q!==null&&typeof q==="object"&&!Array.isArray(q)&&!R(q)&&!(q instanceof ArrayBuffer)){let G={},Q={};for(let J in q){let K=q[J];if(j(K))G[J]=this.registerCallback(K);else Q[J]=K}let Z=q[x];if(typeof Z==="function")this.releaseHandlers.add(Z);if(Object.keys(G).length>0)return{rawValue:Q,callbackMap:G}}return{rawValue:q,callbackMap:{}}}reconstructResult(q,G){if(""in G)return this.createRemoteCallback(G[""]);let Q=q&&typeof q==="object"?{...q}:{};for(let Z in G){let J=G[Z];if(J)Q[Z]=this.createRemoteCallback(J)}return Q}buildCallMessage(q,G,Q,Z){return{t:0,id:q,p:G,a:Q,...Object.keys(Z).length>0&&{c:Z}}}call(q,G){if(this.released)return Promise.reject(Error("Endpoint has been released"));let{rawArgs:Q,callbackMap:Z,transferables:J}=this.processArgs(G),K=this.generateCallId(),$=this.buildCallMessage(K,q,Q,Z);return new Promise((W,V)=>{let U=setTimeout(()=>{this.pendingCalls.delete(K),V(Error(`Call timed out: ${q.join(".")}`))},this.options.timeout);this.pendingCalls.set(K,{resolve:W,reject:V,timer:U}),this.send($,J)})}send(q,G=[]){this.debug("sending",B[q.t],q),this.target.postMessage(q,G)}getPendingCallCount(){return this.pendingCalls.size}hasPendingCalls(){return this.pendingCalls.size>0}async shutdown(q={}){let{timeout:G=I}=q;if(this.released)return{success:!0,timeout:!1};if(!this.hasPendingCalls())return this.release(),{success:!0,timeout:!1};return new Promise((Q)=>{let Z=!1,J=($)=>{if(Z)return;if(Z=!0,clearTimeout(K),!this.released)this.release();Q($)},K=setTimeout(()=>{J({success:!1,timeout:!0})},G);for(let[,$]of this.pendingCalls){let{resolve:W,reject:V}=$;$.resolve=(U)=>{if(W(U),!this.hasPendingCalls())J({success:!0,timeout:!1})},$.reject=(U)=>{if(V(U),!this.hasPendingCalls())J({success:!0,timeout:!1})}}})}release(q={}){if(this.released)return;this.released=!0;for(let[,Q]of this.pendingCalls){if(Q.timer)clearTimeout(Q.timer);Q.reject(Error("Endpoint released"))}this.pendingCalls.clear();let{silent:G=!1}=q;if(!G){if(this.remoteCallbacks.size>0){let Q=[...this.remoteCallbacks.keys()];this.send({t:4,id:this.generateCallId(),c:Q})}this.send({t:5,id:this.generateCallId()})}this.remoteCallbacks.clear(),this.callbacks.clear(),this.releaseHandlers.clear(),this.detachListener()}getTarget(){return this.target}}function j(q){return typeof q==="function"}function R(q){return typeof MessagePort<"u"&&q instanceof MessagePort}function f(q,G){return new S(q,G)}class h extends S{pendingBroadcastCalls=new Map;get broadcastTarget(){return this.target}call(q,G){if(this.released)return Promise.reject(Error("Endpoint has been released"));let{rawArgs:Q,callbackMap:Z,transferables:J}=this.processArgs(G),K=this.generateCallId(),$=this.buildCallMessage(K,q,Q,Z);return this.sendBroadcast(K,$,J,q)}sendBroadcast(q,G,Q,Z){return this.debug("sending broadcast",B[G.t],G),new Promise((J,K)=>{let $={resolve:J,reject:K,timer:setTimeout(()=>this.handleTimeout(q,Z),this.options.timeout),expectedCount:1/0,results:[],errors:[]};this.pendingBroadcastCalls.set(q,$),Promise.resolve(this.broadcastTarget.postMessage(G,Q)).then((W)=>this.handleSubscriberCount(q,W))})}handleTimeout(q,G){let Q=this.pendingBroadcastCalls.get(q);if(!Q)return;this.pendingBroadcastCalls.delete(q);let Z=Q.results.length+Q.errors.length,J=Q.expectedCount===1/0?0:Q.expectedCount-Z;if(J>0){let K=Error(`Broadcast timed out: ${G.join(".")}`);K.name="TimeoutError";for(let $=0;$<J;$++)Q.errors.push(K)}if(this.debug(`Broadcast timed out with ${Q.results.length} results, ${Q.errors.length} errors (${J} timed out)`),Q.results.length>0||Q.errors.length>0)Q.resolve({results:Q.results,errors:Q.errors});else Q.reject(Error(`Broadcast timed out: ${G.join(".")}`))}handleSubscriberCount(q,G){let Q=this.pendingBroadcastCalls.get(q);if(!Q)return;if(G===0){this.completeBroadcast(q,Q);return}Q.expectedCount=G,this.checkCompletion(q,Q)}checkCompletion(q,G){if(G.results.length+G.errors.length>=G.expectedCount)this.completeBroadcast(q,G)}completeBroadcast(q,G){this.pendingBroadcastCalls.delete(q),clearTimeout(G.timer),G.resolve({results:G.results,errors:G.errors})}handleResult(q){let G=this.pendingBroadcastCalls.get(q.id);if(!G){super.handleResult(q);return}if(q.cm&&Object.keys(q.cm).length>0)G.results.push(this.reconstructResult(q.v,q.cm));else G.results.push(q.v);this.debug(`Broadcast received ${G.results.length}/${G.expectedCount} responses`),this.checkCompletion(q.id,G)}handleError(q){let G=this.pendingBroadcastCalls.get(q.id);if(!G){super.handleError(q);return}G.errors.push(z(q)),this.debug(`Broadcast received error (${G.results.length} results, ${G.errors.length} errors / ${G.expectedCount} expected)`),this.checkCompletion(q.id,G)}getPendingCallCount(){return super.getPendingCallCount()+this.pendingBroadcastCalls.size}hasPendingCalls(){return super.hasPendingCalls()||this.pendingBroadcastCalls.size>0}release(q={}){for(let[,G]of this.pendingBroadcastCalls)clearTimeout(G.timer),G.reject(Error("Endpoint released"));this.pendingBroadcastCalls.clear(),super.release(q)}}function g(q,G){return new h(q,G)}function r(q,G){let Z=f(globalThis,G);return Z.expose(q),Z}function a(q,G){let Q=f(q,G);return w(Q)}export{w as wrap,Y as serializeError,a as remote,b as releaseProxy,A as isProxy,R as isMessagePort,j as isFunction,M as getEndpoint,r as expose,z as deserializeError,f as createEndpoint,g as createBroadcastEndpoint,_ as REMOTE_PROXY,N as RELEASE,F as PROXY_ENDPOINT,H as PATH,B as MessageType,S as Endpoint,C as ENDPOINT,x as CALLBACK_RELEASE,h as BroadcastEndpoint};
|
package/dist/proxy.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export declare const REMOTE_PROXY: unique symbol;
|
|
|
11
11
|
export declare const ENDPOINT: unique symbol;
|
|
12
12
|
/** Symbol for releasing a proxy */
|
|
13
13
|
export declare const RELEASE: unique symbol;
|
|
14
|
+
/** Symbol for registering a cleanup handler on a returned object, invoked when the remote endpoint releases */
|
|
15
|
+
export declare const CALLBACK_RELEASE: unique symbol;
|
|
14
16
|
/**
|
|
15
17
|
* Wrap a worker/endpoint to create a type-safe remote API proxy
|
|
16
18
|
*
|