@xentobias/worker-rpc 1.0.1
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/channel.d.ts +55 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/endpoint.d.ts +103 -0
- package/dist/endpoint.d.ts.map +1 -0
- package/dist/index.d.ts +132 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +678 -0
- package/dist/proxy.d.ts +45 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/serialize.d.ts +37 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/types.d.ts +154 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker RPC - Channel Module
|
|
3
|
+
* Direct worker-to-worker communication without routing through main thread
|
|
4
|
+
*/
|
|
5
|
+
import { Endpoint } from "./endpoint";
|
|
6
|
+
import { type MessageTarget, type EndpointOptions, type RemoteObject } from "./types";
|
|
7
|
+
/** Unique identifier for workers */
|
|
8
|
+
export type WorkerId = string;
|
|
9
|
+
/** Worker registry for managing direct channels */
|
|
10
|
+
export interface WorkerRegistry {
|
|
11
|
+
/** Register a worker with an ID */
|
|
12
|
+
register(id: WorkerId, worker: MessageTarget): void;
|
|
13
|
+
/** Unregister a worker */
|
|
14
|
+
unregister(id: WorkerId): void;
|
|
15
|
+
/** Get a direct channel to another worker */
|
|
16
|
+
getChannel<T extends object>(targetId: WorkerId): Promise<RemoteObject<T>>;
|
|
17
|
+
/** Release all channels */
|
|
18
|
+
releaseAll(): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a worker registry for the main thread
|
|
22
|
+
* This allows workers to establish direct communication channels
|
|
23
|
+
*/
|
|
24
|
+
export declare function createRegistry(): WorkerRegistry;
|
|
25
|
+
/**
|
|
26
|
+
* Create a direct channel from within a worker to another worker
|
|
27
|
+
* This bypasses the main thread for all subsequent communication
|
|
28
|
+
*/
|
|
29
|
+
export declare function createWorkerChannel<T extends object>(mainThreadEndpoint: Endpoint, targetWorkerId: WorkerId, options?: EndpointOptions): Promise<RemoteObject<T>>;
|
|
30
|
+
/**
|
|
31
|
+
* Accept a channel connection from another worker
|
|
32
|
+
* Call this in the worker that receives the channel request
|
|
33
|
+
*/
|
|
34
|
+
export declare function acceptChannel<T extends object>(port: MessagePort, api: T, options?: EndpointOptions): RemoteObject<T>;
|
|
35
|
+
/**
|
|
36
|
+
* Set up a worker to handle incoming channel requests
|
|
37
|
+
*/
|
|
38
|
+
export declare function setupChannelHandler(mainThread: MessageTarget, getApi: () => object, options?: EndpointOptions): void;
|
|
39
|
+
/**
|
|
40
|
+
* Peer-to-peer channel utilities
|
|
41
|
+
*/
|
|
42
|
+
export declare class PeerChannel<T extends object> {
|
|
43
|
+
private endpoint;
|
|
44
|
+
private remote;
|
|
45
|
+
constructor(port: MessagePort, localApi: object, options?: EndpointOptions);
|
|
46
|
+
/** Get the remote API proxy */
|
|
47
|
+
getRemote(): RemoteObject<T>;
|
|
48
|
+
/** Release this channel */
|
|
49
|
+
release(): void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a pair of connected peer channels for testing or in-process communication
|
|
53
|
+
*/
|
|
54
|
+
export declare function createPeerPair<A extends object, B extends object>(apiA: A, apiB: B, options?: EndpointOptions): [RemoteObject<B>, RemoteObject<A>];
|
|
55
|
+
//# sourceMappingURL=channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAkB,MAAM,YAAY,CAAC;AAEtD,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,SAAS,CAAC;AAEjB,oCAAoC;AACpC,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IACpD,0BAA0B;IAC1B,UAAU,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/B,6CAA6C;IAC7C,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3E,2BAA2B;IAC3B,UAAU,IAAI,IAAI,CAAC;CACpB;AAQD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,cAAc,CAyF/C;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,MAAM,EACxD,kBAAkB,EAAE,QAAQ,EAC5B,cAAc,EAAE,QAAQ,EACxB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAkD1B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC5C,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,CAAC,EACN,OAAO,CAAC,EAAE,eAAe,GACxB,YAAY,CAAC,CAAC,CAAC,CAKjB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,aAAa,EACzB,MAAM,EAAE,MAAM,MAAM,EACpB,OAAO,CAAC,EAAE,eAAe,GACxB,IAAI,CAoCN;AAED;;GAEG;AACH,qBAAa,WAAW,CAAC,CAAC,SAAS,MAAM;IACvC,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,MAAM,CAAkB;gBAEpB,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe;IAO1E,+BAA+B;IAC/B,SAAS,IAAI,YAAY,CAAC,CAAC,CAAC;IAI5B,2BAA2B;IAC3B,OAAO,IAAI,IAAI;CAGhB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EAC/D,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,CAAC,EACP,OAAO,CAAC,EAAE,eAAe,GACxB,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAapC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker RPC - Endpoint Module
|
|
3
|
+
* Core RPC endpoint handling message passing and method invocation
|
|
4
|
+
* Uses structured clone for data, custom handling only for callbacks
|
|
5
|
+
*/
|
|
6
|
+
import { type MethodPath, type MessageTarget, type EndpointOptions, type ExposeOptions } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* RPC Endpoint - handles bidirectional communication with a worker
|
|
9
|
+
*/
|
|
10
|
+
export declare class Endpoint {
|
|
11
|
+
private target;
|
|
12
|
+
private options;
|
|
13
|
+
private exposedApi;
|
|
14
|
+
private exposeOptions;
|
|
15
|
+
/** Pending calls awaiting response */
|
|
16
|
+
private pendingCalls;
|
|
17
|
+
/** Registered callbacks (functions passed as arguments) */
|
|
18
|
+
private callbacks;
|
|
19
|
+
/** Remote callbacks (proxies for functions on the other side) */
|
|
20
|
+
private remoteCallbacks;
|
|
21
|
+
/** Message handler bound to this instance */
|
|
22
|
+
private boundMessageHandler;
|
|
23
|
+
/** Whether this endpoint has been released */
|
|
24
|
+
private released;
|
|
25
|
+
constructor(target: MessageTarget, options?: EndpointOptions);
|
|
26
|
+
/**
|
|
27
|
+
* Attach the message listener to the target
|
|
28
|
+
*/
|
|
29
|
+
private attachListener;
|
|
30
|
+
/**
|
|
31
|
+
* Detach the message listener from the target
|
|
32
|
+
*/
|
|
33
|
+
private detachListener;
|
|
34
|
+
/**
|
|
35
|
+
* Log a debug message
|
|
36
|
+
*/
|
|
37
|
+
private debug;
|
|
38
|
+
/**
|
|
39
|
+
* Expose an API object for remote invocation
|
|
40
|
+
*/
|
|
41
|
+
expose(api: object, options?: ExposeOptions): void;
|
|
42
|
+
/**
|
|
43
|
+
* Handle incoming messages
|
|
44
|
+
*/
|
|
45
|
+
private handleMessage;
|
|
46
|
+
/**
|
|
47
|
+
* Handle a method call request
|
|
48
|
+
*/
|
|
49
|
+
private handleCall;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve a method from the exposed API by path
|
|
52
|
+
*/
|
|
53
|
+
private resolveMethod;
|
|
54
|
+
/**
|
|
55
|
+
* Handle a successful result
|
|
56
|
+
*/
|
|
57
|
+
private handleResult;
|
|
58
|
+
/**
|
|
59
|
+
* Handle an error result
|
|
60
|
+
*/
|
|
61
|
+
private handleError;
|
|
62
|
+
/**
|
|
63
|
+
* Handle a callback invocation
|
|
64
|
+
*/
|
|
65
|
+
private handleCallback;
|
|
66
|
+
/**
|
|
67
|
+
* Handle release of callbacks
|
|
68
|
+
*/
|
|
69
|
+
private handleRelease;
|
|
70
|
+
/**
|
|
71
|
+
* Register a local callback function
|
|
72
|
+
*/
|
|
73
|
+
private registerCallback;
|
|
74
|
+
/**
|
|
75
|
+
* Create a proxy function for a remote callback
|
|
76
|
+
*/
|
|
77
|
+
private createRemoteCallback;
|
|
78
|
+
/**
|
|
79
|
+
* Invoke a remote callback
|
|
80
|
+
*/
|
|
81
|
+
private invokeCallback;
|
|
82
|
+
/**
|
|
83
|
+
* Call a remote method
|
|
84
|
+
*/
|
|
85
|
+
call(path: MethodPath, args: unknown[]): Promise<unknown>;
|
|
86
|
+
/**
|
|
87
|
+
* Send a message to the target
|
|
88
|
+
*/
|
|
89
|
+
private send;
|
|
90
|
+
/**
|
|
91
|
+
* Release this endpoint and clean up resources
|
|
92
|
+
*/
|
|
93
|
+
release(): void;
|
|
94
|
+
/**
|
|
95
|
+
* Get the underlying message target
|
|
96
|
+
*/
|
|
97
|
+
getTarget(): MessageTarget;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Create an endpoint for a worker or message port
|
|
101
|
+
*/
|
|
102
|
+
export declare function createEndpoint(target: MessageTarget, options?: EndpointOptions): Endpoint;
|
|
103
|
+
//# sourceMappingURL=endpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint.d.ts","sourceRoot":"","sources":["../src/endpoint.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAUL,KAAK,UAAU,EAGf,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EAEnB,MAAM,SAAS,CAAC;AAejB;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,aAAa,CAAqB;IAE1C,sCAAsC;IACtC,OAAO,CAAC,YAAY,CAAkC;IAEtD,2DAA2D;IAC3D,OAAO,CAAC,SAAS,CAA+C;IAEhE,iEAAiE;IACjE,OAAO,CAAC,eAAe,CAAmC;IAE1D,6CAA6C;IAC7C,OAAO,CAAC,mBAAmB,CAAgC;IAE3D,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAS;gBAEb,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,eAAoB;IAYhE;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,KAAK;IAMb;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,IAAI;IAOtD;;OAEG;IACH,OAAO,CAAC,aAAa;IA6BrB;;OAEG;YACW,UAAU;IAqDxB;;OAEG;IACH,OAAO,CAAC,aAAa;IAgCrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAoBpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAuBnB;;OAEG;YACW,cAAc;IA8D5B;;OAEG;IACH,OAAO,CAAC,aAAa;IAMrB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;OAEG;YACW,cAAc;IA6C5B;;OAEG;IACG,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAkD/D;;OAEG;IACH,OAAO,CAAC,IAAI;IAKZ;;OAEG;IACH,OAAO,IAAI,IAAI;IAkCf;;OAEG;IACH,SAAS,IAAI,aAAa;CAG3B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAEzF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker RPC - High-performance worker communication for Bun.js
|
|
3
|
+
*
|
|
4
|
+
* A type-safe, ergonomic library for seamless RPC between Bun workers.
|
|
5
|
+
* Supports nested methods, callback functions, and direct worker-to-worker channels.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // worker.ts
|
|
10
|
+
* import { expose } from "worker-rpc";
|
|
11
|
+
*
|
|
12
|
+
* const api = {
|
|
13
|
+
* add: (a: number, b: number) => a + b,
|
|
14
|
+
* db: {
|
|
15
|
+
* users: {
|
|
16
|
+
* find: async (id: string) => ({ id, name: "John" }),
|
|
17
|
+
* },
|
|
18
|
+
* },
|
|
19
|
+
* processWithCallback: async (data: number[], cb: (progress: number) => void) => {
|
|
20
|
+
* for (let i = 0; i < data.length; i++) {
|
|
21
|
+
* await someWork(data[i]);
|
|
22
|
+
* cb((i + 1) / data.length);
|
|
23
|
+
* }
|
|
24
|
+
* return "done";
|
|
25
|
+
* },
|
|
26
|
+
* };
|
|
27
|
+
*
|
|
28
|
+
* expose(api);
|
|
29
|
+
*
|
|
30
|
+
* export type WorkerApi = typeof api;
|
|
31
|
+
*
|
|
32
|
+
* // main.ts
|
|
33
|
+
* import { wrap, createEndpoint } from "worker-rpc";
|
|
34
|
+
* import type { WorkerApi } from "./worker";
|
|
35
|
+
*
|
|
36
|
+
* const worker = new Worker("./worker.ts");
|
|
37
|
+
* const api = wrap<WorkerApi>(createEndpoint(worker));
|
|
38
|
+
*
|
|
39
|
+
* // Type-safe, async calls
|
|
40
|
+
* const sum = await api.add(1, 2);
|
|
41
|
+
* const user = await api.db.users.find("123");
|
|
42
|
+
*
|
|
43
|
+
* // Callbacks work seamlessly
|
|
44
|
+
* await api.processWithCallback([1, 2, 3], (progress) => {
|
|
45
|
+
* console.log(`Progress: ${progress * 100}%`);
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export { type CallId, type CallbackId, type MethodPath, MessageType, type RpcMessage, type EndpointOptions, type ExposeOptions, type RemoteObject, type RemoteFunction, type MessageTarget, type BunWorker, REMOTE_PROXY, ENDPOINT, RELEASE, CREATE_CHANNEL, } from "./types";
|
|
50
|
+
export { Endpoint, createEndpoint } from "./endpoint";
|
|
51
|
+
export { wrap, isProxy, getEndpoint, releaseProxy } from "./proxy";
|
|
52
|
+
export { type WorkerId, type WorkerRegistry, createRegistry, createWorkerChannel, acceptChannel, setupChannelHandler, PeerChannel, createPeerPair, } from "./channel";
|
|
53
|
+
export { serializeError, deserializeError, isFunction, isMessagePort, isTransferable, type SerializedError, } from "./serialize";
|
|
54
|
+
import { Endpoint } from "./endpoint";
|
|
55
|
+
import type { MessageTarget, EndpointOptions, RemoteObject } from "./types";
|
|
56
|
+
/**
|
|
57
|
+
* Expose an API object for remote invocation
|
|
58
|
+
* Call this in the worker to expose methods to the parent
|
|
59
|
+
*
|
|
60
|
+
* @param api - The API object to expose
|
|
61
|
+
* @param options - Optional configuration
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const api = {
|
|
66
|
+
* greet: (name: string) => `Hello, ${name}!`,
|
|
67
|
+
* math: {
|
|
68
|
+
* add: (a: number, b: number) => a + b,
|
|
69
|
+
* },
|
|
70
|
+
* };
|
|
71
|
+
*
|
|
72
|
+
* expose(api);
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function expose(api: object, options?: EndpointOptions): Endpoint;
|
|
76
|
+
/**
|
|
77
|
+
* Create a wrapped remote API from a worker
|
|
78
|
+
* Convenience function that combines createEndpoint and wrap
|
|
79
|
+
*
|
|
80
|
+
* @param worker - The worker or message target
|
|
81
|
+
* @param options - Optional configuration
|
|
82
|
+
* @returns A type-safe proxy for the remote API
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const worker = new Worker("./worker.ts");
|
|
87
|
+
* const api = remote<WorkerApi>(worker);
|
|
88
|
+
*
|
|
89
|
+
* const result = await api.someMethod();
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function remote<T extends object>(worker: MessageTarget, options?: EndpointOptions): RemoteObject<T>;
|
|
93
|
+
/**
|
|
94
|
+
* Transfer a value by reference (marks it for zero-copy transfer)
|
|
95
|
+
* Currently supports ArrayBuffer and MessagePort
|
|
96
|
+
*
|
|
97
|
+
* Note: With the optimized serialization, ArrayBuffers are passed through
|
|
98
|
+
* structured clone which handles transfer automatically.
|
|
99
|
+
*
|
|
100
|
+
* @param value - The value to transfer
|
|
101
|
+
* @returns The same value
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const buffer = new ArrayBuffer(1024);
|
|
106
|
+
* await api.processBuffer(transfer(buffer));
|
|
107
|
+
* // buffer is now unusable in this context (transferred)
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export declare function transfer<T extends ArrayBuffer | MessagePort>(value: T): T;
|
|
111
|
+
/**
|
|
112
|
+
* Create a proxy function that can be passed to workers
|
|
113
|
+
* Useful for creating callbacks with specific invocation limits
|
|
114
|
+
*
|
|
115
|
+
* @param fn - The function to wrap
|
|
116
|
+
* @param options - Options for the callback
|
|
117
|
+
* @returns The wrapped function
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* // Single-use callback
|
|
122
|
+
* await api.onComplete(callback(() => console.log("done"), { once: true }));
|
|
123
|
+
*
|
|
124
|
+
* // Limited invocations
|
|
125
|
+
* await api.onProgress(callback((p) => console.log(p), { times: 10 }));
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare function callback<T extends (...args: any[]) => any>(fn: T, _options?: {
|
|
129
|
+
once?: boolean;
|
|
130
|
+
times?: number;
|
|
131
|
+
}): T;
|
|
132
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAGH,OAAO,EACL,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,UAAU,EACf,WAAW,EACX,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGtD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGnE,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,cAAc,GACf,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,cAAc,EACd,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,QAAQ,EAAkB,MAAM,YAAY,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAMvE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EACrC,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,eAAe,GACxB,YAAY,CAAC,CAAC,CAAC,CAGjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAIzE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxD,EAAE,EAAE,CAAC,EACL,QAAQ,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,CAAC,CAIH"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/types.ts
|
|
3
|
+
var MessageType;
|
|
4
|
+
((MessageType2) => {
|
|
5
|
+
MessageType2[MessageType2["Call"] = 0] = "Call";
|
|
6
|
+
MessageType2[MessageType2["Result"] = 1] = "Result";
|
|
7
|
+
MessageType2[MessageType2["Error"] = 2] = "Error";
|
|
8
|
+
MessageType2[MessageType2["Callback"] = 3] = "Callback";
|
|
9
|
+
MessageType2[MessageType2["Release"] = 4] = "Release";
|
|
10
|
+
MessageType2[MessageType2["Channel"] = 5] = "Channel";
|
|
11
|
+
MessageType2[MessageType2["ChannelAck"] = 6] = "ChannelAck";
|
|
12
|
+
})(MessageType ||= {});
|
|
13
|
+
var REMOTE_PROXY = Symbol.for("worker-rpc:remote-proxy");
|
|
14
|
+
var ENDPOINT = Symbol.for("worker-rpc:endpoint");
|
|
15
|
+
var RELEASE = Symbol.for("worker-rpc:release");
|
|
16
|
+
var CREATE_CHANNEL = Symbol.for("worker-rpc:create-channel");
|
|
17
|
+
// src/serialize.ts
|
|
18
|
+
function serializeError(error) {
|
|
19
|
+
return {
|
|
20
|
+
e: error.message,
|
|
21
|
+
n: error.name,
|
|
22
|
+
s: error.stack
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function deserializeError(data) {
|
|
26
|
+
const error = new Error(data.e);
|
|
27
|
+
error.name = data.n;
|
|
28
|
+
if (data.s) {
|
|
29
|
+
error.stack = data.s;
|
|
30
|
+
}
|
|
31
|
+
return error;
|
|
32
|
+
}
|
|
33
|
+
function isFunction(value) {
|
|
34
|
+
return typeof value === "function";
|
|
35
|
+
}
|
|
36
|
+
function isMessagePort(value) {
|
|
37
|
+
return typeof MessagePort !== "undefined" && value instanceof MessagePort;
|
|
38
|
+
}
|
|
39
|
+
function isTransferable(value) {
|
|
40
|
+
return value instanceof ArrayBuffer || typeof MessagePort !== "undefined" && value instanceof MessagePort;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/endpoint.ts
|
|
44
|
+
var DEFAULT_TIMEOUT = 30000;
|
|
45
|
+
var idCounter = 0;
|
|
46
|
+
function generateId() {
|
|
47
|
+
return `${++idCounter}:${Date.now().toString(36)}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
class Endpoint {
|
|
51
|
+
target;
|
|
52
|
+
options;
|
|
53
|
+
exposedApi = null;
|
|
54
|
+
exposeOptions = {};
|
|
55
|
+
pendingCalls = new Map;
|
|
56
|
+
callbacks = new Map;
|
|
57
|
+
remoteCallbacks = new Map;
|
|
58
|
+
boundMessageHandler;
|
|
59
|
+
released = false;
|
|
60
|
+
constructor(target, options = {}) {
|
|
61
|
+
this.target = target;
|
|
62
|
+
this.options = {
|
|
63
|
+
timeout: options.timeout ?? DEFAULT_TIMEOUT,
|
|
64
|
+
onError: options.onError ?? console.error,
|
|
65
|
+
debug: options.debug ?? false
|
|
66
|
+
};
|
|
67
|
+
this.boundMessageHandler = this.handleMessage.bind(this);
|
|
68
|
+
this.attachListener();
|
|
69
|
+
}
|
|
70
|
+
attachListener() {
|
|
71
|
+
if (this.target.addEventListener) {
|
|
72
|
+
this.target.addEventListener("message", this.boundMessageHandler);
|
|
73
|
+
} else if (this.target.onmessage !== undefined) {
|
|
74
|
+
this.target.onmessage = this.boundMessageHandler;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
detachListener() {
|
|
78
|
+
if (this.target.removeEventListener) {
|
|
79
|
+
this.target.removeEventListener("message", this.boundMessageHandler);
|
|
80
|
+
} else if (this.target.onmessage !== undefined) {
|
|
81
|
+
this.target.onmessage = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
debug(...args) {
|
|
85
|
+
if (this.options.debug) {
|
|
86
|
+
console.log("[worker-rpc]", ...args);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
expose(api, options = {}) {
|
|
90
|
+
this.exposedApi = api;
|
|
91
|
+
this.exposeOptions = {
|
|
92
|
+
maxDepth: options.maxDepth ?? 10
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
handleMessage(event) {
|
|
96
|
+
const message = event.data;
|
|
97
|
+
if (typeof message !== "object" || message === null || !("t" in message)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.debug("received", MessageType[message.t], message);
|
|
101
|
+
switch (message.t) {
|
|
102
|
+
case 0 /* Call */:
|
|
103
|
+
this.handleCall(message);
|
|
104
|
+
break;
|
|
105
|
+
case 1 /* Result */:
|
|
106
|
+
this.handleResult(message);
|
|
107
|
+
break;
|
|
108
|
+
case 2 /* Error */:
|
|
109
|
+
this.handleError(message);
|
|
110
|
+
break;
|
|
111
|
+
case 3 /* Callback */:
|
|
112
|
+
this.handleCallback(message);
|
|
113
|
+
break;
|
|
114
|
+
case 4 /* Release */:
|
|
115
|
+
this.handleRelease(message);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async handleCall(message) {
|
|
120
|
+
const { id, p: path, a: args, c: callbackMap } = message;
|
|
121
|
+
try {
|
|
122
|
+
const { method, thisArg } = this.resolveMethod(path);
|
|
123
|
+
if (typeof method !== "function") {
|
|
124
|
+
throw new Error(`Method not found: ${path.join(".")}`);
|
|
125
|
+
}
|
|
126
|
+
const resolvedArgs = args.map((arg, index) => {
|
|
127
|
+
const callbackId = callbackMap?.[index];
|
|
128
|
+
if (callbackId) {
|
|
129
|
+
return this.createRemoteCallback(callbackId);
|
|
130
|
+
}
|
|
131
|
+
return arg;
|
|
132
|
+
});
|
|
133
|
+
const result = await method.apply(thisArg, resolvedArgs);
|
|
134
|
+
if (isFunction(result)) {
|
|
135
|
+
const callbackId = this.registerCallback(result);
|
|
136
|
+
this.send({
|
|
137
|
+
t: 1 /* Result */,
|
|
138
|
+
id,
|
|
139
|
+
v: null,
|
|
140
|
+
c: callbackId
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
this.send({
|
|
144
|
+
t: 1 /* Result */,
|
|
145
|
+
id,
|
|
146
|
+
v: result
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
151
|
+
this.send({
|
|
152
|
+
t: 2 /* Error */,
|
|
153
|
+
id,
|
|
154
|
+
e: err.message,
|
|
155
|
+
n: err.name,
|
|
156
|
+
s: err.stack
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
resolveMethod(path) {
|
|
161
|
+
if (!this.exposedApi) {
|
|
162
|
+
throw new Error("No API exposed");
|
|
163
|
+
}
|
|
164
|
+
let current = this.exposedApi;
|
|
165
|
+
let parent = null;
|
|
166
|
+
for (let i = 0;i < path.length; i++) {
|
|
167
|
+
const key = path[i];
|
|
168
|
+
if (current === null || current === undefined) {
|
|
169
|
+
throw new Error(`Cannot access property '${key}' of ${current}`);
|
|
170
|
+
}
|
|
171
|
+
if (key === undefined) {
|
|
172
|
+
throw new Error(`Invalid path at index ${i}`);
|
|
173
|
+
}
|
|
174
|
+
parent = current;
|
|
175
|
+
current = current[key];
|
|
176
|
+
if (i >= (this.exposeOptions.maxDepth ?? 10)) {
|
|
177
|
+
throw new Error(`Maximum nesting depth exceeded`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { target: this.exposedApi, method: current, thisArg: parent };
|
|
181
|
+
}
|
|
182
|
+
handleResult(message) {
|
|
183
|
+
const pending = this.pendingCalls.get(message.id);
|
|
184
|
+
if (!pending) {
|
|
185
|
+
this.debug("Received result for unknown call:", message.id);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this.pendingCalls.delete(message.id);
|
|
189
|
+
if (pending.timer) {
|
|
190
|
+
clearTimeout(pending.timer);
|
|
191
|
+
}
|
|
192
|
+
if (message.c) {
|
|
193
|
+
pending.resolve(this.createRemoteCallback(message.c));
|
|
194
|
+
} else {
|
|
195
|
+
pending.resolve(message.v);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
handleError(message) {
|
|
199
|
+
const pending = this.pendingCalls.get(message.id);
|
|
200
|
+
if (!pending) {
|
|
201
|
+
this.debug("Received error for unknown call:", message.id);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this.pendingCalls.delete(message.id);
|
|
205
|
+
if (pending.timer) {
|
|
206
|
+
clearTimeout(pending.timer);
|
|
207
|
+
}
|
|
208
|
+
const error = new Error(message.e);
|
|
209
|
+
if (message.n) {
|
|
210
|
+
error.name = message.n;
|
|
211
|
+
}
|
|
212
|
+
if (message.s) {
|
|
213
|
+
error.stack = message.s;
|
|
214
|
+
}
|
|
215
|
+
pending.reject(error);
|
|
216
|
+
}
|
|
217
|
+
async handleCallback(message) {
|
|
218
|
+
const { id, c: callbackId, a: args, cb: callbackMap } = message;
|
|
219
|
+
const registration = this.callbacks.get(callbackId);
|
|
220
|
+
if (!registration) {
|
|
221
|
+
this.send({
|
|
222
|
+
t: 2 /* Error */,
|
|
223
|
+
id,
|
|
224
|
+
e: `Callback not found: ${callbackId}`
|
|
225
|
+
});
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
const resolvedArgs = args.map((arg, index) => {
|
|
230
|
+
const cbId = callbackMap?.[index];
|
|
231
|
+
if (cbId) {
|
|
232
|
+
return this.createRemoteCallback(cbId);
|
|
233
|
+
}
|
|
234
|
+
return arg;
|
|
235
|
+
});
|
|
236
|
+
const result = await registration.fn(...resolvedArgs);
|
|
237
|
+
if (registration.remaining > 0) {
|
|
238
|
+
registration.remaining--;
|
|
239
|
+
if (registration.remaining === 0) {
|
|
240
|
+
this.callbacks.delete(callbackId);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (isFunction(result)) {
|
|
244
|
+
const cbId = this.registerCallback(result);
|
|
245
|
+
this.send({
|
|
246
|
+
t: 1 /* Result */,
|
|
247
|
+
id,
|
|
248
|
+
v: null,
|
|
249
|
+
c: cbId
|
|
250
|
+
});
|
|
251
|
+
} else {
|
|
252
|
+
this.send({
|
|
253
|
+
t: 1 /* Result */,
|
|
254
|
+
id,
|
|
255
|
+
v: result
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
260
|
+
this.send({
|
|
261
|
+
t: 2 /* Error */,
|
|
262
|
+
id,
|
|
263
|
+
e: err.message,
|
|
264
|
+
n: err.name,
|
|
265
|
+
s: err.stack
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
handleRelease(message) {
|
|
270
|
+
for (const callbackId of message.c) {
|
|
271
|
+
this.callbacks.delete(callbackId);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
registerCallback(fn, remaining = -1) {
|
|
275
|
+
const id = `cb:${generateId()}`;
|
|
276
|
+
this.callbacks.set(id, { fn, remaining });
|
|
277
|
+
return id;
|
|
278
|
+
}
|
|
279
|
+
createRemoteCallback(callbackId) {
|
|
280
|
+
let proxy = this.remoteCallbacks.get(callbackId);
|
|
281
|
+
if (proxy) {
|
|
282
|
+
return proxy;
|
|
283
|
+
}
|
|
284
|
+
proxy = async (...args) => {
|
|
285
|
+
return this.invokeCallback(callbackId, args);
|
|
286
|
+
};
|
|
287
|
+
this.remoteCallbacks.set(callbackId, proxy);
|
|
288
|
+
return proxy;
|
|
289
|
+
}
|
|
290
|
+
async invokeCallback(callbackId, args) {
|
|
291
|
+
const id = generateId();
|
|
292
|
+
const rawArgs = [];
|
|
293
|
+
const callbackMap = {};
|
|
294
|
+
const transferables = [];
|
|
295
|
+
for (let i = 0;i < args.length; i++) {
|
|
296
|
+
const arg = args[i];
|
|
297
|
+
if (isFunction(arg)) {
|
|
298
|
+
callbackMap[i] = this.registerCallback(arg);
|
|
299
|
+
rawArgs.push(null);
|
|
300
|
+
} else if (isMessagePort(arg)) {
|
|
301
|
+
transferables.push(arg);
|
|
302
|
+
rawArgs.push(arg);
|
|
303
|
+
} else {
|
|
304
|
+
rawArgs.push(arg);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return new Promise((resolve, reject) => {
|
|
308
|
+
const timer = setTimeout(() => {
|
|
309
|
+
this.pendingCalls.delete(id);
|
|
310
|
+
reject(new Error(`Callback invocation timed out: ${callbackId}`));
|
|
311
|
+
}, this.options.timeout);
|
|
312
|
+
this.pendingCalls.set(id, { resolve, reject, timer });
|
|
313
|
+
const message = {
|
|
314
|
+
t: 3 /* Callback */,
|
|
315
|
+
id,
|
|
316
|
+
c: callbackId,
|
|
317
|
+
a: rawArgs,
|
|
318
|
+
...Object.keys(callbackMap).length > 0 && { cb: callbackMap }
|
|
319
|
+
};
|
|
320
|
+
this.send(message, transferables);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
async call(path, args) {
|
|
324
|
+
if (this.released) {
|
|
325
|
+
throw new Error("Endpoint has been released");
|
|
326
|
+
}
|
|
327
|
+
const id = generateId();
|
|
328
|
+
const rawArgs = [];
|
|
329
|
+
const callbackMap = {};
|
|
330
|
+
const transferables = [];
|
|
331
|
+
for (let i = 0;i < args.length; i++) {
|
|
332
|
+
const arg = args[i];
|
|
333
|
+
if (isFunction(arg)) {
|
|
334
|
+
callbackMap[i] = this.registerCallback(arg);
|
|
335
|
+
rawArgs.push(null);
|
|
336
|
+
} else if (isMessagePort(arg)) {
|
|
337
|
+
transferables.push(arg);
|
|
338
|
+
rawArgs.push(arg);
|
|
339
|
+
} else {
|
|
340
|
+
rawArgs.push(arg);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return new Promise((resolve, reject) => {
|
|
344
|
+
const timer = setTimeout(() => {
|
|
345
|
+
this.pendingCalls.delete(id);
|
|
346
|
+
reject(new Error(`Call timed out: ${path.join(".")}`));
|
|
347
|
+
}, this.options.timeout);
|
|
348
|
+
this.pendingCalls.set(id, { resolve, reject, timer });
|
|
349
|
+
const message = {
|
|
350
|
+
t: 0 /* Call */,
|
|
351
|
+
id,
|
|
352
|
+
p: path,
|
|
353
|
+
a: rawArgs,
|
|
354
|
+
...Object.keys(callbackMap).length > 0 && { c: callbackMap }
|
|
355
|
+
};
|
|
356
|
+
this.send(message, transferables);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
send(message, transferables = []) {
|
|
360
|
+
this.debug("sending", MessageType[message.t], message);
|
|
361
|
+
this.target.postMessage(message, transferables);
|
|
362
|
+
}
|
|
363
|
+
release() {
|
|
364
|
+
if (this.released) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
this.released = true;
|
|
368
|
+
for (const [, pending] of this.pendingCalls) {
|
|
369
|
+
if (pending.timer) {
|
|
370
|
+
clearTimeout(pending.timer);
|
|
371
|
+
}
|
|
372
|
+
pending.reject(new Error("Endpoint released"));
|
|
373
|
+
}
|
|
374
|
+
this.pendingCalls.clear();
|
|
375
|
+
if (this.remoteCallbacks.size > 0) {
|
|
376
|
+
const callbackIds = Array.from(this.remoteCallbacks.keys());
|
|
377
|
+
this.send({
|
|
378
|
+
t: 4 /* Release */,
|
|
379
|
+
id: generateId(),
|
|
380
|
+
c: callbackIds
|
|
381
|
+
});
|
|
382
|
+
this.remoteCallbacks.clear();
|
|
383
|
+
}
|
|
384
|
+
this.callbacks.clear();
|
|
385
|
+
this.detachListener();
|
|
386
|
+
}
|
|
387
|
+
getTarget() {
|
|
388
|
+
return this.target;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function createEndpoint(target, options) {
|
|
392
|
+
return new Endpoint(target, options);
|
|
393
|
+
}
|
|
394
|
+
// src/proxy.ts
|
|
395
|
+
var PATH = Symbol("worker-rpc:path");
|
|
396
|
+
var PROXY_ENDPOINT = Symbol("worker-rpc:proxy-endpoint");
|
|
397
|
+
var proxyHandler = {
|
|
398
|
+
get(target, prop, receiver) {
|
|
399
|
+
if (prop === REMOTE_PROXY) {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
if (prop === ENDPOINT) {
|
|
403
|
+
return target[PROXY_ENDPOINT];
|
|
404
|
+
}
|
|
405
|
+
if (prop === RELEASE) {
|
|
406
|
+
return () => {
|
|
407
|
+
const endpoint = target[PROXY_ENDPOINT];
|
|
408
|
+
endpoint.release();
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
if (prop === "then") {
|
|
412
|
+
if (target[PATH].length === 0) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const endpoint = target[PROXY_ENDPOINT];
|
|
416
|
+
const path = target[PATH];
|
|
417
|
+
const promise = endpoint.call(path, []);
|
|
418
|
+
return promise.then.bind(promise);
|
|
419
|
+
}
|
|
420
|
+
if (prop === "toJSON") {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (prop === Symbol.toStringTag || prop === Symbol.iterator || prop === Symbol.asyncIterator || prop === "constructor" || prop === "prototype") {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (typeof prop === "string") {
|
|
427
|
+
const endpoint = target[PROXY_ENDPOINT];
|
|
428
|
+
const currentPath = target[PATH];
|
|
429
|
+
const newPath = [...currentPath, prop];
|
|
430
|
+
return createProxyInternal(endpoint, newPath);
|
|
431
|
+
}
|
|
432
|
+
return;
|
|
433
|
+
},
|
|
434
|
+
apply(target, _thisArg, args) {
|
|
435
|
+
const endpoint = target[PROXY_ENDPOINT];
|
|
436
|
+
const path = target[PATH];
|
|
437
|
+
return endpoint.call(path, args);
|
|
438
|
+
},
|
|
439
|
+
set() {
|
|
440
|
+
throw new Error("Cannot set properties on a remote proxy");
|
|
441
|
+
},
|
|
442
|
+
deleteProperty() {
|
|
443
|
+
throw new Error("Cannot delete properties on a remote proxy");
|
|
444
|
+
},
|
|
445
|
+
getPrototypeOf() {
|
|
446
|
+
return Function.prototype;
|
|
447
|
+
},
|
|
448
|
+
has(_target, prop) {
|
|
449
|
+
return prop === REMOTE_PROXY || prop === ENDPOINT || prop === RELEASE;
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
function createProxyInternal(endpoint, path) {
|
|
453
|
+
const target = function() {};
|
|
454
|
+
target[PROXY_ENDPOINT] = endpoint;
|
|
455
|
+
target[PATH] = path;
|
|
456
|
+
return new Proxy(target, proxyHandler);
|
|
457
|
+
}
|
|
458
|
+
function wrap(endpoint) {
|
|
459
|
+
return createProxyInternal(endpoint, []);
|
|
460
|
+
}
|
|
461
|
+
function isProxy(value) {
|
|
462
|
+
if (value === null || value === undefined) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
return value[REMOTE_PROXY] === true;
|
|
467
|
+
} catch {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function getEndpoint(proxy) {
|
|
472
|
+
if (isProxy(proxy)) {
|
|
473
|
+
return proxy[ENDPOINT];
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
function releaseProxy(proxy) {
|
|
478
|
+
if (isProxy(proxy)) {
|
|
479
|
+
proxy[RELEASE]();
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// src/channel.ts
|
|
483
|
+
function createRegistry() {
|
|
484
|
+
const workers = new Map;
|
|
485
|
+
const channels = new Map;
|
|
486
|
+
const pendingChannels = new Map;
|
|
487
|
+
function handleWorkerMessage(workerId, event) {
|
|
488
|
+
const message = event.data;
|
|
489
|
+
if (typeof message !== "object" || message === null || message.t !== 5 /* Channel */) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const { target: targetId, port, id } = message;
|
|
493
|
+
const targetWorker = workers.get(targetId);
|
|
494
|
+
if (!targetWorker) {
|
|
495
|
+
const worker = workers.get(workerId);
|
|
496
|
+
if (worker) {
|
|
497
|
+
worker.postMessage({
|
|
498
|
+
t: 2 /* Error */,
|
|
499
|
+
id,
|
|
500
|
+
e: `Worker not found: ${targetId}`
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
targetWorker.postMessage({
|
|
506
|
+
t: 5 /* Channel */,
|
|
507
|
+
id,
|
|
508
|
+
source: workerId,
|
|
509
|
+
port
|
|
510
|
+
}, [port]);
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
register(id, worker) {
|
|
514
|
+
workers.set(id, worker);
|
|
515
|
+
const handler = (event) => handleWorkerMessage(id, event);
|
|
516
|
+
if (worker.addEventListener) {
|
|
517
|
+
worker.addEventListener("message", handler);
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
unregister(id) {
|
|
521
|
+
workers.delete(id);
|
|
522
|
+
for (const [key, state] of channels) {
|
|
523
|
+
if (key.includes(id)) {
|
|
524
|
+
state.endpoint.release();
|
|
525
|
+
state.port.close();
|
|
526
|
+
channels.delete(key);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
async getChannel(targetId) {
|
|
531
|
+
throw new Error("getChannel should be called from within a worker using createWorkerChannel");
|
|
532
|
+
},
|
|
533
|
+
releaseAll() {
|
|
534
|
+
for (const [, state] of channels) {
|
|
535
|
+
state.endpoint.release();
|
|
536
|
+
state.port.close();
|
|
537
|
+
}
|
|
538
|
+
channels.clear();
|
|
539
|
+
workers.clear();
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
async function createWorkerChannel(mainThreadEndpoint, targetWorkerId, options) {
|
|
544
|
+
const channel = new MessageChannel;
|
|
545
|
+
return new Promise((resolve, reject) => {
|
|
546
|
+
const id = `channel:${Date.now()}:${Math.random().toString(36).slice(2)}`;
|
|
547
|
+
const originalHandler = mainThreadEndpoint.getTarget().onmessage;
|
|
548
|
+
const responseHandler = (event) => {
|
|
549
|
+
const message = event.data;
|
|
550
|
+
if (message?.id === id) {
|
|
551
|
+
if (mainThreadEndpoint.getTarget().onmessage === responseHandler) {
|
|
552
|
+
mainThreadEndpoint.getTarget().onmessage = originalHandler;
|
|
553
|
+
}
|
|
554
|
+
if (message.t === 2 /* Error */) {
|
|
555
|
+
reject(new Error(message.e));
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
const endpoint = createEndpoint(channel.port1, options);
|
|
559
|
+
resolve(wrap(endpoint));
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
const target = mainThreadEndpoint.getTarget();
|
|
563
|
+
if (target.addEventListener) {
|
|
564
|
+
target.addEventListener("message", responseHandler);
|
|
565
|
+
} else {
|
|
566
|
+
target.onmessage = responseHandler;
|
|
567
|
+
}
|
|
568
|
+
target.postMessage({
|
|
569
|
+
t: 5 /* Channel */,
|
|
570
|
+
id,
|
|
571
|
+
target: targetWorkerId,
|
|
572
|
+
port: channel.port2
|
|
573
|
+
}, [channel.port2]);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
function acceptChannel(port, api, options) {
|
|
577
|
+
const endpoint = createEndpoint(port, options);
|
|
578
|
+
endpoint.expose(api);
|
|
579
|
+
port.start();
|
|
580
|
+
return wrap(endpoint);
|
|
581
|
+
}
|
|
582
|
+
function setupChannelHandler(mainThread, getApi, options) {
|
|
583
|
+
const handler = (event) => {
|
|
584
|
+
const message = event.data;
|
|
585
|
+
if (typeof message !== "object" || message === null || message.t !== 5 /* Channel */) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
const { port, source, id } = message;
|
|
589
|
+
if (!port) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const endpoint = createEndpoint(port, options);
|
|
593
|
+
endpoint.expose(getApi());
|
|
594
|
+
port.start();
|
|
595
|
+
mainThread.postMessage({
|
|
596
|
+
t: 6 /* ChannelAck */,
|
|
597
|
+
id,
|
|
598
|
+
source
|
|
599
|
+
});
|
|
600
|
+
};
|
|
601
|
+
if (mainThread.addEventListener) {
|
|
602
|
+
mainThread.addEventListener("message", handler);
|
|
603
|
+
} else {
|
|
604
|
+
mainThread.onmessage = handler;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
class PeerChannel {
|
|
609
|
+
endpoint;
|
|
610
|
+
remote;
|
|
611
|
+
constructor(port, localApi, options) {
|
|
612
|
+
this.endpoint = createEndpoint(port, options);
|
|
613
|
+
this.endpoint.expose(localApi);
|
|
614
|
+
this.remote = wrap(this.endpoint);
|
|
615
|
+
port.start();
|
|
616
|
+
}
|
|
617
|
+
getRemote() {
|
|
618
|
+
return this.remote;
|
|
619
|
+
}
|
|
620
|
+
release() {
|
|
621
|
+
this.endpoint.release();
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function createPeerPair(apiA, apiB, options) {
|
|
625
|
+
const channel = new MessageChannel;
|
|
626
|
+
const endpointA = createEndpoint(channel.port1, options);
|
|
627
|
+
endpointA.expose(apiA);
|
|
628
|
+
const endpointB = createEndpoint(channel.port2, options);
|
|
629
|
+
endpointB.expose(apiB);
|
|
630
|
+
channel.port1.start();
|
|
631
|
+
channel.port2.start();
|
|
632
|
+
return [wrap(endpointA), wrap(endpointB)];
|
|
633
|
+
}
|
|
634
|
+
// src/index.ts
|
|
635
|
+
function expose(api, options) {
|
|
636
|
+
const self = globalThis;
|
|
637
|
+
const endpoint = createEndpoint(self, options);
|
|
638
|
+
endpoint.expose(api);
|
|
639
|
+
return endpoint;
|
|
640
|
+
}
|
|
641
|
+
function remote(worker, options) {
|
|
642
|
+
const endpoint = createEndpoint(worker, options);
|
|
643
|
+
return wrap(endpoint);
|
|
644
|
+
}
|
|
645
|
+
function transfer(value) {
|
|
646
|
+
return value;
|
|
647
|
+
}
|
|
648
|
+
function callback(fn, _options) {
|
|
649
|
+
return fn;
|
|
650
|
+
}
|
|
651
|
+
export {
|
|
652
|
+
wrap,
|
|
653
|
+
transfer,
|
|
654
|
+
setupChannelHandler,
|
|
655
|
+
serializeError,
|
|
656
|
+
remote,
|
|
657
|
+
releaseProxy,
|
|
658
|
+
isTransferable,
|
|
659
|
+
isProxy,
|
|
660
|
+
isMessagePort,
|
|
661
|
+
isFunction,
|
|
662
|
+
getEndpoint,
|
|
663
|
+
expose,
|
|
664
|
+
deserializeError,
|
|
665
|
+
createWorkerChannel,
|
|
666
|
+
createRegistry,
|
|
667
|
+
createPeerPair,
|
|
668
|
+
createEndpoint,
|
|
669
|
+
callback,
|
|
670
|
+
acceptChannel,
|
|
671
|
+
REMOTE_PROXY,
|
|
672
|
+
RELEASE,
|
|
673
|
+
PeerChannel,
|
|
674
|
+
MessageType,
|
|
675
|
+
Endpoint,
|
|
676
|
+
ENDPOINT,
|
|
677
|
+
CREATE_CHANNEL
|
|
678
|
+
};
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker RPC - Proxy Module
|
|
3
|
+
* Creates type-safe proxies for remote API access
|
|
4
|
+
*/
|
|
5
|
+
import { Endpoint } from "./endpoint";
|
|
6
|
+
import { type RemoteObject } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* Wrap a worker/endpoint to create a type-safe remote API proxy
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* interface WorkerApi {
|
|
13
|
+
* add(a: number, b: number): number;
|
|
14
|
+
* db: {
|
|
15
|
+
* users: {
|
|
16
|
+
* find(id: string): Promise<User>;
|
|
17
|
+
* };
|
|
18
|
+
* };
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* const worker = new Worker("./worker.ts");
|
|
22
|
+
* const api = wrap<WorkerApi>(worker);
|
|
23
|
+
*
|
|
24
|
+
* // All calls are async and type-safe
|
|
25
|
+
* const sum = await api.add(1, 2);
|
|
26
|
+
* const user = await api.db.users.find("123");
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function wrap<T extends object>(endpoint: Endpoint): RemoteObject<T>;
|
|
30
|
+
export declare function wrap<T extends object>(endpoint: Endpoint, options?: {
|
|
31
|
+
timeout?: number;
|
|
32
|
+
}): RemoteObject<T>;
|
|
33
|
+
/**
|
|
34
|
+
* Check if a value is a remote proxy
|
|
35
|
+
*/
|
|
36
|
+
export declare function isProxy(value: unknown): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Get the endpoint from a proxy
|
|
39
|
+
*/
|
|
40
|
+
export declare function getEndpoint(proxy: unknown): Endpoint | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Release a proxy and its underlying endpoint
|
|
43
|
+
*/
|
|
44
|
+
export declare function releaseProxy(proxy: unknown): void;
|
|
45
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,SAAS,CAAC;AA6GjB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;AAC5E,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EACnC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,YAAY,CAAC,CAAC,CAAC,CAAC;AAKnB;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAU/C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAKhE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAIjD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker RPC - Serialization Module
|
|
3
|
+
* Minimal serialization - only handles Error objects for name/stack preservation
|
|
4
|
+
* All other data passes through structured clone directly
|
|
5
|
+
*/
|
|
6
|
+
/** Serialized error for transmission */
|
|
7
|
+
export interface SerializedError {
|
|
8
|
+
/** Error message */
|
|
9
|
+
e: string;
|
|
10
|
+
/** Error name (e.g., "TypeError") */
|
|
11
|
+
n: string;
|
|
12
|
+
/** Error stack trace */
|
|
13
|
+
s?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Serialize an Error object for transmission
|
|
17
|
+
* Preserves name and stack which structured clone doesn't handle well
|
|
18
|
+
*/
|
|
19
|
+
export declare function serializeError(error: Error): SerializedError;
|
|
20
|
+
/**
|
|
21
|
+
* Deserialize an error from transmission
|
|
22
|
+
* Reconstructs the Error with proper name and stack
|
|
23
|
+
*/
|
|
24
|
+
export declare function deserializeError(data: SerializedError): Error;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a value is a function
|
|
27
|
+
*/
|
|
28
|
+
export declare function isFunction(value: unknown): value is Function;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a value is a MessagePort
|
|
31
|
+
*/
|
|
32
|
+
export declare function isMessagePort(value: unknown): value is MessagePort;
|
|
33
|
+
/**
|
|
34
|
+
* Check if a value is transferable (ArrayBuffer or MessagePort)
|
|
35
|
+
*/
|
|
36
|
+
export declare function isTransferable(value: unknown): value is Transferable;
|
|
37
|
+
//# sourceMappingURL=serialize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC9B,oBAAoB;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,qCAAqC;IACrC,CAAC,EAAE,MAAM,CAAC;IACV,wBAAwB;IACxB,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,eAAe,CAM5D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,KAAK,CAO7D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAKlE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAKpE"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker RPC - Type Definitions
|
|
3
|
+
* High-performance, type-safe worker communication for Bun.js
|
|
4
|
+
*/
|
|
5
|
+
/** Unique identifier for tracking RPC calls */
|
|
6
|
+
export type CallId = string;
|
|
7
|
+
/** Unique identifier for callback functions */
|
|
8
|
+
export type CallbackId = string;
|
|
9
|
+
/** Path to a nested method (e.g., ['db', 'users', 'find']) */
|
|
10
|
+
export type MethodPath = string[];
|
|
11
|
+
/** Message types for the RPC protocol */
|
|
12
|
+
export declare enum MessageType {
|
|
13
|
+
/** Request to invoke a method */
|
|
14
|
+
Call = 0,
|
|
15
|
+
/** Successful response */
|
|
16
|
+
Result = 1,
|
|
17
|
+
/** Error response */
|
|
18
|
+
Error = 2,
|
|
19
|
+
/** Callback invocation */
|
|
20
|
+
Callback = 3,
|
|
21
|
+
/** Release a callback reference */
|
|
22
|
+
Release = 4,
|
|
23
|
+
/** Establish a direct channel between workers */
|
|
24
|
+
Channel = 5,
|
|
25
|
+
/** Acknowledgment for channel establishment */
|
|
26
|
+
ChannelAck = 6
|
|
27
|
+
}
|
|
28
|
+
/** Base message structure */
|
|
29
|
+
export interface BaseMessage {
|
|
30
|
+
/** Message type discriminator */
|
|
31
|
+
t: MessageType;
|
|
32
|
+
/** Call ID for request/response correlation */
|
|
33
|
+
id: CallId;
|
|
34
|
+
}
|
|
35
|
+
/** Request to invoke a remote method */
|
|
36
|
+
export interface CallMessage extends BaseMessage {
|
|
37
|
+
t: MessageType.Call;
|
|
38
|
+
/** Method path (supports nested objects) */
|
|
39
|
+
p: MethodPath;
|
|
40
|
+
/** Arguments (passed raw via structured clone) */
|
|
41
|
+
a: unknown[];
|
|
42
|
+
/** Callback mappings: argIndex -> callbackId (only for function arguments) */
|
|
43
|
+
c?: Record<number, CallbackId>;
|
|
44
|
+
}
|
|
45
|
+
/** Successful response */
|
|
46
|
+
export interface ResultMessage extends BaseMessage {
|
|
47
|
+
t: MessageType.Result;
|
|
48
|
+
/** Return value (passed raw via structured clone) */
|
|
49
|
+
v: unknown;
|
|
50
|
+
/** Callback ID if return value is a function */
|
|
51
|
+
c?: CallbackId;
|
|
52
|
+
}
|
|
53
|
+
/** Error response */
|
|
54
|
+
export interface ErrorMessage extends BaseMessage {
|
|
55
|
+
t: MessageType.Error;
|
|
56
|
+
/** Error message */
|
|
57
|
+
e: string;
|
|
58
|
+
/** Error name (e.g., "TypeError", "RangeError") */
|
|
59
|
+
n?: string;
|
|
60
|
+
/** Error stack trace */
|
|
61
|
+
s?: string;
|
|
62
|
+
}
|
|
63
|
+
/** Callback invocation request */
|
|
64
|
+
export interface CallbackMessage extends BaseMessage {
|
|
65
|
+
t: MessageType.Callback;
|
|
66
|
+
/** Callback ID */
|
|
67
|
+
c: CallbackId;
|
|
68
|
+
/** Arguments (passed raw via structured clone) */
|
|
69
|
+
a: unknown[];
|
|
70
|
+
/** Callback mappings for nested callbacks in args */
|
|
71
|
+
cb?: Record<number, CallbackId>;
|
|
72
|
+
}
|
|
73
|
+
/** Release a callback reference */
|
|
74
|
+
export interface ReleaseMessage extends BaseMessage {
|
|
75
|
+
t: MessageType.Release;
|
|
76
|
+
/** Callback IDs to release */
|
|
77
|
+
c: CallbackId[];
|
|
78
|
+
}
|
|
79
|
+
/** Channel establishment message */
|
|
80
|
+
export interface ChannelMessage extends BaseMessage {
|
|
81
|
+
t: MessageType.Channel;
|
|
82
|
+
/** Target worker identifier */
|
|
83
|
+
target: string;
|
|
84
|
+
/** MessagePort for direct communication */
|
|
85
|
+
port: MessagePort;
|
|
86
|
+
}
|
|
87
|
+
/** Channel acknowledgment */
|
|
88
|
+
export interface ChannelAckMessage extends BaseMessage {
|
|
89
|
+
t: MessageType.ChannelAck;
|
|
90
|
+
/** Source worker identifier */
|
|
91
|
+
source: string;
|
|
92
|
+
}
|
|
93
|
+
/** Union of all message types */
|
|
94
|
+
export type RpcMessage = CallMessage | ResultMessage | ErrorMessage | CallbackMessage | ReleaseMessage | ChannelMessage | ChannelAckMessage;
|
|
95
|
+
/** Unwrap a Promise type */
|
|
96
|
+
export type Unpromise<T> = T extends Promise<infer U> ? U : T;
|
|
97
|
+
/** Make a function async if it isn't already */
|
|
98
|
+
export type Promisify<T> = T extends (...args: infer A) => infer R ? (...args: A) => Promise<Unpromise<R>> : never;
|
|
99
|
+
/** Convert a function type to its remote callable version */
|
|
100
|
+
export type RemoteFunction<T> = T extends (...args: infer A) => infer R ? (...args: A) => Promise<Unpromise<R>> : never;
|
|
101
|
+
/** Convert an object type to its remote callable version */
|
|
102
|
+
export type RemoteObject<T> = {
|
|
103
|
+
[K in keyof T]: T[K] extends (...args: any[]) => any ? RemoteFunction<T[K]> : T[K] extends object ? RemoteObject<T[K]> : T[K];
|
|
104
|
+
};
|
|
105
|
+
/** Symbol to mark a proxy as a remote reference */
|
|
106
|
+
export declare const REMOTE_PROXY: unique symbol;
|
|
107
|
+
/** Symbol for accessing the underlying endpoint */
|
|
108
|
+
export declare const ENDPOINT: unique symbol;
|
|
109
|
+
/** Symbol for releasing a proxy */
|
|
110
|
+
export declare const RELEASE: unique symbol;
|
|
111
|
+
/** Symbol for creating a direct channel */
|
|
112
|
+
export declare const CREATE_CHANNEL: unique symbol;
|
|
113
|
+
/** Configuration options for the RPC endpoint */
|
|
114
|
+
export interface EndpointOptions {
|
|
115
|
+
/** Timeout for RPC calls in milliseconds (default: 30000) */
|
|
116
|
+
timeout?: number;
|
|
117
|
+
/** Custom error handler */
|
|
118
|
+
onError?: (error: Error) => void;
|
|
119
|
+
/** Enable debug logging */
|
|
120
|
+
debug?: boolean;
|
|
121
|
+
}
|
|
122
|
+
/** Configuration for exposing an API */
|
|
123
|
+
export interface ExposeOptions {
|
|
124
|
+
/** Maximum depth for nested objects (default: 10) */
|
|
125
|
+
maxDepth?: number;
|
|
126
|
+
}
|
|
127
|
+
/** Pending call information */
|
|
128
|
+
export interface PendingCall {
|
|
129
|
+
resolve: (value: any) => void;
|
|
130
|
+
reject: (error: Error) => void;
|
|
131
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
132
|
+
}
|
|
133
|
+
/** Callback registration */
|
|
134
|
+
export interface CallbackRegistration {
|
|
135
|
+
fn: Function;
|
|
136
|
+
/** Number of times this callback can be invoked (-1 = unlimited) */
|
|
137
|
+
remaining: number;
|
|
138
|
+
}
|
|
139
|
+
/** Transferable types */
|
|
140
|
+
export type TransferableValue = ArrayBuffer | MessagePort;
|
|
141
|
+
/** Worker-like message target */
|
|
142
|
+
export interface MessageTarget {
|
|
143
|
+
postMessage(message: any, transfer?: any[]): void;
|
|
144
|
+
addEventListener?(type: string, listener: (event: any) => void): void;
|
|
145
|
+
removeEventListener?(type: string, listener: (event: any) => void): void;
|
|
146
|
+
onmessage?: ((event: any) => void) | null;
|
|
147
|
+
start?(): void;
|
|
148
|
+
close?(): void;
|
|
149
|
+
}
|
|
150
|
+
/** Extended Worker type with Bun-specific features */
|
|
151
|
+
export interface BunWorker extends MessageTarget {
|
|
152
|
+
terminate?(): void;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xentobias/worker-rpc",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "High-performance, type-safe RPC for Bun.js Workers",
|
|
5
|
+
"module": "src/index.ts",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun && tsc --declaration",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"test": "bun test",
|
|
22
|
+
"example": "bun run examples/basic/main.ts",
|
|
23
|
+
"example:nested": "bun run examples/nested/main.ts",
|
|
24
|
+
"example:callbacks": "bun run examples/callbacks/main.ts",
|
|
25
|
+
"example:w2w": "bun run examples/worker-to-worker/main.ts",
|
|
26
|
+
"benchmark": "bun run benchmark/run.ts"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"bun",
|
|
30
|
+
"worker",
|
|
31
|
+
"rpc",
|
|
32
|
+
"comlink",
|
|
33
|
+
"typescript",
|
|
34
|
+
"proxy",
|
|
35
|
+
"message-passing"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/bun": "latest",
|
|
40
|
+
"comlink": "^4.4.2",
|
|
41
|
+
"typescript": "^5"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"typescript": "^5"
|
|
45
|
+
}
|
|
46
|
+
}
|