swarpc 0.5.0 → 0.6.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/swarpc.d.ts +4 -2
- package/dist/swarpc.d.ts.map +1 -1
- package/dist/swarpc.js +21 -7
- package/dist/types.d.ts +39 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +0 -6
- package/package.json +5 -2
- package/src/swarpc.ts +32 -21
- package/src/types.ts +63 -1
- package/src/utils.ts +0 -6
package/dist/swarpc.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ProceduresMap, type SwarpcClient, type SwarpcServer } from "./types.js";
|
|
1
|
+
import { Hooks, type ProceduresMap, type SwarpcClient, type SwarpcServer } from "./types.js";
|
|
2
2
|
export type { ProceduresMap, SwarpcClient, SwarpcServer } from "./types.js";
|
|
3
3
|
/**
|
|
4
4
|
* Creates a sw&rpc server instance.
|
|
@@ -15,9 +15,11 @@ export declare function Server<Procedures extends ProceduresMap>(procedures: Pro
|
|
|
15
15
|
* @param procedures procedures the client will be able to call
|
|
16
16
|
* @param param1 various options
|
|
17
17
|
* @param param1.worker if provided, the client will use this worker to post messages.
|
|
18
|
+
* @param param1.hooks hooks to run on messages received from the server
|
|
18
19
|
* @returns a sw&rpc client instance. Each property of the procedures map will be a method, that accepts an input and an optional onProgress callback.
|
|
19
20
|
*/
|
|
20
|
-
export declare function Client<Procedures extends ProceduresMap>(procedures: Procedures, { worker }?: {
|
|
21
|
+
export declare function Client<Procedures extends ProceduresMap>(procedures: Procedures, { worker, hooks }?: {
|
|
21
22
|
worker?: Worker;
|
|
23
|
+
hooks?: Hooks<Procedures>;
|
|
22
24
|
}): SwarpcClient<Procedures>;
|
|
23
25
|
//# sourceMappingURL=swarpc.d.ts.map
|
package/dist/swarpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swarpc.d.ts","sourceRoot":"","sources":["../src/swarpc.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"swarpc.d.ts","sourceRoot":"","sources":["../src/swarpc.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,EAML,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,YAAY,CAAA;AAGnB,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3E;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,UAAU,SAAS,aAAa,EACrD,UAAU,EAAE,UAAU,EACtB,EAAE,MAAM,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GACnC,YAAY,CAAC,UAAU,CAAC,CAuG1B;AA+FD;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,UAAU,SAAS,aAAa,EACrD,UAAU,EAAE,UAAU,EACtB,EAAE,MAAM,EAAE,KAAU,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;CAAO,GAC1E,YAAY,CAAC,UAAU,CAAC,CA0D1B"}
|
package/dist/swarpc.js
CHANGED
|
@@ -53,7 +53,13 @@ export function Server(procedures, { worker } = {}) {
|
|
|
53
53
|
// Get autotransfer preference from the procedure definition
|
|
54
54
|
const { autotransfer = "output-only" } = instance[zProcedures][functionName];
|
|
55
55
|
// Shorthand function with functionName, requestId, etc. set
|
|
56
|
-
const postMsg = async (data) => postMessage({
|
|
56
|
+
const postMsg = async (data) => postMessage({
|
|
57
|
+
by: "sw&rpc",
|
|
58
|
+
functionName,
|
|
59
|
+
requestId,
|
|
60
|
+
autotransfer,
|
|
61
|
+
...data,
|
|
62
|
+
});
|
|
57
63
|
// Prepare a function to post errors back to the client
|
|
58
64
|
const postError = async (error) => postMsg({
|
|
59
65
|
error: {
|
|
@@ -105,7 +111,7 @@ let _clientListenerStarted = false;
|
|
|
105
111
|
* @param worker if provided, the client will use this worker to listen for messages, instead of using the service worker
|
|
106
112
|
* @returns
|
|
107
113
|
*/
|
|
108
|
-
async function startClientListener(worker) {
|
|
114
|
+
async function startClientListener(worker, hooks = {}) {
|
|
109
115
|
if (_clientListenerStarted)
|
|
110
116
|
return;
|
|
111
117
|
// Get service worker registration if no worker is provided
|
|
@@ -123,8 +129,12 @@ async function startClientListener(worker) {
|
|
|
123
129
|
l.client.debug("", "Starting client listener on", w);
|
|
124
130
|
w.addEventListener("message", (event) => {
|
|
125
131
|
// Get the data from the event
|
|
132
|
+
const eventData = event.data || {};
|
|
133
|
+
// Ignore other messages that aren't for us
|
|
134
|
+
if (eventData?.by !== "sw&rpc")
|
|
135
|
+
return;
|
|
126
136
|
// We don't use a arktype schema here, we trust the server to send valid data
|
|
127
|
-
const { functionName, requestId, ...data } =
|
|
137
|
+
const { functionName, requestId, ...data } = eventData;
|
|
128
138
|
// Sanity check in case we somehow receive a message without requestId
|
|
129
139
|
if (!requestId) {
|
|
130
140
|
throw new Error("[SWARPC Client] Message received without requestId");
|
|
@@ -134,16 +144,19 @@ async function startClientListener(worker) {
|
|
|
134
144
|
if (!handlers) {
|
|
135
145
|
throw new Error(`[SWARPC Client] ${requestId} has no active request handlers`);
|
|
136
146
|
}
|
|
137
|
-
// React to the data received
|
|
138
|
-
//
|
|
147
|
+
// React to the data received: call hook, call handler,
|
|
148
|
+
// and remove the request from pendingRequests (unless it's a progress update)
|
|
139
149
|
if ("error" in data) {
|
|
150
|
+
hooks.error?.(functionName, new Error(data.error.message));
|
|
140
151
|
handlers.reject(new Error(data.error.message));
|
|
141
152
|
pendingRequests.delete(requestId);
|
|
142
153
|
}
|
|
143
154
|
else if ("progress" in data) {
|
|
155
|
+
hooks.progress?.(functionName, data.progress);
|
|
144
156
|
handlers.onProgress(data.progress);
|
|
145
157
|
}
|
|
146
158
|
else if ("result" in data) {
|
|
159
|
+
hooks.success?.(functionName, data.result);
|
|
147
160
|
handlers.resolve(data.result);
|
|
148
161
|
pendingRequests.delete(requestId);
|
|
149
162
|
}
|
|
@@ -155,9 +168,10 @@ async function startClientListener(worker) {
|
|
|
155
168
|
* @param procedures procedures the client will be able to call
|
|
156
169
|
* @param param1 various options
|
|
157
170
|
* @param param1.worker if provided, the client will use this worker to post messages.
|
|
171
|
+
* @param param1.hooks hooks to run on messages received from the server
|
|
158
172
|
* @returns a sw&rpc client instance. Each property of the procedures map will be a method, that accepts an input and an optional onProgress callback.
|
|
159
173
|
*/
|
|
160
|
-
export function Client(procedures, { worker } = {}) {
|
|
174
|
+
export function Client(procedures, { worker, hooks = {} } = {}) {
|
|
161
175
|
// Store procedures on a symbol key, to avoid conflicts with procedure names
|
|
162
176
|
const instance = { [zProcedures]: procedures };
|
|
163
177
|
for (const functionName of Object.keys(procedures)) {
|
|
@@ -170,7 +184,7 @@ export function Client(procedures, { worker } = {}) {
|
|
|
170
184
|
// Validate the input against the procedure's input schema
|
|
171
185
|
procedures[functionName].input.assert(input);
|
|
172
186
|
// Ensure that we're listening for messages from the server
|
|
173
|
-
await startClientListener(worker);
|
|
187
|
+
await startClientListener(worker, hooks);
|
|
174
188
|
// If no worker is provided, we use the service worker
|
|
175
189
|
const w = worker ?? (await navigator.serviceWorker.ready.then((r) => r.active));
|
|
176
190
|
if (!w) {
|
package/dist/types.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export type Procedure<I extends Type, P extends Type, S extends Type> = {
|
|
|
33
33
|
*/
|
|
34
34
|
export type ProcedureImplementation<I extends Type, P extends Type, S extends Type> = (input: I["inferOut"], onProgress: (progress: P["inferIn"]) => void) => Promise<S["inferIn"]>;
|
|
35
35
|
/**
|
|
36
|
-
* Declarations of procedures by name
|
|
36
|
+
* Declarations of procedures by name.
|
|
37
37
|
*/
|
|
38
38
|
export type ProceduresMap = Record<string, Procedure<Type, Type, Type>>;
|
|
39
39
|
/**
|
|
@@ -42,6 +42,44 @@ export type ProceduresMap = Record<string, Procedure<Type, Type, Type>>;
|
|
|
42
42
|
export type ImplementationsMap<Procedures extends ProceduresMap> = {
|
|
43
43
|
[F in keyof Procedures]: ProcedureImplementation<Procedures[F]["input"], Procedures[F]["progress"], Procedures[F]["success"]>;
|
|
44
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Declaration of hooks to run on messages received from the server
|
|
47
|
+
*/
|
|
48
|
+
export type Hooks<Procedures extends ProceduresMap> = {
|
|
49
|
+
/**
|
|
50
|
+
* Called when a procedure call has been successful.
|
|
51
|
+
*/
|
|
52
|
+
success?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, data: Procedures[Procedure]["success"]["inferOut"]) => void;
|
|
53
|
+
/**
|
|
54
|
+
* Called when a procedure call has failed.
|
|
55
|
+
*/
|
|
56
|
+
error?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, error: Error) => void;
|
|
57
|
+
/**
|
|
58
|
+
* Called when a procedure call sends progress updates.
|
|
59
|
+
*/
|
|
60
|
+
progress?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, data: Procedures[Procedure]["progress"]["inferOut"]) => void;
|
|
61
|
+
};
|
|
62
|
+
export type PayloadHeader<PM extends ProceduresMap, Name extends keyof PM = keyof PM> = {
|
|
63
|
+
by: "sw&rpc";
|
|
64
|
+
functionName: Name & string;
|
|
65
|
+
requestId: string;
|
|
66
|
+
autotransfer: PM[Name]["autotransfer"];
|
|
67
|
+
};
|
|
68
|
+
export type PayloadCore<PM extends ProceduresMap, Name extends keyof PM = keyof PM> = {
|
|
69
|
+
input: PM[Name]["input"]["inferOut"];
|
|
70
|
+
} | {
|
|
71
|
+
progress: PM[Name]["progress"]["inferOut"];
|
|
72
|
+
} | {
|
|
73
|
+
result: PM[Name]["success"]["inferOut"];
|
|
74
|
+
} | {
|
|
75
|
+
error: {
|
|
76
|
+
message: string;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* The effective payload as sent by the server to the client
|
|
81
|
+
*/
|
|
82
|
+
export type Payload<PM extends ProceduresMap, Name extends keyof PM = keyof PM> = PayloadHeader<PM, Name> & PayloadCore<PM, Name>;
|
|
45
83
|
/**
|
|
46
84
|
* A procedure's corresponding method on the client instance -- used to call the procedure
|
|
47
85
|
*/
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEnC;;GAEG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,SAAS,IAAI,EAAE,CAAC,SAAS,IAAI,IAAI;IACtE;;OAEG;IACH,KAAK,EAAE,CAAC,CAAA;IACR;;;OAGG;IACH,QAAQ,EAAE,CAAC,CAAA;IACX;;OAEG;IACH,OAAO,EAAE,CAAC,CAAA;IACV;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,aAAa,CAAA;CAClD,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,uBAAuB,CACjC,CAAC,SAAS,IAAI,EACd,CAAC,SAAS,IAAI,EACd,CAAC,SAAS,IAAI,IACZ,CACF,KAAK,EAAE,CAAC,CAAC,UAAU,CAAC,EACpB,UAAU,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,KACzC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;AAE1B;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAEvE;;GAEG;AACH,MAAM,MAAM,kBAAkB,CAAC,UAAU,SAAS,aAAa,IAAI;KAChE,CAAC,IAAI,MAAM,UAAU,GAAG,uBAAuB,CAC9C,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EACtB,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,EACzB,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CACzB;CACF,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAChE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,EAC5B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,KAAK,IAAI,KACvD,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,CAAC,CAAC,CAAA;AAEtC;;GAEG;AACH,eAAO,MAAM,gBAAgB,eAAmC,CAAA;AAChE;;GAEG;AACH,eAAO,MAAM,WAAW,eAA8B,CAAA;AAEtD;;GAEG;AACH,MAAM,MAAM,YAAY,CAAC,UAAU,SAAS,aAAa,IAAI;IAC3D,CAAC,WAAW,CAAC,EAAE,UAAU,CAAA;CAC1B,GAAG;KACD,CAAC,IAAI,MAAM,UAAU,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;CACrD,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,CAAC,UAAU,SAAS,aAAa,IAAI;IAC3D,CAAC,WAAW,CAAC,EAAE,UAAU,CAAA;IACzB,CAAC,gBAAgB,CAAC,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAA;IAClD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B,GAAG;KACD,CAAC,IAAI,MAAM,UAAU,GAAG,CACvB,IAAI,EAAE,uBAAuB,CAC3B,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EACtB,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,EACzB,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CACzB,KACE,IAAI;CACV,CAAA"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAWA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,GAAG,GAAG,YAAY,EAAE,CAsB5D"}
|
package/dist/utils.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
// TODO: keep it in sync with web standards, how?
|
|
2
2
|
const transferableClasses = [
|
|
3
|
-
OffscreenCanvas,
|
|
4
|
-
ImageBitmap,
|
|
5
3
|
MessagePort,
|
|
6
|
-
MediaSourceHandle,
|
|
7
4
|
ReadableStream,
|
|
8
5
|
WritableStream,
|
|
9
6
|
TransformStream,
|
|
10
|
-
AudioData,
|
|
11
|
-
VideoFrame,
|
|
12
|
-
RTCDataChannel,
|
|
13
7
|
ArrayBuffer,
|
|
14
8
|
];
|
|
15
9
|
export function findTransferables(value) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swarpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Full type-safe RPC library for service worker -- move things off of the UI thread with ease!",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"service-workers",
|
|
@@ -30,12 +30,15 @@
|
|
|
30
30
|
"build": "tsc",
|
|
31
31
|
"dev": "tsc --watch",
|
|
32
32
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
33
|
-
"typedoc": "typedoc src/swarpc.ts src/types.ts --readme README.md"
|
|
33
|
+
"typedoc": "typedoc src/swarpc.ts src/types.ts --readme README.md",
|
|
34
|
+
"version": "kacl release && prettier -w CHANGELOG.md && git add CHANGELOG.md"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"arktype": "^2.1.20"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
40
|
+
"kacl": "^1.1.1",
|
|
41
|
+
"prettier": "^3.6.2",
|
|
39
42
|
"typedoc": "^0.28.7",
|
|
40
43
|
"typescript": "^5.8.3"
|
|
41
44
|
}
|
package/src/swarpc.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type } from "arktype"
|
|
2
2
|
import {
|
|
3
|
+
Hooks,
|
|
3
4
|
ImplementationsMap,
|
|
4
|
-
|
|
5
|
+
Payload,
|
|
6
|
+
PayloadCore,
|
|
5
7
|
zImplementations,
|
|
6
8
|
zProcedures,
|
|
7
9
|
type ProceduresMap,
|
|
@@ -53,17 +55,7 @@ export function Server<Procedures extends ProceduresMap>(
|
|
|
53
55
|
|
|
54
56
|
instance.start = (self: Window) => {
|
|
55
57
|
// Used to post messages back to the client
|
|
56
|
-
const postMessage = async (
|
|
57
|
-
data: {
|
|
58
|
-
functionName: string
|
|
59
|
-
requestId: string
|
|
60
|
-
autotransfer: Procedure<Type, Type, Type>["autotransfer"]
|
|
61
|
-
} & Partial<{
|
|
62
|
-
result: any
|
|
63
|
-
error: any
|
|
64
|
-
progress: any
|
|
65
|
-
}>
|
|
66
|
-
) => {
|
|
58
|
+
const postMessage = async (data: Payload<Procedures>) => {
|
|
67
59
|
const transfer =
|
|
68
60
|
data.autotransfer === "never" ? [] : findTransferables(data)
|
|
69
61
|
|
|
@@ -91,8 +83,15 @@ export function Server<Procedures extends ProceduresMap>(
|
|
|
91
83
|
|
|
92
84
|
// Shorthand function with functionName, requestId, etc. set
|
|
93
85
|
const postMsg = async (
|
|
94
|
-
data:
|
|
95
|
-
) =>
|
|
86
|
+
data: PayloadCore<Procedures, typeof functionName>
|
|
87
|
+
) =>
|
|
88
|
+
postMessage({
|
|
89
|
+
by: "sw&rpc",
|
|
90
|
+
functionName,
|
|
91
|
+
requestId,
|
|
92
|
+
autotransfer,
|
|
93
|
+
...data,
|
|
94
|
+
})
|
|
96
95
|
|
|
97
96
|
// Prepare a function to post errors back to the client
|
|
98
97
|
const postError = async (error: any) =>
|
|
@@ -158,7 +157,10 @@ let _clientListenerStarted = false
|
|
|
158
157
|
* @param worker if provided, the client will use this worker to listen for messages, instead of using the service worker
|
|
159
158
|
* @returns
|
|
160
159
|
*/
|
|
161
|
-
async function startClientListener
|
|
160
|
+
async function startClientListener<Procedures extends ProceduresMap>(
|
|
161
|
+
worker?: Worker,
|
|
162
|
+
hooks: Hooks<Procedures> = {}
|
|
163
|
+
) {
|
|
162
164
|
if (_clientListenerStarted) return
|
|
163
165
|
|
|
164
166
|
// Get service worker registration if no worker is provided
|
|
@@ -179,9 +181,14 @@ async function startClientListener(worker?: Worker) {
|
|
|
179
181
|
l.client.debug("", "Starting client listener on", w)
|
|
180
182
|
w.addEventListener("message", (event) => {
|
|
181
183
|
// Get the data from the event
|
|
184
|
+
const eventData = (event as MessageEvent).data || {}
|
|
185
|
+
|
|
186
|
+
// Ignore other messages that aren't for us
|
|
187
|
+
if (eventData?.by !== "sw&rpc") return
|
|
188
|
+
|
|
182
189
|
// We don't use a arktype schema here, we trust the server to send valid data
|
|
183
190
|
const { functionName, requestId, ...data } =
|
|
184
|
-
|
|
191
|
+
eventData as Payload<Procedures>
|
|
185
192
|
|
|
186
193
|
// Sanity check in case we somehow receive a message without requestId
|
|
187
194
|
if (!requestId) {
|
|
@@ -196,14 +203,17 @@ async function startClientListener(worker?: Worker) {
|
|
|
196
203
|
)
|
|
197
204
|
}
|
|
198
205
|
|
|
199
|
-
// React to the data received
|
|
200
|
-
//
|
|
206
|
+
// React to the data received: call hook, call handler,
|
|
207
|
+
// and remove the request from pendingRequests (unless it's a progress update)
|
|
201
208
|
if ("error" in data) {
|
|
209
|
+
hooks.error?.(functionName, new Error(data.error.message))
|
|
202
210
|
handlers.reject(new Error(data.error.message))
|
|
203
211
|
pendingRequests.delete(requestId)
|
|
204
212
|
} else if ("progress" in data) {
|
|
213
|
+
hooks.progress?.(functionName, data.progress)
|
|
205
214
|
handlers.onProgress(data.progress)
|
|
206
215
|
} else if ("result" in data) {
|
|
216
|
+
hooks.success?.(functionName, data.result)
|
|
207
217
|
handlers.resolve(data.result)
|
|
208
218
|
pendingRequests.delete(requestId)
|
|
209
219
|
}
|
|
@@ -217,11 +227,12 @@ async function startClientListener(worker?: Worker) {
|
|
|
217
227
|
* @param procedures procedures the client will be able to call
|
|
218
228
|
* @param param1 various options
|
|
219
229
|
* @param param1.worker if provided, the client will use this worker to post messages.
|
|
230
|
+
* @param param1.hooks hooks to run on messages received from the server
|
|
220
231
|
* @returns a sw&rpc client instance. Each property of the procedures map will be a method, that accepts an input and an optional onProgress callback.
|
|
221
232
|
*/
|
|
222
233
|
export function Client<Procedures extends ProceduresMap>(
|
|
223
234
|
procedures: Procedures,
|
|
224
|
-
{ worker }: { worker?: Worker } = {}
|
|
235
|
+
{ worker, hooks = {} }: { worker?: Worker; hooks?: Hooks<Procedures> } = {}
|
|
225
236
|
): SwarpcClient<Procedures> {
|
|
226
237
|
// Store procedures on a symbol key, to avoid conflicts with procedure names
|
|
227
238
|
const instance = { [zProcedures]: procedures } as Partial<
|
|
@@ -243,7 +254,7 @@ export function Client<Procedures extends ProceduresMap>(
|
|
|
243
254
|
// Validate the input against the procedure's input schema
|
|
244
255
|
procedures[functionName].input.assert(input)
|
|
245
256
|
// Ensure that we're listening for messages from the server
|
|
246
|
-
await startClientListener(worker)
|
|
257
|
+
await startClientListener(worker, hooks)
|
|
247
258
|
|
|
248
259
|
// If no worker is provided, we use the service worker
|
|
249
260
|
const w =
|
package/src/types.ts
CHANGED
|
@@ -43,7 +43,7 @@ export type ProcedureImplementation<
|
|
|
43
43
|
) => Promise<S["inferIn"]>
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
* Declarations of procedures by name
|
|
46
|
+
* Declarations of procedures by name.
|
|
47
47
|
*/
|
|
48
48
|
export type ProceduresMap = Record<string, Procedure<Type, Type, Type>>
|
|
49
49
|
|
|
@@ -58,6 +58,68 @@ export type ImplementationsMap<Procedures extends ProceduresMap> = {
|
|
|
58
58
|
>
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Declaration of hooks to run on messages received from the server
|
|
63
|
+
*/
|
|
64
|
+
export type Hooks<Procedures extends ProceduresMap> = {
|
|
65
|
+
/**
|
|
66
|
+
* Called when a procedure call has been successful.
|
|
67
|
+
*/
|
|
68
|
+
success?: <Procedure extends keyof ProceduresMap>(
|
|
69
|
+
procedure: Procedure,
|
|
70
|
+
data: Procedures[Procedure]["success"]["inferOut"]
|
|
71
|
+
) => void
|
|
72
|
+
/**
|
|
73
|
+
* Called when a procedure call has failed.
|
|
74
|
+
*/
|
|
75
|
+
error?: <Procedure extends keyof ProceduresMap>(
|
|
76
|
+
procedure: Procedure,
|
|
77
|
+
error: Error
|
|
78
|
+
) => void
|
|
79
|
+
/**
|
|
80
|
+
* Called when a procedure call sends progress updates.
|
|
81
|
+
*/
|
|
82
|
+
progress?: <Procedure extends keyof ProceduresMap>(
|
|
83
|
+
procedure: Procedure,
|
|
84
|
+
data: Procedures[Procedure]["progress"]["inferOut"]
|
|
85
|
+
) => void
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type PayloadHeader<
|
|
89
|
+
PM extends ProceduresMap,
|
|
90
|
+
Name extends keyof PM = keyof PM
|
|
91
|
+
> = {
|
|
92
|
+
by: "sw&rpc"
|
|
93
|
+
functionName: Name & string
|
|
94
|
+
requestId: string
|
|
95
|
+
autotransfer: PM[Name]["autotransfer"]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type PayloadCore<
|
|
99
|
+
PM extends ProceduresMap,
|
|
100
|
+
Name extends keyof PM = keyof PM
|
|
101
|
+
> =
|
|
102
|
+
| {
|
|
103
|
+
input: PM[Name]["input"]["inferOut"]
|
|
104
|
+
}
|
|
105
|
+
| {
|
|
106
|
+
progress: PM[Name]["progress"]["inferOut"]
|
|
107
|
+
}
|
|
108
|
+
| {
|
|
109
|
+
result: PM[Name]["success"]["inferOut"]
|
|
110
|
+
}
|
|
111
|
+
| {
|
|
112
|
+
error: { message: string }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The effective payload as sent by the server to the client
|
|
117
|
+
*/
|
|
118
|
+
export type Payload<
|
|
119
|
+
PM extends ProceduresMap,
|
|
120
|
+
Name extends keyof PM = keyof PM
|
|
121
|
+
> = PayloadHeader<PM, Name> & PayloadCore<PM, Name>
|
|
122
|
+
|
|
61
123
|
/**
|
|
62
124
|
* A procedure's corresponding method on the client instance -- used to call the procedure
|
|
63
125
|
*/
|
package/src/utils.ts
CHANGED
|
@@ -2,16 +2,10 @@ type Constructor<T> = new (...args: any[]) => T
|
|
|
2
2
|
|
|
3
3
|
// TODO: keep it in sync with web standards, how?
|
|
4
4
|
const transferableClasses: Constructor<Transferable>[] = [
|
|
5
|
-
OffscreenCanvas,
|
|
6
|
-
ImageBitmap,
|
|
7
5
|
MessagePort,
|
|
8
|
-
MediaSourceHandle,
|
|
9
6
|
ReadableStream,
|
|
10
7
|
WritableStream,
|
|
11
8
|
TransformStream,
|
|
12
|
-
AudioData,
|
|
13
|
-
VideoFrame,
|
|
14
|
-
RTCDataChannel,
|
|
15
9
|
ArrayBuffer,
|
|
16
10
|
]
|
|
17
11
|
|