@xentobias/worker-rpc 1.0.11 → 1.0.13

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.
@@ -159,6 +159,18 @@ export declare class Endpoint {
159
159
  callbackMap: Record<number, CallbackId>;
160
160
  transferables: TransferableValue[];
161
161
  };
162
+ /**
163
+ * Process a return value - extracts functions from first-level object properties.
164
+ * For performance, only scans the first level of plain objects.
165
+ */
166
+ protected processResult(value: unknown): {
167
+ rawValue: unknown;
168
+ callbackMap: Record<string, CallbackId>;
169
+ };
170
+ /**
171
+ * Reconstruct a result value by merging data with callback proxies.
172
+ */
173
+ protected reconstructResult(value: unknown, callbackMap: Record<string, CallbackId>): unknown;
162
174
  /**
163
175
  * Build a call message
164
176
  */
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- var U;((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"})(U||={});var _=30000;class S{static generateRandomId(){return Math.random().toString(36).slice(2,8)}static extractEndpointId(z){return z.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(z,q={}){this.target=z,this.id=q.id??S.generateRandomId(),this.options={id:this.id,timeout:q.timeout??_,onError:q.onError??console.error,debug:q.debug??!1,onRelease:q.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(...z){if(this.options.debug)console.log("[worker-rpc]",...z)}expose(z,q={}){this.exposedApi=z,this.exposeOptions={maxDepth:q.maxDepth??10}}handleMessage(z){let q=z.data;if(typeof q!=="object"||q===null||!("t"in q))return;switch(this.debug("received",U[q.t],q),q.t){case 0:this.handleCall(q);break;case 1:this.handleResult(q);break;case 2:this.handleError(q);break;case 3:this.handleCallback(q);break;case 4:this.handleCallbackRelease(q);break;case 5:this.handleEndpointRelease();break}}async handleCall(z){let{id:q,p:G,a:Q,c:V}=z;try{let{method:Z,thisArg:K}=this.resolveMethod(G);if(typeof Z!=="function")throw Error(`Method not found: ${G.join(".")}`);let $=Q.map((W,H)=>{let D=V?.[H];if(D)return this.createRemoteCallback(D);return W}),J=await Z.apply(K,$);if(L(J)){let W=this.registerCallback(J);this.send({t:1,id:q,v:null,c:W})}else this.send({t:1,id:q,v:J})}catch(Z){let K=Z instanceof Error?Z:Error(String(Z));this.send({t:2,id:q,e:K.message,n:K.name,s:K.stack})}}resolveMethod(z){if(!this.exposedApi)throw Error("No API exposed");let q=this.exposedApi,G=null;for(let Q=0;Q<z.length;Q++){let V=z[Q];if(q===null||q===void 0)throw Error(`Cannot access property '${V}' of ${q}`);if(V===void 0)throw Error(`Invalid path at index ${Q}`);if(G=q,q=q[V],Q>=(this.exposeOptions.maxDepth??10))throw Error("Maximum nesting depth exceeded")}return{target:this.exposedApi,method:q,thisArg:G}}handleResult(z){let q=this.pendingCalls.get(z.id);if(!q){this.debug("Received result for unknown call:",z.id);return}if(this.pendingCalls.delete(z.id),q.timer)clearTimeout(q.timer);if(z.c)q.resolve(this.createRemoteCallback(z.c));else q.resolve(z.v)}handleError(z){let q=this.pendingCalls.get(z.id);if(!q){this.debug("Received error for unknown call:",z.id);return}if(this.pendingCalls.delete(z.id),q.timer)clearTimeout(q.timer);let G=Error(z.e);if(z.n)G.name=z.n;if(z.s)G.stack=z.s;q.reject(G)}async handleCallback(z){let{id:q,c:G,a:Q,cb:V}=z,Z=this.callbacks.get(G);if(!Z){this.send({t:2,id:q,e:`Callback not found: ${G}`});return}try{let K=Q.map((J,W)=>{let H=V?.[W];if(H)return this.createRemoteCallback(H);return J}),$=await Z.fn(...K);if(Z.remaining>0){if(Z.remaining--,Z.remaining===0)this.callbacks.delete(G)}if(L($)){let J=this.registerCallback($);this.send({t:1,id:q,v:null,c:J})}else this.send({t:1,id:q,v:$})}catch(K){let $=K instanceof Error?K:Error(String(K));this.send({t:2,id:q,e:$.message,n:$.name,s:$.stack})}}handleCallbackRelease(z){for(let q of z.c)this.callbacks.delete(q)}handleEndpointRelease(){this.debug("Remote endpoint released"),this.options.onRelease(),this.release({silent:!0})}registerCallback(z,q=-1){let G=this.generateCallbackId();return this.callbacks.set(G,{fn:z,remaining:q}),G}createRemoteCallback(z){let q=this.remoteCallbacks.get(z);if(q)return q;return q=async(...G)=>{return this.invokeCallback(z,G)},this.remoteCallbacks.set(z,q),q}async invokeCallback(z,q){let G=this.generateCallId(),Q=[],V={},Z=[];for(let K=0;K<q.length;K++){let $=q[K];if(L($))V[K]=this.registerCallback($),Q.push(null);else if(R($))Z.push($),Q.push($);else Q.push($)}return new Promise((K,$)=>{let J=setTimeout(()=>{this.pendingCalls.delete(G),$(Error(`Callback invocation timed out: ${z}`))},this.options.timeout);this.pendingCalls.set(G,{resolve:K,reject:$,timer:J});let W={t:3,id:G,c:z,a:Q,...Object.keys(V).length>0&&{cb:V}};this.send(W,Z)})}processArgs(z){let q=[],G={},Q=[];for(let V=0;V<z.length;V++){let Z=z[V];if(L(Z))G[V]=this.registerCallback(Z),q.push(null);else if(R(Z))Q.push(Z),q.push(Z);else q.push(Z)}return{rawArgs:q,callbackMap:G,transferables:Q}}buildCallMessage(z,q,G,Q){return{t:0,id:z,p:q,a:G,...Object.keys(Q).length>0&&{c:Q}}}call(z,q){if(this.released)return Promise.reject(Error("Endpoint has been released"));let{rawArgs:G,callbackMap:Q,transferables:V}=this.processArgs(q),Z=this.generateCallId(),K=this.buildCallMessage(Z,z,G,Q);return new Promise(($,J)=>{let W=setTimeout(()=>{this.pendingCalls.delete(Z),J(Error(`Call timed out: ${z.join(".")}`))},this.options.timeout);this.pendingCalls.set(Z,{resolve:$,reject:J,timer:W}),this.send(K,V)})}send(z,q=[]){this.debug("sending",U[z.t],z),this.target.postMessage(z,q)}getPendingCallCount(){return this.pendingCalls.size}hasPendingCalls(){return this.pendingCalls.size>0}async shutdown(z={}){let{timeout:q=_}=z;if(this.released)return{success:!0,timeout:!1};if(!this.hasPendingCalls())return this.release(),{success:!0,timeout:!1};return new Promise((G)=>{let Q=!1,V=(K)=>{if(Q)return;if(Q=!0,clearTimeout(Z),!this.released)this.release();G(K)},Z=setTimeout(()=>{V({success:!1,timeout:!0})},q);for(let[,K]of this.pendingCalls){let{resolve:$,reject:J}=K;K.resolve=(W)=>{if($(W),!this.hasPendingCalls())V({success:!0,timeout:!1})},K.reject=(W)=>{if(J(W),!this.hasPendingCalls())V({success:!0,timeout:!1})}}})}release(z={}){if(this.released)return;this.released=!0;for(let[,G]of this.pendingCalls){if(G.timer)clearTimeout(G.timer);G.reject(Error("Endpoint released"))}this.pendingCalls.clear();let{silent:q=!1}=z;if(!q){if(this.remoteCallbacks.size>0){let G=[...this.remoteCallbacks.keys()];this.send({t:4,id:this.generateCallId(),c:G})}this.send({t:5,id:this.generateCallId()})}this.remoteCallbacks.clear(),this.callbacks.clear(),this.detachListener()}getTarget(){return this.target}}function L(z){return typeof z==="function"}function R(z){return typeof MessagePort<"u"&&z instanceof MessagePort}function C(z,q){return new S(z,q)}var F=Symbol("rpc:path"),B=Symbol("rpc:proxy-endpoint"),X=Symbol.for("rpc:remote-proxy"),Y=Symbol.for("rpc:endpoint"),j=Symbol.for("rpc:release"),v={get(z,q){if(q===X)return!0;if(q===Y)return z[B];if(q===j)return()=>{z[B].release()};if(q==="then"){if(z[F].length===0)return;let G=z[B],Q=z[F],V=G.call(Q,[]);return V.then.bind(V)}if(q==="toJSON")return;if(q===Symbol.toStringTag||q===Symbol.iterator||q===Symbol.asyncIterator||q==="constructor"||q==="prototype")return;if(typeof q==="string"){let G=z[B],V=[...z[F],q];return N(G,V)}},apply(z,q,G){let Q=z[B],V=z[F];return Q.call(V,G)},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(z,q){return q===X||q===Y||q===j}};function N(z,q){let G=function(){};return G[B]=z,G[F]=q,new Proxy(G,v)}function f(z){return N(z,[])}function O(z){if(z===null||z===void 0)return!1;try{return z[X]===!0}catch{return!1}}function I(z){if(O(z))return z[Y]}function h(z){if(O(z))z[j]()}class x extends S{pendingBroadcastCalls=new Map;get broadcastTarget(){return this.target}call(z,q){if(this.released)return Promise.reject(Error("Endpoint has been released"));let{rawArgs:G,callbackMap:Q,transferables:V}=this.processArgs(q),Z=this.generateCallId(),K=this.buildCallMessage(Z,z,G,Q);return this.sendBroadcast(Z,K,V,z)}sendBroadcast(z,q,G,Q){return this.debug("sending broadcast",U[q.t],q),new Promise((V,Z)=>{let K={resolve:V,reject:Z,timer:setTimeout(()=>this.handleTimeout(z,Q),this.options.timeout),expectedCount:1/0,results:[],errors:[]};this.pendingBroadcastCalls.set(z,K),Promise.resolve(this.broadcastTarget.postMessage(q,G)).then(($)=>this.handleSubscriberCount(z,$))})}handleTimeout(z,q){let G=this.pendingBroadcastCalls.get(z);if(!G)return;this.pendingBroadcastCalls.delete(z);let Q=G.results.length+G.errors.length,V=G.expectedCount===1/0?0:G.expectedCount-Q;if(V>0){let Z=Error(`Broadcast timed out: ${q.join(".")}`);Z.name="TimeoutError";for(let K=0;K<V;K++)G.errors.push(Z)}if(this.debug(`Broadcast timed out with ${G.results.length} results, ${G.errors.length} errors (${V} timed out)`),G.results.length>0||G.errors.length>0)G.resolve({results:G.results,errors:G.errors});else G.reject(Error(`Broadcast timed out: ${q.join(".")}`))}handleSubscriberCount(z,q){let G=this.pendingBroadcastCalls.get(z);if(!G)return;if(q===0){this.completeBroadcast(z,G);return}G.expectedCount=q,this.checkCompletion(z,G)}checkCompletion(z,q){if(q.results.length+q.errors.length>=q.expectedCount)this.completeBroadcast(z,q)}completeBroadcast(z,q){this.pendingBroadcastCalls.delete(z),clearTimeout(q.timer),q.resolve({results:q.results,errors:q.errors})}handleResult(z){let q=this.pendingBroadcastCalls.get(z.id);if(!q){super.handleResult(z);return}q.results.push(z.c?this.createRemoteCallback(z.c):z.v),this.debug(`Broadcast received ${q.results.length}/${q.expectedCount} responses`),this.checkCompletion(z.id,q)}handleError(z){let q=this.pendingBroadcastCalls.get(z.id);if(!q){super.handleError(z);return}let G=Error(z.e);if(z.n)G.name=z.n;if(z.s)G.stack=z.s;q.errors.push(G),this.debug(`Broadcast received error (${q.results.length} results, ${q.errors.length} errors / ${q.expectedCount} expected)`),this.checkCompletion(z.id,q)}getPendingCallCount(){return super.getPendingCallCount()+this.pendingBroadcastCalls.size}hasPendingCalls(){return super.hasPendingCalls()||this.pendingBroadcastCalls.size>0}release(z={}){for(let[,q]of this.pendingBroadcastCalls)clearTimeout(q.timer),q.reject(Error("Endpoint released"));this.pendingBroadcastCalls.clear(),super.release(z)}}function M(z,q){return new x(z,q)}function l(z,q){let Q=C(globalThis,q);return Q.expose(z),Q}function c(z,q){let G=C(z,q);return f(G)}export{f as wrap,c as remote,h as releaseProxy,O as isProxy,R as isMessagePort,L as isFunction,I as getEndpoint,l as expose,C as createEndpoint,M as createBroadcastEndpoint,X as REMOTE_PROXY,j as RELEASE,B as PROXY_ENDPOINT,F as PATH,U as MessageType,S as Endpoint,Y as ENDPOINT,x as BroadcastEndpoint};
2
+ var V;((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"})(V||={});var f=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;released=!1;constructor(q,z={}){this.target=q,this.id=z.id??S.generateRandomId(),this.options={id:this.id,timeout:z.timeout??f,onError:z.onError??console.error,debug:z.debug??!1,onRelease:z.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,z={}){this.exposedApi=q,this.exposeOptions={maxDepth:z.maxDepth??10}}handleMessage(q){let z=q.data;if(typeof z!=="object"||z===null||!("t"in z))return;switch(this.debug("received",V[z.t],z),z.t){case 0:this.handleCall(z);break;case 1:this.handleResult(z);break;case 2:this.handleError(z);break;case 3:this.handleCallback(z);break;case 4:this.handleCallbackRelease(z);break;case 5:this.handleEndpointRelease();break}}async handleCall(q){let{id:z,p:G,a:Q,c:Z}=q;try{let{method:$,thisArg:K}=this.resolveMethod(G);if(typeof $!=="function")throw Error(`Method not found: ${G.join(".")}`);let J=Q.map((Y,L)=>{let N=Z?.[L];if(N)return this.createRemoteCallback(N);return Y}),U=await $.apply(K,J),{rawValue:W,callbackMap:H}=this.processResult(U);this.send({t:1,id:z,v:W,...Object.keys(H).length>0&&{cm:H}})}catch($){let K=$ instanceof Error?$:Error(String($));this.send({t:2,id:z,e:K.message,n:K.name,s:K.stack})}}resolveMethod(q){if(!this.exposedApi)throw Error("No API exposed");let z=this.exposedApi,G=null;for(let Q=0;Q<q.length;Q++){let Z=q[Q];if(z===null||z===void 0)throw Error(`Cannot access property '${Z}' of ${z}`);if(Z===void 0)throw Error(`Invalid path at index ${Q}`);if(G=z,z=z[Z],Q>=(this.exposeOptions.maxDepth??10))throw Error("Maximum nesting depth exceeded")}return{target:this.exposedApi,method:z,thisArg:G}}handleResult(q){let z=this.pendingCalls.get(q.id);if(!z){this.debug("Received result for unknown call:",q.id);return}if(this.pendingCalls.delete(q.id),z.timer)clearTimeout(z.timer);if(q.cm&&Object.keys(q.cm).length>0)z.resolve(this.reconstructResult(q.v,q.cm));else z.resolve(q.v)}handleError(q){let z=this.pendingCalls.get(q.id);if(!z){this.debug("Received error for unknown call:",q.id);return}if(this.pendingCalls.delete(q.id),z.timer)clearTimeout(z.timer);let G=Error(q.e);if(q.n)G.name=q.n;if(q.s)G.stack=q.s;z.reject(G)}async handleCallback(q){let{id:z,c:G,a:Q,cb:Z}=q,$=this.callbacks.get(G);if(!$){this.send({t:2,id:z,e:`Callback not found: ${G}`});return}try{let K=Q.map((H,Y)=>{let L=Z?.[Y];if(L)return this.createRemoteCallback(L);return H}),J=await $.fn(...K);if($.remaining>0){if($.remaining--,$.remaining===0)this.callbacks.delete(G)}let{rawValue:U,callbackMap:W}=this.processResult(J);this.send({t:1,id:z,v:U,...Object.keys(W).length>0&&{cm:W}})}catch(K){let J=K instanceof Error?K:Error(String(K));this.send({t:2,id:z,e:J.message,n:J.name,s:J.stack})}}handleCallbackRelease(q){for(let z of q.c)this.callbacks.delete(z)}handleEndpointRelease(){this.debug("Remote endpoint released"),this.options.onRelease(),this.release({silent:!0})}registerCallback(q,z=-1){let G=this.generateCallbackId();return this.callbacks.set(G,{fn:q,remaining:z}),G}createRemoteCallback(q){let z=this.remoteCallbacks.get(q);if(z)return z;return z=async(...G)=>{return this.invokeCallback(q,G)},this.remoteCallbacks.set(q,z),z}async invokeCallback(q,z){let G=this.generateCallId(),Q=[],Z={},$=[];for(let K=0;K<z.length;K++){let J=z[K];if(X(J))Z[K]=this.registerCallback(J),Q.push(null);else if(j(J))$.push(J),Q.push(J);else Q.push(J)}return new Promise((K,J)=>{let U=setTimeout(()=>{this.pendingCalls.delete(G),J(Error(`Callback invocation timed out: ${q}`))},this.options.timeout);this.pendingCalls.set(G,{resolve:K,reject:J,timer:U});let W={t:3,id:G,c:q,a:Q,...Object.keys(Z).length>0&&{cb:Z}};this.send(W,$)})}processArgs(q){let z=[],G={},Q=[];for(let Z=0;Z<q.length;Z++){let $=q[Z];if(X($))G[Z]=this.registerCallback($),z.push(null);else if(j($))Q.push($),z.push($);else z.push($)}return{rawArgs:z,callbackMap:G,transferables:Q}}processResult(q){if(X(q))return{rawValue:null,callbackMap:{"":this.registerCallback(q)}};if(q!==null&&typeof q==="object"&&!Array.isArray(q)&&!j(q)&&!(q instanceof ArrayBuffer)){let z={},G={};for(let Q in q){let Z=q[Q];if(X(Z))z[Q]=this.registerCallback(Z);else G[Q]=Z}if(Object.keys(z).length>0)return{rawValue:G,callbackMap:z}}return{rawValue:q,callbackMap:{}}}reconstructResult(q,z){if(""in z)return this.createRemoteCallback(z[""]);let G=q&&typeof q==="object"?{...q}:{};for(let Q in z){let Z=z[Q];if(Z)G[Q]=this.createRemoteCallback(Z)}return G}buildCallMessage(q,z,G,Q){return{t:0,id:q,p:z,a:G,...Object.keys(Q).length>0&&{c:Q}}}call(q,z){if(this.released)return Promise.reject(Error("Endpoint has been released"));let{rawArgs:G,callbackMap:Q,transferables:Z}=this.processArgs(z),$=this.generateCallId(),K=this.buildCallMessage($,q,G,Q);return new Promise((J,U)=>{let W=setTimeout(()=>{this.pendingCalls.delete($),U(Error(`Call timed out: ${q.join(".")}`))},this.options.timeout);this.pendingCalls.set($,{resolve:J,reject:U,timer:W}),this.send(K,Z)})}send(q,z=[]){this.debug("sending",V[q.t],q),this.target.postMessage(q,z)}getPendingCallCount(){return this.pendingCalls.size}hasPendingCalls(){return this.pendingCalls.size>0}async shutdown(q={}){let{timeout:z=f}=q;if(this.released)return{success:!0,timeout:!1};if(!this.hasPendingCalls())return this.release(),{success:!0,timeout:!1};return new Promise((G)=>{let Q=!1,Z=(K)=>{if(Q)return;if(Q=!0,clearTimeout($),!this.released)this.release();G(K)},$=setTimeout(()=>{Z({success:!1,timeout:!0})},z);for(let[,K]of this.pendingCalls){let{resolve:J,reject:U}=K;K.resolve=(W)=>{if(J(W),!this.hasPendingCalls())Z({success:!0,timeout:!1})},K.reject=(W)=>{if(U(W),!this.hasPendingCalls())Z({success:!0,timeout:!1})}}})}release(q={}){if(this.released)return;this.released=!0;for(let[,G]of this.pendingCalls){if(G.timer)clearTimeout(G.timer);G.reject(Error("Endpoint released"))}this.pendingCalls.clear();let{silent:z=!1}=q;if(!z){if(this.remoteCallbacks.size>0){let G=[...this.remoteCallbacks.keys()];this.send({t:4,id:this.generateCallId(),c:G})}this.send({t:5,id:this.generateCallId()})}this.remoteCallbacks.clear(),this.callbacks.clear(),this.detachListener()}getTarget(){return this.target}}function X(q){return typeof q==="function"}function j(q){return typeof MessagePort<"u"&&q instanceof MessagePort}function D(q,z){return new S(q,z)}var F=Symbol("rpc:path"),B=Symbol("rpc:proxy-endpoint"),_=Symbol.for("rpc:remote-proxy"),C=Symbol.for("rpc:endpoint"),R=Symbol.for("rpc:release"),w={get(q,z){if(z===_)return!0;if(z===C)return q[B];if(z===R)return()=>{q[B].release()};if(z==="then"){if(q[F].length===0)return;let G=q[B],Q=q[F],Z=G.call(Q,[]);return Z.then.bind(Z)}if(z==="toJSON")return;if(z===Symbol.toStringTag||z===Symbol.iterator||z===Symbol.asyncIterator||z==="constructor"||z==="prototype")return;if(typeof z==="string"){let G=q[B],Z=[...q[F],z];return O(G,Z)}},apply(q,z,G){let Q=q[B],Z=q[F];return Q.call(Z,G)},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,z){return z===_||z===C||z===R}};function O(q,z){let G=function(){};return G[B]=q,G[F]=z,new Proxy(G,w)}function x(q){return O(q,[])}function A(q){if(q===null||q===void 0)return!1;try{return q[_]===!0}catch{return!1}}function T(q){if(A(q))return q[C]}function v(q){if(A(q))q[R]()}class P extends S{pendingBroadcastCalls=new Map;get broadcastTarget(){return this.target}call(q,z){if(this.released)return Promise.reject(Error("Endpoint has been released"));let{rawArgs:G,callbackMap:Q,transferables:Z}=this.processArgs(z),$=this.generateCallId(),K=this.buildCallMessage($,q,G,Q);return this.sendBroadcast($,K,Z,q)}sendBroadcast(q,z,G,Q){return this.debug("sending broadcast",V[z.t],z),new Promise((Z,$)=>{let K={resolve:Z,reject:$,timer:setTimeout(()=>this.handleTimeout(q,Q),this.options.timeout),expectedCount:1/0,results:[],errors:[]};this.pendingBroadcastCalls.set(q,K),Promise.resolve(this.broadcastTarget.postMessage(z,G)).then((J)=>this.handleSubscriberCount(q,J))})}handleTimeout(q,z){let G=this.pendingBroadcastCalls.get(q);if(!G)return;this.pendingBroadcastCalls.delete(q);let Q=G.results.length+G.errors.length,Z=G.expectedCount===1/0?0:G.expectedCount-Q;if(Z>0){let $=Error(`Broadcast timed out: ${z.join(".")}`);$.name="TimeoutError";for(let K=0;K<Z;K++)G.errors.push($)}if(this.debug(`Broadcast timed out with ${G.results.length} results, ${G.errors.length} errors (${Z} timed out)`),G.results.length>0||G.errors.length>0)G.resolve({results:G.results,errors:G.errors});else G.reject(Error(`Broadcast timed out: ${z.join(".")}`))}handleSubscriberCount(q,z){let G=this.pendingBroadcastCalls.get(q);if(!G)return;if(z===0){this.completeBroadcast(q,G);return}G.expectedCount=z,this.checkCompletion(q,G)}checkCompletion(q,z){if(z.results.length+z.errors.length>=z.expectedCount)this.completeBroadcast(q,z)}completeBroadcast(q,z){this.pendingBroadcastCalls.delete(q),clearTimeout(z.timer),z.resolve({results:z.results,errors:z.errors})}handleResult(q){let z=this.pendingBroadcastCalls.get(q.id);if(!z){super.handleResult(q);return}if(q.cm&&Object.keys(q.cm).length>0)z.results.push(this.reconstructResult(q.v,q.cm));else z.results.push(q.v);this.debug(`Broadcast received ${z.results.length}/${z.expectedCount} responses`),this.checkCompletion(q.id,z)}handleError(q){let z=this.pendingBroadcastCalls.get(q.id);if(!z){super.handleError(q);return}let G=Error(q.e);if(q.n)G.name=q.n;if(q.s)G.stack=q.s;z.errors.push(G),this.debug(`Broadcast received error (${z.results.length} results, ${z.errors.length} errors / ${z.expectedCount} expected)`),this.checkCompletion(q.id,z)}getPendingCallCount(){return super.getPendingCallCount()+this.pendingBroadcastCalls.size}hasPendingCalls(){return super.hasPendingCalls()||this.pendingBroadcastCalls.size>0}release(q={}){for(let[,z]of this.pendingBroadcastCalls)clearTimeout(z.timer),z.reject(Error("Endpoint released"));this.pendingBroadcastCalls.clear(),super.release(q)}}function y(q,z){return new P(q,z)}function l(q,z){let Q=D(globalThis,z);return Q.expose(q),Q}function o(q,z){let G=D(q,z);return x(G)}export{x as wrap,o as remote,v as releaseProxy,A as isProxy,j as isMessagePort,X as isFunction,T as getEndpoint,l as expose,D as createEndpoint,y as createBroadcastEndpoint,_ as REMOTE_PROXY,R as RELEASE,B as PROXY_ENDPOINT,F as PATH,V as MessageType,S as Endpoint,C as ENDPOINT,P as BroadcastEndpoint};
package/dist/types.d.ts CHANGED
@@ -38,8 +38,8 @@ export interface ResultMessage extends BaseMessage {
38
38
  t: MessageType.Result;
39
39
  /** Return value (passed raw via structured clone) */
40
40
  v: unknown;
41
- /** Callback ID if return value is a function */
42
- c?: CallbackId;
41
+ /** Callback map: property key -> callbackId (empty string key "" for single function return) */
42
+ cm?: Record<string, CallbackId>;
43
43
  }
44
44
  /** Error response */
45
45
  export interface ErrorMessage extends BaseMessage {
@@ -75,14 +75,22 @@ export interface EndpointReleaseMessage extends BaseMessage {
75
75
  export type RpcMessage = CallMessage | ResultMessage | ErrorMessage | CallbackMessage | CallbackReleaseMessage | EndpointReleaseMessage;
76
76
  /** Unwrap a Promise type */
77
77
  export type Unpromise<T> = T extends Promise<infer U> ? U : T;
78
+ /**
79
+ * Transform first-level functions in a return value to async versions.
80
+ * This matches the runtime behavior where only first-level object properties
81
+ * are scanned for functions (for performance reasons).
82
+ */
83
+ export type RemoteReturnValue<T> = T extends (...args: infer A) => infer R ? (...args: A) => Promise<Unpromise<R>> : T extends object ? {
84
+ [K in keyof T]: T[K] extends (...args: infer A) => infer R ? (...args: A) => Promise<Unpromise<R>> : T[K];
85
+ } : T;
78
86
  /** Helper to convert a single property to its remote version */
79
- export type RemoteProperty<T> = T extends (...args: infer A) => infer R ? (...args: A) => Promise<Unpromise<R>> : T extends object ? RemoteObject<T> : T;
87
+ export type RemoteProperty<T> = T extends (...args: infer A) => infer R ? (...args: A) => Promise<RemoteReturnValue<Unpromise<R>>> : T extends object ? RemoteObject<T> : T;
80
88
  /** Convert an object type to its remote callable version */
81
89
  export type RemoteObject<T> = {
82
90
  [K in keyof T]: RemoteProperty<T[K]>;
83
91
  };
84
92
  /** Helper to convert a single property to its broadcast remote version */
85
- export type RemoteBroadcastProperty<T> = T extends (...args: infer A) => infer R ? (...args: A) => Promise<BroadcastResult<Unpromise<R>>> : T extends object ? RemoteBroadcastObject<T> : T;
93
+ export type RemoteBroadcastProperty<T> = T extends (...args: infer A) => infer R ? (...args: A) => Promise<BroadcastResult<RemoteReturnValue<Unpromise<R>>>> : T extends object ? RemoteBroadcastObject<T> : T;
86
94
  /** Convert an object type to its broadcast remote version (all methods return arrays) */
87
95
  export type RemoteBroadcastObject<T> = {
88
96
  [K in keyof T]: RemoteBroadcastProperty<T[K]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xentobias/worker-rpc",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "High-performance, type-safe RPC for Workers",
5
5
  "module": "src/index.ts",
6
6
  "main": "dist/index.js",
@@ -44,4 +44,4 @@
44
44
  "peerDependencies": {
45
45
  "typescript": "^5"
46
46
  }
47
- }
47
+ }
package/dist/id.d.ts DELETED
@@ -1,23 +0,0 @@
1
- /** Unique identifier for tracking RPC calls */
2
- export type CallId = `c:${string}`;
3
- /** Unique identifier for callback functions */
4
- export type CallbackId = `cb:${string}`;
5
- /**
6
- * Generate a random ID string for use as a default endpoint ID.
7
- * @returns A random 6-character alphanumeric string
8
- */
9
- export declare function generateRandomId(): string;
10
- /**
11
- * Extract the endpoint ID from a call ID.
12
- * Call IDs have the format: "c:<endpointId>:<counter>"
13
- *
14
- * @param callId - The call ID to extract from
15
- * @returns The endpoint ID portion of the call ID
16
- *
17
- * @example
18
- * ```typescript
19
- * extractEndpointId('c:runner3:1') // returns 'runner3'
20
- * extractEndpointId('c:abc123:42') // returns 'abc123'
21
- * ```
22
- */
23
- export declare function extractEndpointId(callId: CallId): string;