swarpc 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +32 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +19 -7
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/log.d.ts +21 -13
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +26 -16
- package/dist/server.d.ts +21 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +32 -24
- package/dist/types.d.ts +44 -23
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -0
- package/package.json +20 -3
- package/src/client.ts +36 -8
- package/src/index.ts +5 -0
- package/src/log.ts +68 -12
- package/src/server.ts +66 -46
- package/src/types.ts +43 -31
package/dist/client.d.ts
CHANGED
|
@@ -1,21 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
5
|
+
import { type Logger, type LogLevel } from "./log.js";
|
|
6
|
+
import { ClientMethod, Hooks, zProcedures, type ProceduresMap } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* The sw&rpc client instance, which provides {@link ClientMethod | methods to call procedures}.
|
|
9
|
+
* Each property of the procedures map will be a method, that accepts an input, an optional onProgress callback and an optional request ID.
|
|
10
|
+
* If you want to be able to cancel the request, you can set the request's ID yourself, and call `.abort(requestId, reason)` on the client instance to cancel it.
|
|
11
|
+
*/
|
|
12
|
+
export type SwarpcClient<Procedures extends ProceduresMap> = {
|
|
13
|
+
[zProcedures]: Procedures;
|
|
14
|
+
} & {
|
|
15
|
+
[F in keyof Procedures]: ClientMethod<Procedures[F]>;
|
|
16
|
+
};
|
|
4
17
|
/**
|
|
5
18
|
*
|
|
6
|
-
* @param procedures procedures the client will be able to call
|
|
19
|
+
* @param procedures procedures the client will be able to call, see {@link ProceduresMap}
|
|
7
20
|
* @param options various options
|
|
8
21
|
* @param options.worker if provided, the client will use this worker to post messages.
|
|
9
22
|
* @param options.hooks hooks to run on messages received from the server
|
|
10
|
-
* @
|
|
23
|
+
* @param options.restartListener if true, will force the listener to restart even if it has already been started
|
|
24
|
+
* @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, see {@link ClientMethod}
|
|
25
|
+
*
|
|
26
|
+
* An example of defining and using a client:
|
|
27
|
+
* {@includeCode ../example/src/routes/+page.svelte}
|
|
11
28
|
*/
|
|
12
|
-
export declare function Client<Procedures extends ProceduresMap>(procedures: Procedures, { worker, loglevel, hooks, }?: {
|
|
29
|
+
export declare function Client<Procedures extends ProceduresMap>(procedures: Procedures, { worker, loglevel, restartListener, hooks, }?: {
|
|
13
30
|
worker?: Worker;
|
|
14
31
|
hooks?: Hooks<Procedures>;
|
|
15
32
|
loglevel?: LogLevel;
|
|
33
|
+
restartListener?: boolean;
|
|
16
34
|
}): SwarpcClient<Procedures>;
|
|
35
|
+
/**
|
|
36
|
+
* Starts the client listener, which listens for messages from the sw&rpc server.
|
|
37
|
+
* @param worker if provided, the client will use this worker to listen for messages, instead of using the service worker
|
|
38
|
+
* @param force if true, will force the listener to restart even if it has already been started
|
|
39
|
+
* @returns
|
|
40
|
+
*/
|
|
41
|
+
export declare function startClientListener<Procedures extends ProceduresMap>(l: Logger, worker?: Worker, hooks?: Hooks<Procedures>): Promise<void>;
|
|
17
42
|
/**
|
|
18
43
|
* Generate a random request ID, used to identify requests between client and server.
|
|
44
|
+
* @source
|
|
19
45
|
* @returns a 6-character hexadecimal string
|
|
20
46
|
*/
|
|
21
47
|
export declare function makeRequestId(): string;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAgB,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnE,OAAO,EACL,YAAY,EACZ,KAAK,EAGL,WAAW,EACX,KAAK,aAAa,EACnB,MAAM,YAAY,CAAA;AAGnB;;;;GAIG;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;AAkBD;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,UAAU,SAAS,aAAa,EACrD,UAAU,EAAE,UAAU,EACtB,EACE,MAAM,EACN,QAAkB,EAClB,eAAuB,EACvB,KAAU,GACX,GAAE;IACD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;IACzB,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,eAAe,CAAC,EAAE,OAAO,CAAA;CACrB,GACL,YAAY,CAAC,UAAU,CAAC,CAiG1B;AA6BD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,UAAU,SAAS,aAAa,EACxE,CAAC,EAAE,MAAM,EACT,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,GAAE,KAAK,CAAC,UAAU,CAAM,iBA4D9B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
1
5
|
import { createLogger } from "./log.js";
|
|
2
|
-
import { zProcedures } from "./types.js";
|
|
6
|
+
import { zProcedures, } from "./types.js";
|
|
3
7
|
import { findTransferables } from "./utils.js";
|
|
4
8
|
/**
|
|
5
9
|
* Pending requests are stored in a map, where the key is the request ID.
|
|
@@ -11,14 +15,20 @@ const pendingRequests = new Map();
|
|
|
11
15
|
let _clientListenerStarted = false;
|
|
12
16
|
/**
|
|
13
17
|
*
|
|
14
|
-
* @param procedures procedures the client will be able to call
|
|
18
|
+
* @param procedures procedures the client will be able to call, see {@link ProceduresMap}
|
|
15
19
|
* @param options various options
|
|
16
20
|
* @param options.worker if provided, the client will use this worker to post messages.
|
|
17
21
|
* @param options.hooks hooks to run on messages received from the server
|
|
18
|
-
* @
|
|
22
|
+
* @param options.restartListener if true, will force the listener to restart even if it has already been started
|
|
23
|
+
* @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, see {@link ClientMethod}
|
|
24
|
+
*
|
|
25
|
+
* An example of defining and using a client:
|
|
26
|
+
* {@includeCode ../example/src/routes/+page.svelte}
|
|
19
27
|
*/
|
|
20
|
-
export function Client(procedures, { worker, loglevel = "debug", hooks = {}, } = {}) {
|
|
28
|
+
export function Client(procedures, { worker, loglevel = "debug", restartListener = false, hooks = {}, } = {}) {
|
|
21
29
|
const l = createLogger("client", loglevel);
|
|
30
|
+
if (restartListener)
|
|
31
|
+
_clientListenerStarted = false;
|
|
22
32
|
// Store procedures on a symbol key, to avoid conflicts with procedure names
|
|
23
33
|
const instance = { [zProcedures]: procedures };
|
|
24
34
|
for (const functionName of Object.keys(procedures)) {
|
|
@@ -96,9 +106,10 @@ async function postMessage(l, worker, hooks, message, options) {
|
|
|
96
106
|
/**
|
|
97
107
|
* Starts the client listener, which listens for messages from the sw&rpc server.
|
|
98
108
|
* @param worker if provided, the client will use this worker to listen for messages, instead of using the service worker
|
|
109
|
+
* @param force if true, will force the listener to restart even if it has already been started
|
|
99
110
|
* @returns
|
|
100
111
|
*/
|
|
101
|
-
async function startClientListener(l, worker, hooks = {}) {
|
|
112
|
+
export async function startClientListener(l, worker, hooks = {}) {
|
|
102
113
|
if (_clientListenerStarted)
|
|
103
114
|
return;
|
|
104
115
|
// Get service worker registration if no worker is provided
|
|
@@ -113,7 +124,7 @@ async function startClientListener(l, worker, hooks = {}) {
|
|
|
113
124
|
}
|
|
114
125
|
const w = worker ?? navigator.serviceWorker;
|
|
115
126
|
// Start listening for messages
|
|
116
|
-
l.debug(
|
|
127
|
+
l.debug(null, "Starting client listener", { worker, w, hooks });
|
|
117
128
|
w.addEventListener("message", (event) => {
|
|
118
129
|
// Get the data from the event
|
|
119
130
|
const eventData = event.data || {};
|
|
@@ -129,7 +140,7 @@ async function startClientListener(l, worker, hooks = {}) {
|
|
|
129
140
|
// Get the associated pending request handlers
|
|
130
141
|
const handlers = pendingRequests.get(requestId);
|
|
131
142
|
if (!handlers) {
|
|
132
|
-
throw new Error(`[SWARPC Client] ${requestId} has no active request handlers`);
|
|
143
|
+
throw new Error(`[SWARPC Client] ${requestId} has no active request handlers, cannot process ${JSON.stringify(data)}`);
|
|
133
144
|
}
|
|
134
145
|
// React to the data received: call hook, call handler,
|
|
135
146
|
// and remove the request from pendingRequests (unless it's a progress update)
|
|
@@ -152,6 +163,7 @@ async function startClientListener(l, worker, hooks = {}) {
|
|
|
152
163
|
}
|
|
153
164
|
/**
|
|
154
165
|
* Generate a random request ID, used to identify requests between client and server.
|
|
166
|
+
* @source
|
|
155
167
|
* @returns a 6-character hexadecimal string
|
|
156
168
|
*/
|
|
157
169
|
export function makeRequestId() {
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
package/dist/log.d.ts
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @ignore
|
|
7
|
+
*/
|
|
8
|
+
export declare function createLogger(side: "server" | "client", level: LogLevel): Logger;
|
|
9
|
+
export declare function createLogger(side: "server" | "client", level: LogLevel, rqid: string): RequestBoundLogger;
|
|
10
|
+
/**
|
|
11
|
+
* @ignore
|
|
12
|
+
*/
|
|
13
|
+
export type Logger = {
|
|
2
14
|
debug: (rqid: string | null, message: string, ...args: any[]) => void;
|
|
3
15
|
info: (rqid: string | null, message: string, ...args: any[]) => void;
|
|
4
16
|
warn: (rqid: string | null, message: string, ...args: any[]) => void;
|
|
5
17
|
error: (rqid: string | null, message: string, ...args: any[]) => void;
|
|
6
18
|
};
|
|
7
|
-
export type
|
|
8
|
-
|
|
19
|
+
export type RequestBoundLogger = {
|
|
20
|
+
debug: (message: string, ...args: any[]) => void;
|
|
21
|
+
info: (message: string, ...args: any[]) => void;
|
|
22
|
+
warn: (message: string, ...args: any[]) => void;
|
|
23
|
+
error: (message: string, ...args: any[]) => void;
|
|
24
|
+
};
|
|
25
|
+
/** @source */
|
|
26
|
+
export declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error"];
|
|
9
27
|
export type LogLevel = (typeof LOG_LEVELS)[number];
|
|
10
|
-
/**
|
|
11
|
-
* Send log messages to the console, with a helpful prefix.
|
|
12
|
-
* @param severity
|
|
13
|
-
* @param side
|
|
14
|
-
* @param rqid request ID
|
|
15
|
-
* @param message
|
|
16
|
-
* @param args passed to console methods directly
|
|
17
|
-
*/
|
|
18
|
-
export declare function log(severity: "debug" | "info" | "warn" | "error", side: "server" | "client", rqid: string | null, message: string, ...args: any[]): void;
|
|
19
|
-
export {};
|
|
20
28
|
//# sourceMappingURL=log.d.ts.map
|
package/dist/log.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAA;AAChF,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,GAAG,QAAQ,EACzB,KAAK,EAAE,QAAQ,EACf,IAAI,EAAE,MAAM,GACX,kBAAkB,CAAA;AAyBrB;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IACrE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IACpE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IACpE,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAChD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC/C,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC/C,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;CACjD,CAAA;AAED,cAAc;AACd,eAAO,MAAM,UAAU,6CAA8C,CAAA;AAErE,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA"}
|
package/dist/log.js
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
5
|
+
export function createLogger(side, level = "debug", rqid) {
|
|
6
|
+
const lvls = LOG_LEVELS.slice(LOG_LEVELS.indexOf(level));
|
|
7
|
+
if (rqid) {
|
|
8
|
+
return {
|
|
9
|
+
debug: lvls.includes("debug") ? logger("debug", side, rqid) : () => { },
|
|
10
|
+
info: lvls.includes("info") ? logger("info", side, rqid) : () => { },
|
|
11
|
+
warn: lvls.includes("warn") ? logger("warn", side, rqid) : () => { },
|
|
12
|
+
error: lvls.includes("error") ? logger("error", side, rqid) : () => { },
|
|
13
|
+
};
|
|
14
|
+
}
|
|
3
15
|
return {
|
|
4
|
-
debug:
|
|
5
|
-
info:
|
|
6
|
-
warn:
|
|
7
|
-
error:
|
|
16
|
+
debug: lvls.includes("debug") ? logger("debug", side) : () => { },
|
|
17
|
+
info: lvls.includes("info") ? logger("info", side) : () => { },
|
|
18
|
+
warn: lvls.includes("warn") ? logger("warn", side) : () => { },
|
|
19
|
+
error: lvls.includes("error") ? logger("error", side) : () => { },
|
|
8
20
|
};
|
|
9
21
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function logger(severity, side) {
|
|
18
|
-
return (rqid, message, ...args) => log(severity, side, rqid, message, ...args);
|
|
22
|
+
/** @source */
|
|
23
|
+
export const LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
24
|
+
function logger(severity, side, rqid) {
|
|
25
|
+
if (rqid === undefined) {
|
|
26
|
+
return (rqid, message, ...args) => log(severity, side, rqid, message, ...args);
|
|
27
|
+
}
|
|
28
|
+
return (message, ...args) => log(severity, side, rqid, message, ...args);
|
|
19
29
|
}
|
|
20
30
|
/**
|
|
21
31
|
* Send log messages to the console, with a helpful prefix.
|
|
@@ -25,7 +35,7 @@ function logger(severity, side) {
|
|
|
25
35
|
* @param message
|
|
26
36
|
* @param args passed to console methods directly
|
|
27
37
|
*/
|
|
28
|
-
|
|
38
|
+
function log(severity, side, rqid, message, ...args) {
|
|
29
39
|
const prefix = "[" +
|
|
30
40
|
["SWARPC", side, rqid ? `%c${rqid}%c` : ""].filter(Boolean).join(" ") +
|
|
31
41
|
"]";
|
package/dist/server.d.ts
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
1
5
|
import { type LogLevel } from "./log.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
6
|
+
import { ImplementationsMap, ProcedureImplementation, zImplementations, zProcedures, type ProceduresMap } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* The sw&rpc server instance, which provides methods to register {@link ProcedureImplementation | procedure implementations},
|
|
9
|
+
* and listens for incoming messages that call those procedures
|
|
10
|
+
*/
|
|
11
|
+
export type SwarpcServer<Procedures extends ProceduresMap> = {
|
|
12
|
+
[zProcedures]: Procedures;
|
|
13
|
+
[zImplementations]: ImplementationsMap<Procedures>;
|
|
14
|
+
start(self: Window | Worker): void;
|
|
15
|
+
} & {
|
|
16
|
+
[F in keyof Procedures]: (impl: ProcedureImplementation<Procedures[F]["input"], Procedures[F]["progress"], Procedures[F]["success"]>) => void;
|
|
17
|
+
};
|
|
4
18
|
/**
|
|
5
19
|
* Creates a sw&rpc server instance.
|
|
6
|
-
* @param procedures procedures the server will implement
|
|
20
|
+
* @param procedures procedures the server will implement, see {@link ProceduresMap}
|
|
7
21
|
* @param options various options
|
|
8
22
|
* @param options.worker if provided, the server will use this worker to post messages, instead of sending it to all clients
|
|
9
|
-
* @returns a SwarpcServer instance. Each property of the procedures map will be a method, that accepts a function implementing the procedure. There is also .start(), to be called after implementing all procedures.
|
|
23
|
+
* @returns a SwarpcServer instance. Each property of the procedures map will be a method, that accepts a function implementing the procedure (see {@link ProcedureImplementation}). There is also .start(), to be called after implementing all procedures.
|
|
24
|
+
*
|
|
25
|
+
* An example of defining a server:
|
|
26
|
+
* {@includeCode ../example/src/service-worker.ts}
|
|
10
27
|
*/
|
|
11
28
|
export declare function Server<Procedures extends ProceduresMap>(procedures: Procedures, { worker, loglevel }?: {
|
|
12
29
|
worker?: Worker;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EACL,kBAAkB,EAKlB,uBAAuB,EACvB,gBAAgB,EAChB,WAAW,EACX,KAAK,aAAa,EACnB,MAAM,YAAY,CAAA;AAGnB;;;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,MAAM,GAAG,IAAI,CAAA;CACnC,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;AAKD;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,UAAU,SAAS,aAAa,EACrD,UAAU,EAAE,UAAU,EACtB,EAAE,MAAM,EAAE,QAAkB,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,CAAA;CAAO,GAC5E,YAAY,CAAC,UAAU,CAAC,CA8J1B"}
|
package/dist/server.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
1
5
|
import { type } from "arktype";
|
|
2
6
|
import { createLogger } from "./log.js";
|
|
3
7
|
import { PayloadHeaderSchema, PayloadSchema, zImplementations, zProcedures, } from "./types.js";
|
|
@@ -6,10 +10,13 @@ const abortControllers = new Map();
|
|
|
6
10
|
const abortedRequests = new Set();
|
|
7
11
|
/**
|
|
8
12
|
* Creates a sw&rpc server instance.
|
|
9
|
-
* @param procedures procedures the server will implement
|
|
13
|
+
* @param procedures procedures the server will implement, see {@link ProceduresMap}
|
|
10
14
|
* @param options various options
|
|
11
15
|
* @param options.worker if provided, the server will use this worker to post messages, instead of sending it to all clients
|
|
12
|
-
* @returns a SwarpcServer instance. Each property of the procedures map will be a method, that accepts a function implementing the procedure. There is also .start(), to be called after implementing all procedures.
|
|
16
|
+
* @returns a SwarpcServer instance. Each property of the procedures map will be a method, that accepts a function implementing the procedure (see {@link ProcedureImplementation}). There is also .start(), to be called after implementing all procedures.
|
|
17
|
+
*
|
|
18
|
+
* An example of defining a server:
|
|
19
|
+
* {@includeCode ../example/src/service-worker.ts}
|
|
13
20
|
*/
|
|
14
21
|
export function Server(procedures, { worker, loglevel = "debug" } = {}) {
|
|
15
22
|
const l = createLogger("server", loglevel);
|
|
@@ -27,17 +34,15 @@ export function Server(procedures, { worker, loglevel = "debug" } = {}) {
|
|
|
27
34
|
if (!instance[zProcedures][functionName]) {
|
|
28
35
|
throw new Error(`No procedure found for function name: ${functionName}`);
|
|
29
36
|
}
|
|
30
|
-
instance[zImplementations][functionName] = (input, onProgress,
|
|
31
|
-
abortSignal?.throwIfAborted();
|
|
37
|
+
instance[zImplementations][functionName] = (input, onProgress, tools) => {
|
|
38
|
+
tools.abortSignal?.throwIfAborted();
|
|
32
39
|
return new Promise((resolve, reject) => {
|
|
33
|
-
abortSignal?.addEventListener("abort", () => {
|
|
34
|
-
let { requestId, reason } = abortSignal?.reason;
|
|
40
|
+
tools.abortSignal?.addEventListener("abort", () => {
|
|
41
|
+
let { requestId, reason } = tools.abortSignal?.reason;
|
|
35
42
|
l.debug(requestId, `Aborted ${functionName} request: ${reason}`);
|
|
36
43
|
reject({ aborted: reason });
|
|
37
44
|
});
|
|
38
|
-
implementation(input, onProgress,
|
|
39
|
-
.then(resolve)
|
|
40
|
-
.catch(reject);
|
|
45
|
+
implementation(input, onProgress, tools).then(resolve).catch(reject);
|
|
41
46
|
});
|
|
42
47
|
};
|
|
43
48
|
});
|
|
@@ -101,13 +106,21 @@ export function Server(procedures, { worker, loglevel = "debug" } = {}) {
|
|
|
101
106
|
await postError("No input provided");
|
|
102
107
|
return;
|
|
103
108
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
try {
|
|
110
|
+
// Call the implementation with the input and a progress callback
|
|
111
|
+
const result = await implementation(payload.input, async (progress) => {
|
|
112
|
+
l.debug(requestId, `Progress for ${functionName}`, progress);
|
|
113
|
+
await postMsg({ progress });
|
|
114
|
+
}, {
|
|
115
|
+
abortSignal: abortControllers.get(requestId)?.signal,
|
|
116
|
+
logger: createLogger("server", loglevel, requestId),
|
|
117
|
+
});
|
|
118
|
+
// Send results
|
|
119
|
+
l.debug(requestId, `Result for ${functionName}`, result);
|
|
120
|
+
await postMsg({ result });
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
109
123
|
// Send errors
|
|
110
|
-
.catch(async (error) => {
|
|
111
124
|
// Handle errors caused by abortions
|
|
112
125
|
if ("aborted" in error) {
|
|
113
126
|
l.debug(requestId, `Received abort error for ${functionName}`, error.aborted);
|
|
@@ -115,17 +128,12 @@ export function Server(procedures, { worker, loglevel = "debug" } = {}) {
|
|
|
115
128
|
abortControllers.delete(requestId);
|
|
116
129
|
return;
|
|
117
130
|
}
|
|
118
|
-
l.
|
|
131
|
+
l.info(requestId, `Error in ${functionName}`, error);
|
|
119
132
|
await postError(error);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.then(async (result) => {
|
|
123
|
-
l.debug(requestId, `Result for ${functionName}`, result);
|
|
124
|
-
await postMsg({ result });
|
|
125
|
-
})
|
|
126
|
-
.finally(() => {
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
127
135
|
abortedRequests.delete(requestId);
|
|
128
|
-
}
|
|
136
|
+
}
|
|
129
137
|
});
|
|
130
138
|
};
|
|
131
139
|
return instance;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
1
5
|
import { type Type } from "arktype";
|
|
6
|
+
import { RequestBoundLogger } from "./log.js";
|
|
2
7
|
/**
|
|
3
8
|
* A procedure declaration
|
|
4
9
|
*/
|
|
@@ -37,7 +42,7 @@ export type Procedure<I extends Type, P extends Type, S extends Type> = {
|
|
|
37
42
|
* const result = await request
|
|
38
43
|
* ```
|
|
39
44
|
*/
|
|
40
|
-
export type CancelablePromise<T> = {
|
|
45
|
+
export type CancelablePromise<T = unknown> = {
|
|
41
46
|
request: Promise<T>;
|
|
42
47
|
/**
|
|
43
48
|
* Abort the request.
|
|
@@ -48,9 +53,33 @@ export type CancelablePromise<T> = {
|
|
|
48
53
|
/**
|
|
49
54
|
* An implementation of a procedure
|
|
50
55
|
*/
|
|
51
|
-
export type ProcedureImplementation<I extends Type, P extends Type, S extends Type> = (
|
|
56
|
+
export type ProcedureImplementation<I extends Type, P extends Type, S extends Type> = (
|
|
57
|
+
/**
|
|
58
|
+
* Input data for the procedure
|
|
59
|
+
*/
|
|
60
|
+
input: I["inferOut"],
|
|
61
|
+
/**
|
|
62
|
+
* Callback to call with progress updates.
|
|
63
|
+
*/
|
|
64
|
+
onProgress: (progress: P["inferIn"]) => void,
|
|
65
|
+
/**
|
|
66
|
+
* Additional tools useful when implementing the procedure.
|
|
67
|
+
*/
|
|
68
|
+
tools: {
|
|
69
|
+
/**
|
|
70
|
+
* AbortSignal that can be used to handle request cancellation -- see [Make cancellable requests](https://gwennlbh.github.io/swarpc/docs/#make-cancelable-requests)
|
|
71
|
+
*/
|
|
72
|
+
abortSignal?: AbortSignal;
|
|
73
|
+
/**
|
|
74
|
+
* Logger instance to use for logging messages related to this procedure call, using the same format as SWARPC's built-in logging.
|
|
75
|
+
*/
|
|
76
|
+
logger: RequestBoundLogger;
|
|
77
|
+
}) => Promise<S["inferIn"]>;
|
|
52
78
|
/**
|
|
53
79
|
* Declarations of procedures by name.
|
|
80
|
+
*
|
|
81
|
+
* An example of declaring procedures:
|
|
82
|
+
* {@includeCode ../example/src/lib/procedures.ts}
|
|
54
83
|
*/
|
|
55
84
|
export type ProceduresMap = Record<string, Procedure<Type, Type, Type>>;
|
|
56
85
|
/**
|
|
@@ -76,6 +105,9 @@ export type Hooks<Procedures extends ProceduresMap> = {
|
|
|
76
105
|
*/
|
|
77
106
|
progress?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, data: Procedures[Procedure]["progress"]["inferOut"]) => void;
|
|
78
107
|
};
|
|
108
|
+
/**
|
|
109
|
+
* @source
|
|
110
|
+
*/
|
|
79
111
|
export declare const PayloadHeaderSchema: import("arktype").Generic<[["Name", string]], {
|
|
80
112
|
readonly by: "\"sw&rpc\"";
|
|
81
113
|
readonly functionName: "Name";
|
|
@@ -86,6 +118,9 @@ export type PayloadHeader<PM extends ProceduresMap, Name extends keyof PM = keyo
|
|
|
86
118
|
functionName: Name & string;
|
|
87
119
|
requestId: string;
|
|
88
120
|
};
|
|
121
|
+
/**
|
|
122
|
+
* @source
|
|
123
|
+
*/
|
|
89
124
|
export declare const PayloadCoreSchema: import("arktype").Generic<[["I", unknown], ["P", unknown], ["S", unknown]], {
|
|
90
125
|
readonly "input?": "I";
|
|
91
126
|
readonly "progress?": "P";
|
|
@@ -112,6 +147,9 @@ export type PayloadCore<PM extends ProceduresMap, Name extends keyof PM = keyof
|
|
|
112
147
|
message: string;
|
|
113
148
|
};
|
|
114
149
|
};
|
|
150
|
+
/**
|
|
151
|
+
* @source
|
|
152
|
+
*/
|
|
115
153
|
export declare const PayloadSchema: import("arktype").Generic<[["Name", string], ["I", unknown], ["P", unknown], ["S", unknown]], readonly ["PayloadHeaderSchema<Name>", "&", "PayloadCoreSchema<I, P, S>"], {
|
|
116
154
|
PayloadCoreSchema: import("arktype/internal/scope.ts").bindGenericToScope<import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
|
|
117
155
|
readonly "input?": "I";
|
|
@@ -230,31 +268,14 @@ export type ClientMethod<P extends Procedure<Type, Type, Type>> = ((input: P["in
|
|
|
230
268
|
};
|
|
231
269
|
/**
|
|
232
270
|
* Symbol used as the key for the procedures map on the server instance
|
|
271
|
+
* @internal
|
|
272
|
+
* @source
|
|
233
273
|
*/
|
|
234
274
|
export declare const zImplementations: unique symbol;
|
|
235
275
|
/**
|
|
236
276
|
* Symbol used as the key for the procedures map on instances
|
|
277
|
+
* @internal
|
|
278
|
+
* @source
|
|
237
279
|
*/
|
|
238
280
|
export declare const zProcedures: unique symbol;
|
|
239
|
-
/**
|
|
240
|
-
* The sw&rpc client instance, which provides methods to call procedures.
|
|
241
|
-
* Each property of the procedures map will be a method, that accepts an input, an optional onProgress callback and an optional request ID.
|
|
242
|
-
* If you want to be able to cancel the request, you can set the request's ID yourself, and call `.abort(requestId, reason)` on the client instance to cancel it.
|
|
243
|
-
*/
|
|
244
|
-
export type SwarpcClient<Procedures extends ProceduresMap> = {
|
|
245
|
-
[zProcedures]: Procedures;
|
|
246
|
-
} & {
|
|
247
|
-
[F in keyof Procedures]: ClientMethod<Procedures[F]>;
|
|
248
|
-
};
|
|
249
|
-
/**
|
|
250
|
-
* The sw&rpc server instance, which provides methods to register procedure implementations,
|
|
251
|
-
* and listens for incoming messages that call those procedures
|
|
252
|
-
*/
|
|
253
|
-
export type SwarpcServer<Procedures extends ProceduresMap> = {
|
|
254
|
-
[zProcedures]: Procedures;
|
|
255
|
-
[zImplementations]: ImplementationsMap<Procedures>;
|
|
256
|
-
start(self: Window | Worker): void;
|
|
257
|
-
} & {
|
|
258
|
-
[F in keyof Procedures]: (impl: ProcedureImplementation<Procedures[F]["input"], Procedures[F]["progress"], Procedures[F]["success"]>) => void;
|
|
259
|
-
};
|
|
260
281
|
//# sourceMappingURL=types.d.ts.map
|
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,EAAQ,KAAK,IAAI,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
1
5
|
import { type } from "arktype";
|
|
6
|
+
/**
|
|
7
|
+
* @source
|
|
8
|
+
*/
|
|
2
9
|
export const PayloadHeaderSchema = type("<Name extends string>", {
|
|
3
10
|
by: '"sw&rpc"',
|
|
4
11
|
functionName: "Name",
|
|
5
12
|
requestId: "string >= 1",
|
|
6
13
|
});
|
|
14
|
+
/**
|
|
15
|
+
* @source
|
|
16
|
+
*/
|
|
7
17
|
export const PayloadCoreSchema = type("<I, P, S>", {
|
|
8
18
|
"input?": "I",
|
|
9
19
|
"progress?": "P",
|
|
@@ -11,6 +21,9 @@ export const PayloadCoreSchema = type("<I, P, S>", {
|
|
|
11
21
|
"abort?": { reason: "string" },
|
|
12
22
|
"error?": { message: "string" },
|
|
13
23
|
});
|
|
24
|
+
/**
|
|
25
|
+
* @source
|
|
26
|
+
*/
|
|
14
27
|
export const PayloadSchema = type
|
|
15
28
|
.scope({ PayloadCoreSchema, PayloadHeaderSchema })
|
|
16
29
|
.type("<Name extends string, I, P, S>", [
|
|
@@ -20,9 +33,13 @@ export const PayloadSchema = type
|
|
|
20
33
|
]);
|
|
21
34
|
/**
|
|
22
35
|
* Symbol used as the key for the procedures map on the server instance
|
|
36
|
+
* @internal
|
|
37
|
+
* @source
|
|
23
38
|
*/
|
|
24
39
|
export const zImplementations = Symbol("SWARPC implementations");
|
|
25
40
|
/**
|
|
26
41
|
* Symbol used as the key for the procedures map on instances
|
|
42
|
+
* @internal
|
|
43
|
+
* @source
|
|
27
44
|
*/
|
|
28
45
|
export const zProcedures = Symbol("SWARPC procedures");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swarpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
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",
|
|
@@ -31,19 +31,36 @@
|
|
|
31
31
|
"dev": "tsc --watch",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
33
|
"test": "vitest",
|
|
34
|
-
"typedoc": "typedoc
|
|
35
|
-
"version": "kacl release && prettier -w CHANGELOG.md && git add CHANGELOG.md"
|
|
34
|
+
"typedoc": "typedoc",
|
|
35
|
+
"version": "kacl release && prettier -w CHANGELOG.md && git add CHANGELOG.md",
|
|
36
|
+
"typedoc:dev": "nodemon --watch src --watch README.md --watch typedoc.json --watch typedoc.css --exec npm run typedoc:withplugins",
|
|
37
|
+
"typedoc:serve": "npx sirv-cli --dev docs",
|
|
38
|
+
"typedoc:plugins": "node -e \"const fs=require('fs');const p=JSON.parse(fs.readFileSync('typedoc.json')).plugin||[];if(p.length)require('child_process').execSync('npm add --legacy-peer-deps -D '+p.join(' '),{stdio:'inherit'});else console.log('No plugins found');\"",
|
|
39
|
+
"typedoc:withplugins": "npm run typedoc:plugins && npm run typedoc"
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
38
42
|
"arktype": "^2.1.20"
|
|
39
43
|
},
|
|
40
44
|
"devDependencies": {
|
|
45
|
+
"@8hobbies/typedoc-plugin-plausible": "^2.2.0",
|
|
41
46
|
"@vitest/web-worker": "^3.2.4",
|
|
42
47
|
"kacl": "^1.1.1",
|
|
48
|
+
"nodemon": "^3.1.10",
|
|
43
49
|
"prettier": "^3.6.2",
|
|
50
|
+
"sirv-cli": "^3.0.1",
|
|
44
51
|
"typedoc": "^0.28.9",
|
|
52
|
+
"typedoc-material-theme": "^1.4.0",
|
|
53
|
+
"typedoc-plugin-dt-links": "^2.0.12",
|
|
54
|
+
"typedoc-plugin-extras": "^4.0.1",
|
|
55
|
+
"typedoc-plugin-inline-sources": "^1.3.0",
|
|
56
|
+
"typedoc-plugin-mdn-links": "^5.0.6",
|
|
57
|
+
"typedoc-plugin-redirect": "^1.2.0",
|
|
45
58
|
"typescript": "^5.9.2",
|
|
46
59
|
"vite": "^7.0.6",
|
|
47
60
|
"vitest": "^3.2.4"
|
|
61
|
+
},
|
|
62
|
+
"volta": {
|
|
63
|
+
"node": "22.18.0",
|
|
64
|
+
"npm": "11.5.2"
|
|
48
65
|
}
|
|
49
66
|
}
|
package/src/client.ts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
import { createLogger, type Logger, type LogLevel } from "./log.js"
|
|
2
7
|
import {
|
|
8
|
+
ClientMethod,
|
|
3
9
|
Hooks,
|
|
4
10
|
Payload,
|
|
5
11
|
PayloadCore,
|
|
6
12
|
zProcedures,
|
|
7
13
|
type ProceduresMap,
|
|
8
|
-
type SwarpcClient
|
|
9
14
|
} from "./types.js"
|
|
10
15
|
import { findTransferables } from "./utils.js"
|
|
11
16
|
|
|
12
|
-
|
|
17
|
+
/**
|
|
18
|
+
* The sw&rpc client instance, which provides {@link ClientMethod | methods to call procedures}.
|
|
19
|
+
* Each property of the procedures map will be a method, that accepts an input, an optional onProgress callback and an optional request ID.
|
|
20
|
+
* If you want to be able to cancel the request, you can set the request's ID yourself, and call `.abort(requestId, reason)` on the client instance to cancel it.
|
|
21
|
+
*/
|
|
22
|
+
export type SwarpcClient<Procedures extends ProceduresMap> = {
|
|
23
|
+
[zProcedures]: Procedures
|
|
24
|
+
} & {
|
|
25
|
+
[F in keyof Procedures]: ClientMethod<Procedures[F]>
|
|
26
|
+
}
|
|
13
27
|
|
|
14
28
|
/**
|
|
15
29
|
* Pending requests are stored in a map, where the key is the request ID.
|
|
@@ -29,22 +43,34 @@ let _clientListenerStarted = false
|
|
|
29
43
|
|
|
30
44
|
/**
|
|
31
45
|
*
|
|
32
|
-
* @param procedures procedures the client will be able to call
|
|
46
|
+
* @param procedures procedures the client will be able to call, see {@link ProceduresMap}
|
|
33
47
|
* @param options various options
|
|
34
48
|
* @param options.worker if provided, the client will use this worker to post messages.
|
|
35
49
|
* @param options.hooks hooks to run on messages received from the server
|
|
36
|
-
* @
|
|
50
|
+
* @param options.restartListener if true, will force the listener to restart even if it has already been started
|
|
51
|
+
* @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, see {@link ClientMethod}
|
|
52
|
+
*
|
|
53
|
+
* An example of defining and using a client:
|
|
54
|
+
* {@includeCode ../example/src/routes/+page.svelte}
|
|
37
55
|
*/
|
|
38
56
|
export function Client<Procedures extends ProceduresMap>(
|
|
39
57
|
procedures: Procedures,
|
|
40
58
|
{
|
|
41
59
|
worker,
|
|
42
60
|
loglevel = "debug",
|
|
61
|
+
restartListener = false,
|
|
43
62
|
hooks = {},
|
|
44
|
-
}: {
|
|
63
|
+
}: {
|
|
64
|
+
worker?: Worker
|
|
65
|
+
hooks?: Hooks<Procedures>
|
|
66
|
+
loglevel?: LogLevel
|
|
67
|
+
restartListener?: boolean
|
|
68
|
+
} = {}
|
|
45
69
|
): SwarpcClient<Procedures> {
|
|
46
70
|
const l = createLogger("client", loglevel)
|
|
47
71
|
|
|
72
|
+
if (restartListener) _clientListenerStarted = false
|
|
73
|
+
|
|
48
74
|
// Store procedures on a symbol key, to avoid conflicts with procedure names
|
|
49
75
|
const instance = { [zProcedures]: procedures } as Partial<
|
|
50
76
|
SwarpcClient<Procedures>
|
|
@@ -169,9 +195,10 @@ async function postMessage<Procedures extends ProceduresMap>(
|
|
|
169
195
|
/**
|
|
170
196
|
* Starts the client listener, which listens for messages from the sw&rpc server.
|
|
171
197
|
* @param worker if provided, the client will use this worker to listen for messages, instead of using the service worker
|
|
198
|
+
* @param force if true, will force the listener to restart even if it has already been started
|
|
172
199
|
* @returns
|
|
173
200
|
*/
|
|
174
|
-
async function startClientListener<Procedures extends ProceduresMap>(
|
|
201
|
+
export async function startClientListener<Procedures extends ProceduresMap>(
|
|
175
202
|
l: Logger,
|
|
176
203
|
worker?: Worker,
|
|
177
204
|
hooks: Hooks<Procedures> = {}
|
|
@@ -193,7 +220,7 @@ async function startClientListener<Procedures extends ProceduresMap>(
|
|
|
193
220
|
const w = worker ?? navigator.serviceWorker
|
|
194
221
|
|
|
195
222
|
// Start listening for messages
|
|
196
|
-
l.debug(
|
|
223
|
+
l.debug(null, "Starting client listener", { worker, w, hooks })
|
|
197
224
|
w.addEventListener("message", (event) => {
|
|
198
225
|
// Get the data from the event
|
|
199
226
|
const eventData = (event as MessageEvent).data || {}
|
|
@@ -213,7 +240,7 @@ async function startClientListener<Procedures extends ProceduresMap>(
|
|
|
213
240
|
const handlers = pendingRequests.get(requestId)
|
|
214
241
|
if (!handlers) {
|
|
215
242
|
throw new Error(
|
|
216
|
-
`[SWARPC Client] ${requestId} has no active request handlers
|
|
243
|
+
`[SWARPC Client] ${requestId} has no active request handlers, cannot process ${JSON.stringify(data)}`,
|
|
217
244
|
)
|
|
218
245
|
}
|
|
219
246
|
|
|
@@ -238,6 +265,7 @@ async function startClientListener<Procedures extends ProceduresMap>(
|
|
|
238
265
|
|
|
239
266
|
/**
|
|
240
267
|
* Generate a random request ID, used to identify requests between client and server.
|
|
268
|
+
* @source
|
|
241
269
|
* @returns a 6-character hexadecimal string
|
|
242
270
|
*/
|
|
243
271
|
export function makeRequestId(): string {
|
package/src/index.ts
CHANGED
package/src/log.ts
CHANGED
|
@@ -1,30 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @ignore
|
|
8
|
+
*/
|
|
9
|
+
export function createLogger(side: "server" | "client", level: LogLevel): Logger
|
|
1
10
|
export function createLogger(
|
|
2
11
|
side: "server" | "client",
|
|
3
|
-
level: LogLevel
|
|
12
|
+
level: LogLevel,
|
|
13
|
+
rqid: string
|
|
14
|
+
): RequestBoundLogger
|
|
15
|
+
export function createLogger(
|
|
16
|
+
side: "server" | "client",
|
|
17
|
+
level: LogLevel = "debug",
|
|
18
|
+
rqid?: string
|
|
4
19
|
) {
|
|
5
|
-
const
|
|
20
|
+
const lvls = LOG_LEVELS.slice(LOG_LEVELS.indexOf(level))
|
|
21
|
+
|
|
22
|
+
if (rqid) {
|
|
23
|
+
return {
|
|
24
|
+
debug: lvls.includes("debug") ? logger("debug", side, rqid) : () => {},
|
|
25
|
+
info: lvls.includes("info") ? logger("info", side, rqid) : () => {},
|
|
26
|
+
warn: lvls.includes("warn") ? logger("warn", side, rqid) : () => {},
|
|
27
|
+
error: lvls.includes("error") ? logger("error", side, rqid) : () => {},
|
|
28
|
+
} as RequestBoundLogger
|
|
29
|
+
}
|
|
6
30
|
|
|
7
31
|
return {
|
|
8
|
-
debug:
|
|
9
|
-
info:
|
|
10
|
-
warn:
|
|
11
|
-
error:
|
|
32
|
+
debug: lvls.includes("debug") ? logger("debug", side) : () => {},
|
|
33
|
+
info: lvls.includes("info") ? logger("info", side) : () => {},
|
|
34
|
+
warn: lvls.includes("warn") ? logger("warn", side) : () => {},
|
|
35
|
+
error: lvls.includes("error") ? logger("error", side) : () => {},
|
|
12
36
|
}
|
|
13
37
|
}
|
|
14
38
|
|
|
15
|
-
|
|
39
|
+
/**
|
|
40
|
+
* @ignore
|
|
41
|
+
*/
|
|
42
|
+
export type Logger = {
|
|
43
|
+
debug: (rqid: string | null, message: string, ...args: any[]) => void
|
|
44
|
+
info: (rqid: string | null, message: string, ...args: any[]) => void
|
|
45
|
+
warn: (rqid: string | null, message: string, ...args: any[]) => void
|
|
46
|
+
error: (rqid: string | null, message: string, ...args: any[]) => void
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type RequestBoundLogger = {
|
|
50
|
+
debug: (message: string, ...args: any[]) => void
|
|
51
|
+
info: (message: string, ...args: any[]) => void
|
|
52
|
+
warn: (message: string, ...args: any[]) => void
|
|
53
|
+
error: (message: string, ...args: any[]) => void
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @source */
|
|
57
|
+
export const LOG_LEVELS = ["debug", "info", "warn", "error"] as const
|
|
16
58
|
|
|
17
|
-
const LOG_LEVELS = ["debug", "info", "warn", "error"] as const
|
|
18
59
|
export type LogLevel = (typeof LOG_LEVELS)[number]
|
|
19
60
|
|
|
20
61
|
/**
|
|
21
|
-
* Creates partially-applied logging functions given the first 2 args
|
|
62
|
+
* Creates partially-applied logging functions given the first 2 or 3 args
|
|
22
63
|
* @param severity
|
|
23
64
|
* @param side
|
|
65
|
+
* @param rqid request ID, or null to not bind it
|
|
24
66
|
* @returns
|
|
25
67
|
*/
|
|
26
|
-
function logger(
|
|
27
|
-
|
|
68
|
+
function logger(
|
|
69
|
+
severity: LogLevel,
|
|
70
|
+
side: "server" | "client",
|
|
71
|
+
rqid: string
|
|
72
|
+
): (message: string, ...args: any[]) => void
|
|
73
|
+
function logger(
|
|
74
|
+
severity: LogLevel,
|
|
75
|
+
side: "server" | "client"
|
|
76
|
+
): (rqid: string | null, message: string, ...args: any[]) => void
|
|
77
|
+
function logger(severity: LogLevel, side: "server" | "client", rqid?: string) {
|
|
78
|
+
if (rqid === undefined) {
|
|
79
|
+
return (rqid: string | null, message: string, ...args: any[]) =>
|
|
80
|
+
log(severity, side, rqid, message, ...args)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (message: string, ...args: any[]) =>
|
|
28
84
|
log(severity, side, rqid, message, ...args)
|
|
29
85
|
}
|
|
30
86
|
|
|
@@ -36,7 +92,7 @@ function logger(severity: LogLevel, side: "server" | "client") {
|
|
|
36
92
|
* @param message
|
|
37
93
|
* @param args passed to console methods directly
|
|
38
94
|
*/
|
|
39
|
-
|
|
95
|
+
function log(
|
|
40
96
|
severity: "debug" | "info" | "warn" | "error",
|
|
41
97
|
side: "server" | "client",
|
|
42
98
|
rqid: string | null,
|
package/src/server.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
import { type } from "arktype"
|
|
2
7
|
import { createLogger, type LogLevel } from "./log.js"
|
|
3
8
|
import {
|
|
@@ -6,24 +11,43 @@ import {
|
|
|
6
11
|
PayloadCore,
|
|
7
12
|
PayloadHeaderSchema,
|
|
8
13
|
PayloadSchema,
|
|
14
|
+
ProcedureImplementation,
|
|
9
15
|
zImplementations,
|
|
10
16
|
zProcedures,
|
|
11
17
|
type ProceduresMap,
|
|
12
|
-
type SwarpcServer,
|
|
13
18
|
} from "./types.js"
|
|
14
19
|
import { findTransferables } from "./utils.js"
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
/**
|
|
22
|
+
* The sw&rpc server instance, which provides methods to register {@link ProcedureImplementation | procedure implementations},
|
|
23
|
+
* and listens for incoming messages that call those procedures
|
|
24
|
+
*/
|
|
25
|
+
export type SwarpcServer<Procedures extends ProceduresMap> = {
|
|
26
|
+
[zProcedures]: Procedures
|
|
27
|
+
[zImplementations]: ImplementationsMap<Procedures>
|
|
28
|
+
start(self: Window | Worker): void
|
|
29
|
+
} & {
|
|
30
|
+
[F in keyof Procedures]: (
|
|
31
|
+
impl: ProcedureImplementation<
|
|
32
|
+
Procedures[F]["input"],
|
|
33
|
+
Procedures[F]["progress"],
|
|
34
|
+
Procedures[F]["success"]
|
|
35
|
+
>
|
|
36
|
+
) => void
|
|
37
|
+
}
|
|
17
38
|
|
|
18
39
|
const abortControllers = new Map<string, AbortController>()
|
|
19
40
|
const abortedRequests = new Set<string>()
|
|
20
41
|
|
|
21
42
|
/**
|
|
22
43
|
* Creates a sw&rpc server instance.
|
|
23
|
-
* @param procedures procedures the server will implement
|
|
44
|
+
* @param procedures procedures the server will implement, see {@link ProceduresMap}
|
|
24
45
|
* @param options various options
|
|
25
46
|
* @param options.worker if provided, the server will use this worker to post messages, instead of sending it to all clients
|
|
26
|
-
* @returns a SwarpcServer instance. Each property of the procedures map will be a method, that accepts a function implementing the procedure. There is also .start(), to be called after implementing all procedures.
|
|
47
|
+
* @returns a SwarpcServer instance. Each property of the procedures map will be a method, that accepts a function implementing the procedure (see {@link ProcedureImplementation}). There is also .start(), to be called after implementing all procedures.
|
|
48
|
+
*
|
|
49
|
+
* An example of defining a server:
|
|
50
|
+
* {@includeCode ../example/src/service-worker.ts}
|
|
27
51
|
*/
|
|
28
52
|
export function Server<Procedures extends ProceduresMap>(
|
|
29
53
|
procedures: Procedures,
|
|
@@ -46,22 +70,16 @@ export function Server<Procedures extends ProceduresMap>(
|
|
|
46
70
|
if (!instance[zProcedures][functionName]) {
|
|
47
71
|
throw new Error(`No procedure found for function name: ${functionName}`)
|
|
48
72
|
}
|
|
49
|
-
instance[zImplementations][functionName] = (
|
|
50
|
-
|
|
51
|
-
onProgress,
|
|
52
|
-
abortSignal
|
|
53
|
-
) => {
|
|
54
|
-
abortSignal?.throwIfAborted()
|
|
73
|
+
instance[zImplementations][functionName] = (input, onProgress, tools) => {
|
|
74
|
+
tools.abortSignal?.throwIfAborted()
|
|
55
75
|
return new Promise((resolve, reject) => {
|
|
56
|
-
abortSignal?.addEventListener("abort", () => {
|
|
57
|
-
let { requestId, reason } = abortSignal?.reason
|
|
76
|
+
tools.abortSignal?.addEventListener("abort", () => {
|
|
77
|
+
let { requestId, reason } = tools.abortSignal?.reason
|
|
58
78
|
l.debug(requestId, `Aborted ${functionName} request: ${reason}`)
|
|
59
79
|
reject({ aborted: reason })
|
|
60
80
|
})
|
|
61
81
|
|
|
62
|
-
implementation(input, onProgress,
|
|
63
|
-
.then(resolve)
|
|
64
|
-
.catch(reject)
|
|
82
|
+
implementation(input, onProgress, tools).then(resolve).catch(reject)
|
|
65
83
|
})
|
|
66
84
|
}
|
|
67
85
|
}) as SwarpcServer<Procedures>[typeof functionName]
|
|
@@ -152,40 +170,42 @@ export function Server<Procedures extends ProceduresMap>(
|
|
|
152
170
|
return
|
|
153
171
|
}
|
|
154
172
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// Handle errors caused by abortions
|
|
167
|
-
if ("aborted" in error) {
|
|
168
|
-
l.debug(
|
|
169
|
-
requestId,
|
|
170
|
-
`Received abort error for ${functionName}`,
|
|
171
|
-
error.aborted
|
|
172
|
-
)
|
|
173
|
-
abortedRequests.add(requestId)
|
|
174
|
-
abortControllers.delete(requestId)
|
|
175
|
-
return
|
|
173
|
+
try {
|
|
174
|
+
// Call the implementation with the input and a progress callback
|
|
175
|
+
const result = await implementation(
|
|
176
|
+
payload.input,
|
|
177
|
+
async (progress: any) => {
|
|
178
|
+
l.debug(requestId, `Progress for ${functionName}`, progress)
|
|
179
|
+
await postMsg({ progress })
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
abortSignal: abortControllers.get(requestId)?.signal,
|
|
183
|
+
logger: createLogger("server", loglevel, requestId),
|
|
176
184
|
}
|
|
185
|
+
)
|
|
177
186
|
|
|
178
|
-
l.error(requestId, `Error in ${functionName}`, error)
|
|
179
|
-
await postError(error)
|
|
180
|
-
})
|
|
181
187
|
// Send results
|
|
182
|
-
.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
188
|
+
l.debug(requestId, `Result for ${functionName}`, result)
|
|
189
|
+
await postMsg({ result })
|
|
190
|
+
} catch (error: any) {
|
|
191
|
+
// Send errors
|
|
192
|
+
// Handle errors caused by abortions
|
|
193
|
+
if ("aborted" in error) {
|
|
194
|
+
l.debug(
|
|
195
|
+
requestId,
|
|
196
|
+
`Received abort error for ${functionName}`,
|
|
197
|
+
error.aborted
|
|
198
|
+
)
|
|
199
|
+
abortedRequests.add(requestId)
|
|
200
|
+
abortControllers.delete(requestId)
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
l.info(requestId, `Error in ${functionName}`, error)
|
|
205
|
+
await postError(error)
|
|
206
|
+
} finally {
|
|
207
|
+
abortedRequests.delete(requestId)
|
|
208
|
+
}
|
|
189
209
|
})
|
|
190
210
|
}
|
|
191
211
|
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* @mergeModuleWith <project>
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
import { type, type Type } from "arktype"
|
|
7
|
+
import { Logger, RequestBoundLogger } from "./log.js"
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* A procedure declaration
|
|
@@ -39,7 +45,7 @@ export type Procedure<I extends Type, P extends Type, S extends Type> = {
|
|
|
39
45
|
* const result = await request
|
|
40
46
|
* ```
|
|
41
47
|
*/
|
|
42
|
-
export type CancelablePromise<T> = {
|
|
48
|
+
export type CancelablePromise<T = unknown> = {
|
|
43
49
|
request: Promise<T>
|
|
44
50
|
/**
|
|
45
51
|
* Abort the request.
|
|
@@ -56,13 +62,34 @@ export type ProcedureImplementation<
|
|
|
56
62
|
P extends Type,
|
|
57
63
|
S extends Type,
|
|
58
64
|
> = (
|
|
65
|
+
/**
|
|
66
|
+
* Input data for the procedure
|
|
67
|
+
*/
|
|
59
68
|
input: I["inferOut"],
|
|
69
|
+
/**
|
|
70
|
+
* Callback to call with progress updates.
|
|
71
|
+
*/
|
|
60
72
|
onProgress: (progress: P["inferIn"]) => void,
|
|
61
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Additional tools useful when implementing the procedure.
|
|
75
|
+
*/
|
|
76
|
+
tools: {
|
|
77
|
+
/**
|
|
78
|
+
* AbortSignal that can be used to handle request cancellation -- see [Make cancellable requests](https://gwennlbh.github.io/swarpc/docs/#make-cancelable-requests)
|
|
79
|
+
*/
|
|
80
|
+
abortSignal?: AbortSignal
|
|
81
|
+
/**
|
|
82
|
+
* Logger instance to use for logging messages related to this procedure call, using the same format as SWARPC's built-in logging.
|
|
83
|
+
*/
|
|
84
|
+
logger: RequestBoundLogger
|
|
85
|
+
}
|
|
62
86
|
) => Promise<S["inferIn"]>
|
|
63
87
|
|
|
64
88
|
/**
|
|
65
89
|
* Declarations of procedures by name.
|
|
90
|
+
*
|
|
91
|
+
* An example of declaring procedures:
|
|
92
|
+
* {@includeCode ../example/src/lib/procedures.ts}
|
|
66
93
|
*/
|
|
67
94
|
export type ProceduresMap = Record<string, Procedure<Type, Type, Type>>
|
|
68
95
|
|
|
@@ -104,6 +131,9 @@ export type Hooks<Procedures extends ProceduresMap> = {
|
|
|
104
131
|
) => void
|
|
105
132
|
}
|
|
106
133
|
|
|
134
|
+
/**
|
|
135
|
+
* @source
|
|
136
|
+
*/
|
|
107
137
|
export const PayloadHeaderSchema = type("<Name extends string>", {
|
|
108
138
|
by: '"sw&rpc"',
|
|
109
139
|
functionName: "Name",
|
|
@@ -119,6 +149,9 @@ export type PayloadHeader<
|
|
|
119
149
|
requestId: string
|
|
120
150
|
}
|
|
121
151
|
|
|
152
|
+
/**
|
|
153
|
+
* @source
|
|
154
|
+
*/
|
|
122
155
|
export const PayloadCoreSchema = type("<I, P, S>", {
|
|
123
156
|
"input?": "I",
|
|
124
157
|
"progress?": "P",
|
|
@@ -147,6 +180,9 @@ export type PayloadCore<
|
|
|
147
180
|
error: { message: string }
|
|
148
181
|
}
|
|
149
182
|
|
|
183
|
+
/**
|
|
184
|
+
* @source
|
|
185
|
+
*/
|
|
150
186
|
export const PayloadSchema = type
|
|
151
187
|
.scope({ PayloadCoreSchema, PayloadHeaderSchema })
|
|
152
188
|
.type("<Name extends string, I, P, S>", [
|
|
@@ -182,38 +218,14 @@ export type ClientMethod<P extends Procedure<Type, Type, Type>> = ((
|
|
|
182
218
|
|
|
183
219
|
/**
|
|
184
220
|
* Symbol used as the key for the procedures map on the server instance
|
|
221
|
+
* @internal
|
|
222
|
+
* @source
|
|
185
223
|
*/
|
|
186
224
|
export const zImplementations = Symbol("SWARPC implementations")
|
|
225
|
+
|
|
187
226
|
/**
|
|
188
227
|
* Symbol used as the key for the procedures map on instances
|
|
228
|
+
* @internal
|
|
229
|
+
* @source
|
|
189
230
|
*/
|
|
190
231
|
export const zProcedures = Symbol("SWARPC procedures")
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* The sw&rpc client instance, which provides methods to call procedures.
|
|
194
|
-
* Each property of the procedures map will be a method, that accepts an input, an optional onProgress callback and an optional request ID.
|
|
195
|
-
* If you want to be able to cancel the request, you can set the request's ID yourself, and call `.abort(requestId, reason)` on the client instance to cancel it.
|
|
196
|
-
*/
|
|
197
|
-
export type SwarpcClient<Procedures extends ProceduresMap> = {
|
|
198
|
-
[zProcedures]: Procedures
|
|
199
|
-
} & {
|
|
200
|
-
[F in keyof Procedures]: ClientMethod<Procedures[F]>
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* The sw&rpc server instance, which provides methods to register procedure implementations,
|
|
205
|
-
* and listens for incoming messages that call those procedures
|
|
206
|
-
*/
|
|
207
|
-
export type SwarpcServer<Procedures extends ProceduresMap> = {
|
|
208
|
-
[zProcedures]: Procedures
|
|
209
|
-
[zImplementations]: ImplementationsMap<Procedures>
|
|
210
|
-
start(self: Window | Worker): void
|
|
211
|
-
} & {
|
|
212
|
-
[F in keyof Procedures]: (
|
|
213
|
-
impl: ProcedureImplementation<
|
|
214
|
-
Procedures[F]["input"],
|
|
215
|
-
Procedures[F]["progress"],
|
|
216
|
-
Procedures[F]["success"]
|
|
217
|
-
>
|
|
218
|
-
) => void
|
|
219
|
-
}
|