swarpc 0.15.0 → 0.16.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/README.md +30 -4
- package/dist/client.d.ts +2 -49
- package/dist/client.js +3 -75
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -6
- package/dist/localstorage.d.ts +1 -14
- package/dist/localstorage.js +0 -1
- package/dist/log.d.ts +0 -29
- package/dist/log.js +2 -18
- package/dist/nodes.d.ts +0 -14
- package/dist/nodes.js +1 -9
- package/dist/polyfills.d.ts +0 -1
- package/dist/polyfills.js +0 -10
- package/dist/scopes.d.ts +1 -4
- package/dist/server.d.ts +1 -2
- package/dist/server.js +9 -45
- package/dist/standardschema.d.ts +0 -1
- package/dist/types.d.ts +0 -67
- package/dist/types.js +63 -47
- package/dist/utils.d.ts +1 -2
- package/dist/utils.js +0 -1
- package/package.json +13 -7
- package/dist/client.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/localstorage.d.ts.map +0 -1
- package/dist/log.d.ts.map +0 -1
- package/dist/nodes.d.ts.map +0 -1
- package/dist/polyfills.d.ts.map +0 -1
- package/dist/scopes.d.ts.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/standardschema.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/src/client.ts +0 -448
- package/src/index.ts +0 -9
- package/src/localstorage.ts +0 -46
- package/src/log.ts +0 -155
- package/src/nodes.ts +0 -63
- package/src/polyfills.ts +0 -22
- package/src/scopes.ts +0 -35
- package/src/server.ts +0 -270
- package/src/standardschema.ts +0 -70
- package/src/types.ts +0 -300
- package/src/utils.ts +0 -34
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<img src="./logo.svg" alt="sw&rpc" />
|
|
4
4
|
</h1>
|
|
5
5
|
|
|
6
|
-
RPC for Service Workers -- move that heavy computation off of your UI thread!
|
|
6
|
+
RPC for Service (and other types of) Workers -- move that heavy computation off of your UI thread!
|
|
7
7
|
|
|
8
8
|
</div>
|
|
9
9
|
|
|
@@ -12,11 +12,13 @@ RPC for Service Workers -- move that heavy computation off of your UI thread!
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
14
14
|
- Fully typesafe
|
|
15
|
+
- Lightweight: no dependencies, less than 5 kB (minified+gzipped)
|
|
15
16
|
- Supports any [Standard Schema](https://standardschema.dev)-compliant validation library (ArkType, Zod, Valibot, etc.)
|
|
16
17
|
- Cancelable requests
|
|
17
18
|
- Parallelization with multiple worker instances
|
|
18
19
|
- Automatic [transfer](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects) of transferable values from- and to- worker code
|
|
19
20
|
- A way to polyfill a pre-filled `localStorage` to be accessed within the worker code
|
|
21
|
+
- First-class support for signaling progress updates (and e.g. display a progress bar)
|
|
20
22
|
- Supports [Service workers](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker), [Shared workers](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) and [Dedicated workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker)
|
|
21
23
|
|
|
22
24
|
## Installation
|
|
@@ -83,9 +85,9 @@ export const procedures = {
|
|
|
83
85
|
} as const satisfies ProceduresMap;
|
|
84
86
|
```
|
|
85
87
|
|
|
86
|
-
### 2. Register your procedures in
|
|
88
|
+
### 2. Register your procedures in worker
|
|
87
89
|
|
|
88
|
-
In your
|
|
90
|
+
In your worker file:
|
|
89
91
|
|
|
90
92
|
```javascript
|
|
91
93
|
import fetchProgress from "fetch-progress"
|
|
@@ -152,6 +154,30 @@ Here's a Svelte example!
|
|
|
152
154
|
</ul>
|
|
153
155
|
```
|
|
154
156
|
|
|
157
|
+
### 4. Registering your worker
|
|
158
|
+
|
|
159
|
+
#### Service Workers
|
|
160
|
+
|
|
161
|
+
If you use SvelteKit, [just name your service worker file `src/service-worker.ts`](https://svelte.dev/docs/kit/service-workers)
|
|
162
|
+
|
|
163
|
+
_If you use any other (meta) framework, please contribute usage documentation here :)_
|
|
164
|
+
|
|
165
|
+
#### Dedicated or Shared Workers
|
|
166
|
+
|
|
167
|
+
Preferred over service workers for heavy computations, since you can run multiple instances of them (see [Configure parallelism](#configure-parallelism))
|
|
168
|
+
|
|
169
|
+
If you use Vite, you can [import files as Web Worker classes](https://vite.dev/guide/features.html#web-workers):
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { Client } from "swarpc";
|
|
173
|
+
import { procedures } from "$lib/off-thread/procedures.ts";
|
|
174
|
+
import OffThreadWorker from "$lib/off-thread/worker.ts?worker";
|
|
175
|
+
|
|
176
|
+
const client = Client(procedures, {
|
|
177
|
+
worker: OffThreadWorker, // don't instanciate the class, sw&rpc does it
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
155
181
|
### Configure parallelism
|
|
156
182
|
|
|
157
183
|
By default, when a `worker` is passed to the `Client`'s options, the client will automatically spin up `navigator.hardwareConcurrency` worker instances and distribute requests among them. You can customize this behavior by setting the `Client:options.nodes` option to control the number of _nodes_ (worker instances).
|
|
@@ -166,7 +192,7 @@ For example:
|
|
|
166
192
|
|
|
167
193
|
```ts
|
|
168
194
|
const client = Client(procedures, {
|
|
169
|
-
worker:
|
|
195
|
+
worker: MyWorker,
|
|
170
196
|
nodes: 4,
|
|
171
197
|
});
|
|
172
198
|
|
package/dist/client.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* @module
|
|
3
3
|
* @mergeModuleWith <project>
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { ClientMethod, Hooks,
|
|
5
|
+
import { type LogLevel } from "./log.js";
|
|
6
|
+
import { ClientMethod, Hooks, WorkerConstructor, zProcedures, type ProceduresMap } from "./types.js";
|
|
7
7
|
/**
|
|
8
8
|
* The sw&rpc client instance, which provides {@link ClientMethod | methods to call procedures}.
|
|
9
9
|
* Each property of the procedures map will be a method, that accepts an input, an optional onProgress callback and an optional request ID.
|
|
@@ -14,30 +14,6 @@ export type SwarpcClient<Procedures extends ProceduresMap> = {
|
|
|
14
14
|
} & {
|
|
15
15
|
[F in keyof Procedures]: ClientMethod<Procedures[F]>;
|
|
16
16
|
};
|
|
17
|
-
/**
|
|
18
|
-
* Context for passing around data useful for requests
|
|
19
|
-
*/
|
|
20
|
-
type Context<Procedures extends ProceduresMap> = {
|
|
21
|
-
/** A logger, bound to the client */
|
|
22
|
-
logger: Logger;
|
|
23
|
-
/** The node to use */
|
|
24
|
-
node: Worker | SharedWorker | undefined;
|
|
25
|
-
/** The ID of the node to use */
|
|
26
|
-
nodeId: string | undefined;
|
|
27
|
-
/** Hooks defined by the client */
|
|
28
|
-
hooks: Hooks<Procedures>;
|
|
29
|
-
/** Local storage data defined by the client for the faux local storage */
|
|
30
|
-
localStorage: Record<string, any>;
|
|
31
|
-
};
|
|
32
|
-
export type PendingRequest = {
|
|
33
|
-
/** ID of the node the request was sent to. udefined if running on a service worker */
|
|
34
|
-
nodeId?: string;
|
|
35
|
-
functionName: string;
|
|
36
|
-
reject: (err: Error) => void;
|
|
37
|
-
onProgress: (progress: any) => void;
|
|
38
|
-
resolve: (result: any) => void;
|
|
39
|
-
};
|
|
40
|
-
export type ClientOptions = Parameters<typeof Client>[1];
|
|
41
17
|
/**
|
|
42
18
|
*
|
|
43
19
|
* @param procedures procedures the client will be able to call, see {@link ProceduresMap}
|
|
@@ -64,26 +40,3 @@ export declare function Client<Procedures extends ProceduresMap>(procedures: Pro
|
|
|
64
40
|
restartListener?: boolean;
|
|
65
41
|
localStorage?: Record<string, any>;
|
|
66
42
|
}): SwarpcClient<Procedures>;
|
|
67
|
-
/**
|
|
68
|
-
* A quicker version of postMessage that does not try to start the client listener, await the service worker, etc.
|
|
69
|
-
* esp. useful for abort logic that needs to not be... put behind everything else on the event loop.
|
|
70
|
-
* @param l
|
|
71
|
-
* @param worker
|
|
72
|
-
* @param message
|
|
73
|
-
* @param options
|
|
74
|
-
*/
|
|
75
|
-
export declare function postMessageSync<Procedures extends ProceduresMap>(l: RequestBoundLogger, worker: Worker | SharedWorker | undefined, message: Payload<Procedures>, options?: StructuredSerializeOptions): void;
|
|
76
|
-
/**
|
|
77
|
-
* Starts the client listener, which listens for messages from the sw&rpc server.
|
|
78
|
-
* @param ctx.worker if provided, the client will use this worker to listen for messages, instead of using the service worker
|
|
79
|
-
* @returns
|
|
80
|
-
*/
|
|
81
|
-
export declare function startClientListener<Procedures extends ProceduresMap>(ctx: Context<Procedures>): Promise<void>;
|
|
82
|
-
/**
|
|
83
|
-
* Generate a random request ID, used to identify requests between client and server.
|
|
84
|
-
* @source
|
|
85
|
-
* @returns a 6-character hexadecimal string
|
|
86
|
-
*/
|
|
87
|
-
export declare function makeRequestId(): string;
|
|
88
|
-
export {};
|
|
89
|
-
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.js
CHANGED
|
@@ -1,42 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module
|
|
3
|
-
* @mergeModuleWith <project>
|
|
4
|
-
*/
|
|
5
1
|
import { createLogger, } from "./log.js";
|
|
6
2
|
import { makeNodeId, nodeIdOrSW, whoToSendTo } from "./nodes.js";
|
|
7
3
|
import { zProcedures, } from "./types.js";
|
|
8
4
|
import { findTransferables } from "./utils.js";
|
|
9
|
-
/**
|
|
10
|
-
* Pending requests are stored in a map, where the key is the request ID.
|
|
11
|
-
* Each request has a set of handlers: resolve, reject, and onProgress.
|
|
12
|
-
* This allows having a single listener for the client, and having multiple in-flight calls to the same procedure.
|
|
13
|
-
*/
|
|
14
5
|
const pendingRequests = new Map();
|
|
15
|
-
// Have we started the client listener?
|
|
16
6
|
let _clientListenerStarted = new Set();
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
-
* @param procedures procedures the client will be able to call, see {@link ProceduresMap}
|
|
20
|
-
* @param options various options
|
|
21
|
-
* @param options.worker The worker class, **not instantiated**, or a path to the source code. If not provided, the client will use the service worker. If a string is provided, it'll instantiate a regular `Worker`, not a `SharedWorker`.
|
|
22
|
-
* Example: `"./worker.js"`
|
|
23
|
-
* See {@link Worker} (used by both dedicated workers and service workers), {@link SharedWorker}, and
|
|
24
|
-
* the different [worker types](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API#worker_types) that exist
|
|
25
|
-
* @param options.hooks Hooks to run on messages received from the server. See {@link Hooks}
|
|
26
|
-
* @param options.loglevel Maximum log level to use, defaults to "debug" (shows everything). "info" will not show debug messages, "warn" will only show warnings and errors, "error" will only show errors.
|
|
27
|
-
* @param options.restartListener If true, will force the listener to restart even if it has already been started. You should probably leave this to false, unless you are testing and want to reset the client state.
|
|
28
|
-
* @param options.localStorage Define a in-memory localStorage with the given key-value pairs. Allows code called on the server to access localStorage (even though SharedWorkers don't have access to the browser's real localStorage)
|
|
29
|
-
* @param options.nodes the number of workers to use for the server, defaults to {@link navigator.hardwareConcurrency}.
|
|
30
|
-
* @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}
|
|
31
|
-
*
|
|
32
|
-
* An example of defining and using a client:
|
|
33
|
-
* {@includeCode ../example/src/routes/+page.svelte}
|
|
34
|
-
*/
|
|
35
7
|
export function Client(procedures, { worker, nodes: nodeCount, loglevel = "debug", restartListener = false, hooks = {}, localStorage = {}, } = {}) {
|
|
36
8
|
const l = createLogger("client", loglevel);
|
|
37
9
|
if (restartListener)
|
|
38
10
|
_clientListenerStarted.clear();
|
|
39
|
-
// Store procedures on a symbol key, to avoid conflicts with procedure names
|
|
40
11
|
const instance = { [zProcedures]: procedures };
|
|
41
12
|
nodeCount ??= navigator.hardwareConcurrency || 1;
|
|
42
13
|
let nodes;
|
|
@@ -72,23 +43,17 @@ export function Client(procedures, { worker, nodes: nodeCount, loglevel = "debug
|
|
|
72
43
|
functionName,
|
|
73
44
|
}, options);
|
|
74
45
|
};
|
|
75
|
-
// Set the method on the instance
|
|
76
46
|
const _runProcedure = async (input, onProgress = () => { }, reqid, nodeId) => {
|
|
77
|
-
// Validate the input against the procedure's input schema
|
|
78
47
|
const validation = procedures[functionName].input["~standard"].validate(input);
|
|
79
48
|
if (validation instanceof Promise)
|
|
80
49
|
throw new Error("Validations must not be async");
|
|
81
50
|
if (validation.issues)
|
|
82
51
|
throw new Error(`Invalid input: ${validation.issues}`);
|
|
83
52
|
const requestId = reqid ?? makeRequestId();
|
|
84
|
-
// Choose which node to use
|
|
85
53
|
nodeId ??= whoToSendTo(nodes, pendingRequests);
|
|
86
54
|
const node = nodes && nodeId ? nodes[nodeId] : undefined;
|
|
87
55
|
const l = createLogger("client", loglevel, nodeIdOrSW(nodeId), requestId);
|
|
88
56
|
return new Promise((resolve, reject) => {
|
|
89
|
-
// Store promise handlers (as well as progress updates handler)
|
|
90
|
-
// so the client listener can resolve/reject the promise (and react to progress updates)
|
|
91
|
-
// when the server sends messages back
|
|
92
57
|
pendingRequests.set(requestId, {
|
|
93
58
|
nodeId,
|
|
94
59
|
functionName,
|
|
@@ -99,14 +64,12 @@ export function Client(procedures, { worker, nodes: nodeCount, loglevel = "debug
|
|
|
99
64
|
const transfer = procedures[functionName].autotransfer === "always"
|
|
100
65
|
? findTransferables(input)
|
|
101
66
|
: [];
|
|
102
|
-
// Post the message to the server
|
|
103
67
|
l.debug(`Requesting ${functionName} with`, input);
|
|
104
68
|
return send(node, nodeId, requestId, { input }, { transfer })
|
|
105
69
|
.then(() => { })
|
|
106
70
|
.catch(reject);
|
|
107
71
|
});
|
|
108
72
|
};
|
|
109
|
-
// @ts-expect-error
|
|
110
73
|
instance[functionName] = _runProcedure;
|
|
111
74
|
instance[functionName].broadcast = async (input, onProgresses, nodesCount) => {
|
|
112
75
|
let nodesToUse = [undefined];
|
|
@@ -151,16 +114,11 @@ export function Client(procedures, { worker, nodes: nodeCount, loglevel = "debug
|
|
|
151
114
|
}
|
|
152
115
|
return instance;
|
|
153
116
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Warms up the client by starting the listener and getting the worker, then posts a message to the worker.
|
|
156
|
-
* @returns the worker to use
|
|
157
|
-
*/
|
|
158
117
|
async function postMessage(ctx, message, options) {
|
|
159
118
|
await startClientListener(ctx);
|
|
160
119
|
const { logger: l, node: worker } = ctx;
|
|
161
120
|
if (!worker && !navigator.serviceWorker.controller)
|
|
162
121
|
l.warn("", "Service Worker is not controlling the page");
|
|
163
|
-
// If no worker is provided, we use the service worker
|
|
164
122
|
const w = worker instanceof SharedWorker
|
|
165
123
|
? worker.port
|
|
166
124
|
: worker === undefined
|
|
@@ -171,18 +129,9 @@ async function postMessage(ctx, message, options) {
|
|
|
171
129
|
}
|
|
172
130
|
w.postMessage(message, options);
|
|
173
131
|
}
|
|
174
|
-
|
|
175
|
-
* A quicker version of postMessage that does not try to start the client listener, await the service worker, etc.
|
|
176
|
-
* esp. useful for abort logic that needs to not be... put behind everything else on the event loop.
|
|
177
|
-
* @param l
|
|
178
|
-
* @param worker
|
|
179
|
-
* @param message
|
|
180
|
-
* @param options
|
|
181
|
-
*/
|
|
182
|
-
export function postMessageSync(l, worker, message, options) {
|
|
132
|
+
function postMessageSync(l, worker, message, options) {
|
|
183
133
|
if (!worker && !navigator.serviceWorker.controller)
|
|
184
134
|
l.warn("Service Worker is not controlling the page");
|
|
185
|
-
// If no worker is provided, we use the service worker
|
|
186
135
|
const w = worker instanceof SharedWorker
|
|
187
136
|
? worker.port
|
|
188
137
|
: worker === undefined
|
|
@@ -193,16 +142,10 @@ export function postMessageSync(l, worker, message, options) {
|
|
|
193
142
|
}
|
|
194
143
|
w.postMessage(message, options);
|
|
195
144
|
}
|
|
196
|
-
|
|
197
|
-
* Starts the client listener, which listens for messages from the sw&rpc server.
|
|
198
|
-
* @param ctx.worker if provided, the client will use this worker to listen for messages, instead of using the service worker
|
|
199
|
-
* @returns
|
|
200
|
-
*/
|
|
201
|
-
export async function startClientListener(ctx) {
|
|
145
|
+
async function startClientListener(ctx) {
|
|
202
146
|
if (_clientListenerStarted.has(nodeIdOrSW(ctx.nodeId)))
|
|
203
147
|
return;
|
|
204
148
|
const { logger: l, node: worker } = ctx;
|
|
205
|
-
// Get service worker registration if no worker is provided
|
|
206
149
|
if (!worker) {
|
|
207
150
|
const sw = await navigator.serviceWorker.ready;
|
|
208
151
|
if (!sw?.active) {
|
|
@@ -213,33 +156,24 @@ export async function startClientListener(ctx) {
|
|
|
213
156
|
}
|
|
214
157
|
}
|
|
215
158
|
const w = worker ?? navigator.serviceWorker;
|
|
216
|
-
// Start listening for messages
|
|
217
159
|
l.debug(null, "Starting client listener", { w, ...ctx });
|
|
218
160
|
const listener = (event) => {
|
|
219
|
-
// Get the data from the event
|
|
220
161
|
const eventData = event.data || {};
|
|
221
|
-
// Ignore other messages that aren't for us
|
|
222
162
|
if (eventData?.by !== "sw&rpc")
|
|
223
163
|
return;
|
|
224
|
-
// We don't use a schema here, we trust the server to send valid data
|
|
225
164
|
const payload = eventData;
|
|
226
|
-
// Ignore #initialize request, it's client->server only
|
|
227
165
|
if ("isInitializeRequest" in payload) {
|
|
228
166
|
l.warn(null, "Ignoring unexpected #initialize from server", payload);
|
|
229
167
|
return;
|
|
230
168
|
}
|
|
231
169
|
const { requestId, ...data } = payload;
|
|
232
|
-
// Sanity check in case we somehow receive a message without requestId
|
|
233
170
|
if (!requestId) {
|
|
234
171
|
throw new Error("[SWARPC Client] Message received without requestId");
|
|
235
172
|
}
|
|
236
|
-
// Get the associated pending request handlers
|
|
237
173
|
const handlers = pendingRequests.get(requestId);
|
|
238
174
|
if (!handlers) {
|
|
239
175
|
throw new Error(`[SWARPC Client] ${requestId} has no active request handlers, cannot process ${JSON.stringify(data)}`);
|
|
240
176
|
}
|
|
241
|
-
// React to the data received: call hook, call handler,
|
|
242
|
-
// and remove the request from pendingRequests (unless it's a progress update)
|
|
243
177
|
if ("error" in data) {
|
|
244
178
|
ctx.hooks.error?.(data.functionName, new Error(data.error.message));
|
|
245
179
|
handlers.reject(new Error(data.error.message));
|
|
@@ -263,7 +197,6 @@ export async function startClientListener(ctx) {
|
|
|
263
197
|
w.addEventListener("message", listener);
|
|
264
198
|
}
|
|
265
199
|
_clientListenerStarted.add(nodeIdOrSW(ctx.nodeId));
|
|
266
|
-
// Recursive terminal case is ensured by calling this *after* _clientListenerStarted is set to true: startClientListener() will therefore not be called in postMessage() again.
|
|
267
200
|
await postMessage(ctx, {
|
|
268
201
|
by: "sw&rpc",
|
|
269
202
|
functionName: "#initialize",
|
|
@@ -272,11 +205,6 @@ export async function startClientListener(ctx) {
|
|
|
272
205
|
nodeId: nodeIdOrSW(ctx.nodeId),
|
|
273
206
|
});
|
|
274
207
|
}
|
|
275
|
-
|
|
276
|
-
* Generate a random request ID, used to identify requests between client and server.
|
|
277
|
-
* @source
|
|
278
|
-
* @returns a 6-character hexadecimal string
|
|
279
|
-
*/
|
|
280
|
-
export function makeRequestId() {
|
|
208
|
+
function makeRequestId() {
|
|
281
209
|
return Math.random().toString(16).substring(2, 8).toUpperCase();
|
|
282
210
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* @mergeModuleWith <project>
|
|
4
4
|
*/
|
|
5
5
|
import "./polyfills.js";
|
|
6
|
-
export * from "./client.js";
|
|
7
|
-
export * from "./server.js";
|
|
8
6
|
export type { ProceduresMap, CancelablePromise } from "./types.js";
|
|
9
|
-
|
|
7
|
+
export { Client } from "./client.js";
|
|
8
|
+
export type { SwarpcClient } from "./client.js";
|
|
9
|
+
export { Server } from "./server.js";
|
|
10
|
+
export type { SwarpcServer } from "./server.js";
|
package/dist/index.js
CHANGED
package/dist/localstorage.d.ts
CHANGED
|
@@ -1,14 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
data: Record<string, any>;
|
|
3
|
-
keysOrder: string[];
|
|
4
|
-
constructor(data: Record<string, any>);
|
|
5
|
-
setItem(key: string, value: string): void;
|
|
6
|
-
getItem(key: string): any;
|
|
7
|
-
hasItem(key: string): boolean;
|
|
8
|
-
removeItem(key: string): void;
|
|
9
|
-
clear(): void;
|
|
10
|
-
key(index: number): string;
|
|
11
|
-
get length(): number;
|
|
12
|
-
register(subject: WorkerGlobalScope | SharedWorkerGlobalScope): void;
|
|
13
|
-
}
|
|
14
|
-
//# sourceMappingURL=localstorage.d.ts.map
|
|
1
|
+
export {};
|
package/dist/localstorage.js
CHANGED
package/dist/log.d.ts
CHANGED
|
@@ -2,33 +2,4 @@
|
|
|
2
2
|
* @module
|
|
3
3
|
* @mergeModuleWith <project>
|
|
4
4
|
*/
|
|
5
|
-
/**
|
|
6
|
-
* @ignore
|
|
7
|
-
*/
|
|
8
|
-
export declare function createLogger(side: "server" | "client", level: LogLevel, nid?: string): Logger;
|
|
9
|
-
export declare function createLogger(side: "server" | "client", level: LogLevel, nid: string, rqid: string): RequestBoundLogger;
|
|
10
|
-
/**
|
|
11
|
-
* @ignore
|
|
12
|
-
*/
|
|
13
|
-
export type Logger = {
|
|
14
|
-
debug: (rqid: string | null, message: string, ...args: any[]) => void;
|
|
15
|
-
info: (rqid: string | null, message: string, ...args: any[]) => void;
|
|
16
|
-
warn: (rqid: string | null, message: string, ...args: any[]) => void;
|
|
17
|
-
error: (rqid: string | null, message: string, ...args: any[]) => void;
|
|
18
|
-
};
|
|
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
|
-
declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error"];
|
|
27
|
-
export type LogLevel = (typeof LOG_LEVELS)[number];
|
|
28
|
-
/**
|
|
29
|
-
*
|
|
30
|
-
* @param scope
|
|
31
|
-
*/
|
|
32
|
-
export declare function injectIntoConsoleGlobal(scope: WorkerGlobalScope | SharedWorkerGlobalScope, nodeId: string): void;
|
|
33
5
|
export {};
|
|
34
|
-
//# sourceMappingURL=log.d.ts.map
|
package/dist/log.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module
|
|
3
|
-
* @mergeModuleWith <project>
|
|
4
|
-
*/
|
|
5
1
|
export function createLogger(side, level = "debug", nid, rqid) {
|
|
6
2
|
const lvls = LOG_LEVELS.slice(LOG_LEVELS.indexOf(level));
|
|
7
3
|
if (rqid && nid) {
|
|
@@ -20,7 +16,6 @@ export function createLogger(side, level = "debug", nid, rqid) {
|
|
|
20
16
|
error: lvls.includes("error") ? logger("error", side, nid) : () => { },
|
|
21
17
|
};
|
|
22
18
|
}
|
|
23
|
-
/** @source */
|
|
24
19
|
const LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
25
20
|
const PATCHABLE_LOG_METHODS = [
|
|
26
21
|
"debug",
|
|
@@ -40,13 +35,6 @@ const originalConsole = PATCHABLE_LOG_METHODS.reduce((result, method) => {
|
|
|
40
35
|
result[method] = console[method];
|
|
41
36
|
return result;
|
|
42
37
|
}, {});
|
|
43
|
-
/**
|
|
44
|
-
* Send log messages to the console, with a helpful prefix.
|
|
45
|
-
* @param method
|
|
46
|
-
* @param side
|
|
47
|
-
* @param ids request ID and node ID
|
|
48
|
-
* @param args passed to console methods directly
|
|
49
|
-
*/
|
|
50
38
|
function log(method, side, { rqid, nid }, ...args) {
|
|
51
39
|
const prefix = [
|
|
52
40
|
`[SWARPC ${side}]`,
|
|
@@ -62,12 +50,8 @@ function log(method, side, { rqid, nid }, ...args) {
|
|
|
62
50
|
prefixStyles.push("color: hotpink", "color: inherit");
|
|
63
51
|
return originalConsole[method](prefix, ...prefixStyles, ...args);
|
|
64
52
|
}
|
|
65
|
-
|
|
66
|
-
*
|
|
67
|
-
* @param scope
|
|
68
|
-
*/
|
|
69
|
-
export function injectIntoConsoleGlobal(scope, nodeId) {
|
|
53
|
+
export function injectIntoConsoleGlobal(scope, nodeId, requestId) {
|
|
70
54
|
for (const method of PATCHABLE_LOG_METHODS) {
|
|
71
|
-
scope.self.console[method] = logger(method, "server", nodeId);
|
|
55
|
+
scope.self.console[method] = (...args) => logger(method, "server", nodeId)(requestId, ...args);
|
|
72
56
|
}
|
|
73
57
|
}
|
package/dist/nodes.d.ts
CHANGED
|
@@ -1,15 +1 @@
|
|
|
1
|
-
import { PendingRequest } from "./client.js";
|
|
2
|
-
/**
|
|
3
|
-
* Returns to which node to send the next request, given the state of the currently pending requests
|
|
4
|
-
*/
|
|
5
|
-
export declare function whoToSendTo(nodes: undefined | Record<string, unknown>, requests: Map<string, PendingRequest>): undefined | string;
|
|
6
|
-
export declare function nodeIdFromScope(scope: WorkerGlobalScope, _scopeType?: "dedicated" | "shared" | "service"): string;
|
|
7
|
-
/**
|
|
8
|
-
* Generate a random request ID, used to identify nodes in the client
|
|
9
|
-
* @source
|
|
10
|
-
*/
|
|
11
|
-
export declare function makeNodeId(): string;
|
|
12
|
-
declare const serviceWorkerNodeId: "(SW)";
|
|
13
|
-
export declare function nodeIdOrSW(id: string | undefined): string | typeof serviceWorkerNodeId;
|
|
14
1
|
export {};
|
|
15
|
-
//# sourceMappingURL=nodes.d.ts.map
|
package/dist/nodes.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { scopeIsDedicated, scopeIsShared } from "./scopes.js";
|
|
2
|
-
/**
|
|
3
|
-
* Returns to which node to send the next request, given the state of the currently pending requests
|
|
4
|
-
*/
|
|
5
2
|
export function whoToSendTo(nodes, requests) {
|
|
6
3
|
if (!nodes)
|
|
7
4
|
return undefined;
|
|
@@ -14,7 +11,6 @@ export function whoToSendTo(nodes, requests) {
|
|
|
14
11
|
for (const [node, reqs] of requestsPerNode.entries()) {
|
|
15
12
|
if (!node)
|
|
16
13
|
continue;
|
|
17
|
-
// Send to the least busy node
|
|
18
14
|
if (reqs.length < requestsPerNode.get(chosen).length)
|
|
19
15
|
chosen = node;
|
|
20
16
|
}
|
|
@@ -27,14 +23,10 @@ export function nodeIdFromScope(scope, _scopeType) {
|
|
|
27
23
|
}
|
|
28
24
|
return "(SW)";
|
|
29
25
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Generate a random request ID, used to identify nodes in the client
|
|
32
|
-
* @source
|
|
33
|
-
*/
|
|
34
26
|
export function makeNodeId() {
|
|
35
27
|
return "N" + Math.random().toString(16).substring(2, 5).toUpperCase();
|
|
36
28
|
}
|
|
37
|
-
const serviceWorkerNodeId = "(SW)";
|
|
29
|
+
const serviceWorkerNodeId = "(SW)";
|
|
38
30
|
export function nodeIdOrSW(id) {
|
|
39
31
|
return id ?? serviceWorkerNodeId;
|
|
40
32
|
}
|
package/dist/polyfills.d.ts
CHANGED
package/dist/polyfills.js
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Groups elements from an iterable into a Map based on a callback function.
|
|
3
|
-
*
|
|
4
|
-
* @template K, T
|
|
5
|
-
* @param {Iterable<T>} iterable - The iterable to group.
|
|
6
|
-
* @param {function(T, number): K} callbackfn - The callback function to
|
|
7
|
-
* determine the grouping key.
|
|
8
|
-
* @returns {Map<K, T[]>} A Map where keys are the grouping keys and values are
|
|
9
|
-
* arrays of grouped elements.
|
|
10
|
-
*/
|
|
11
1
|
Map.groupBy ??= function groupBy(iterable, callbackfn) {
|
|
12
2
|
const map = new Map();
|
|
13
3
|
let i = 0;
|
package/dist/scopes.d.ts
CHANGED
|
@@ -1,4 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export declare function scopeIsDedicated(scope: WorkerGlobalScope, _scopeType?: "dedicated" | "shared" | "service"): scope is DedicatedWorkerGlobalScope;
|
|
3
|
-
export declare function scopeIsService(scope: WorkerGlobalScope, _scopeType?: "dedicated" | "shared" | "service"): scope is ServiceWorkerGlobalScope;
|
|
4
|
-
//# sourceMappingURL=scopes.d.ts.map
|
|
1
|
+
export {};
|
package/dist/server.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export type SwarpcServer<Procedures extends ProceduresMap> = {
|
|
|
21
21
|
* @param options various options
|
|
22
22
|
* @param options.scope The worker scope to use, defaults to the `self` of the file where Server() is called.
|
|
23
23
|
* @param options.loglevel Maximum log level to use, defaults to "debug" (shows everything). "info" will not show debug messages, "warn" will only show warnings and errors, "error" will only show errors.
|
|
24
|
-
* @param options._scopeType
|
|
24
|
+
* @param options._scopeType Don't touch, this is used in testing environments because the mock is subpar. Manually overrides worker scope type detection.
|
|
25
25
|
* @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.
|
|
26
26
|
*
|
|
27
27
|
* An example of defining a server:
|
|
@@ -32,4 +32,3 @@ export declare function Server<Procedures extends ProceduresMap>(procedures: Pro
|
|
|
32
32
|
loglevel?: LogLevel;
|
|
33
33
|
_scopeType?: "dedicated" | "shared" | "service";
|
|
34
34
|
}): SwarpcServer<Procedures>;
|
|
35
|
-
//# sourceMappingURL=server.d.ts.map
|