swarpc 0.11.0 → 0.13.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/src/types.ts CHANGED
@@ -1,239 +1,264 @@
1
- /**
2
- * @module
3
- * @mergeModuleWith <project>
4
- */
5
-
6
- import { type, type Type } from "arktype"
7
- import { RequestBoundLogger } from "./log.js"
8
-
9
- /**
10
- * A procedure declaration
11
- */
12
- export type Procedure<I extends Type, P extends Type, S extends Type> = {
13
- /**
14
- * ArkType type for the input (first argument) of the procedure, when calling it from the client.
15
- */
16
- input: I
17
- /**
18
- * ArkType type for the data as the first argument given to the `onProgress` callback
19
- * when calling the procedure from the client.
20
- */
21
- progress: P
22
- /**
23
- * ArkType type for the output (return value) of the procedure, when calling it from the client.
24
- */
25
- success: S
26
- /**
27
- * When should the procedure automatically add ArrayBuffers and other transferable objects
28
- * to the [transfer list](https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/postMessage#transfer)
29
- * when sending messages, both from the client to the server and vice versa.
30
- *
31
- * Transferring objects can improve performance by avoiding copies of large objects,
32
- * but _moves_ them to the other context, meaning that they cannot be used in the original context after being sent.
33
- *
34
- * 'output-only' by default: only transferables sent from the server to the client will be transferred.
35
- */
36
- autotransfer?: "always" | "never" | "output-only"
37
- }
38
-
39
- /**
40
- * A promise that you can cancel by calling `.cancel(reason)` on it:
41
- *
42
- * ```js
43
- * const { request, cancel } = client.runProcedure.cancelable(input, onProgress)
44
- * setTimeout(() => cancel("Cancelled by user"), 1000)
45
- * const result = await request
46
- * ```
47
- */
48
- export type CancelablePromise<T = unknown> = {
49
- request: Promise<T>
50
- /**
51
- * Abort the request.
52
- * @param reason The reason for cancelling the request.
53
- */
54
- cancel: (reason: string) => void
55
- }
56
-
57
- /**
58
- * An implementation of a procedure
59
- */
60
- export type ProcedureImplementation<
61
- I extends Type,
62
- P extends Type,
63
- S extends Type,
64
- > = (
65
- /**
66
- * Input data for the procedure
67
- */
68
- input: I["inferOut"],
69
- /**
70
- * Callback to call with progress updates.
71
- */
72
- onProgress: (progress: P["inferIn"]) => void,
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
- }
86
- ) => Promise<S["inferIn"]>
87
-
88
- /**
89
- * Declarations of procedures by name.
90
- *
91
- * An example of declaring procedures:
92
- * {@includeCode ../example/src/lib/procedures.ts}
93
- */
94
- export type ProceduresMap = Record<string, Procedure<Type, Type, Type>>
95
-
96
- /**
97
- * Implementations of procedures by name
98
- */
99
- export type ImplementationsMap<Procedures extends ProceduresMap> = {
100
- [F in keyof Procedures]: ProcedureImplementation<
101
- Procedures[F]["input"],
102
- Procedures[F]["progress"],
103
- Procedures[F]["success"]
104
- >
105
- }
106
-
107
- /**
108
- * Declaration of hooks to run on messages received from the server
109
- */
110
- export type Hooks<Procedures extends ProceduresMap> = {
111
- /**
112
- * Called when a procedure call has been successful.
113
- */
114
- success?: <Procedure extends keyof ProceduresMap>(
115
- procedure: Procedure,
116
- data: Procedures[Procedure]["success"]["inferOut"]
117
- ) => void
118
- /**
119
- * Called when a procedure call has failed.
120
- */
121
- error?: <Procedure extends keyof ProceduresMap>(
122
- procedure: Procedure,
123
- error: Error
124
- ) => void
125
- /**
126
- * Called when a procedure call sends progress updates.
127
- */
128
- progress?: <Procedure extends keyof ProceduresMap>(
129
- procedure: Procedure,
130
- data: Procedures[Procedure]["progress"]["inferOut"]
131
- ) => void
132
- }
133
-
134
- export const PayloadInitializeSchema = type({
135
- by: '"sw&rpc"',
136
- functionName: '"#initialize"',
137
- localStorageData: "Record<string, unknown>",
138
- })
139
-
140
- export type PayloadInitialize = typeof PayloadInitializeSchema.infer
141
-
142
- /**
143
- * @source
144
- */
145
- export const PayloadHeaderSchema = type("<Name extends string>", {
146
- by: '"sw&rpc"',
147
- functionName: "Name",
148
- requestId: "string >= 1",
149
- })
150
-
151
- export type PayloadHeader<
152
- PM extends ProceduresMap,
153
- Name extends keyof PM = keyof PM,
154
- > = {
155
- by: "sw&rpc"
156
- functionName: Name & string
157
- requestId: string
158
- }
159
-
160
- /**
161
- * @source
162
- */
163
- export const PayloadCoreSchema = type("<I, P, S>", {
164
- "input?": "I",
165
- "progress?": "P",
166
- "result?": "S",
167
- "abort?": { reason: "string" },
168
- "error?": { message: "string" },
169
- })
170
-
171
- export type PayloadCore<
172
- PM extends ProceduresMap,
173
- Name extends keyof PM = keyof PM,
174
- > =
175
- | {
176
- input: PM[Name]["input"]["inferOut"]
177
- }
178
- | {
179
- progress: PM[Name]["progress"]["inferOut"]
180
- }
181
- | {
182
- result: PM[Name]["success"]["inferOut"]
183
- }
184
- | {
185
- abort: { reason: string }
186
- }
187
- | {
188
- error: { message: string }
189
- }
190
-
191
- /**
192
- * @source
193
- */
194
- export const PayloadSchema = type
195
- .scope({ PayloadCoreSchema, PayloadHeaderSchema, PayloadInitializeSchema })
196
- .type("<Name extends string, I, P, S>", [
197
- ["PayloadHeaderSchema<Name>", "&", "PayloadCoreSchema<I, P, S>"],
198
- "|",
199
- "PayloadInitializeSchema",
200
- ])
201
-
202
- /**
203
- * The effective payload as sent by the server to the client
204
- */
205
- export type Payload<
206
- PM extends ProceduresMap,
207
- Name extends keyof PM = keyof PM,
208
- > = (PayloadHeader<PM, Name> & PayloadCore<PM, Name>) | PayloadInitialize
209
-
210
- /**
211
- * A procedure's corresponding method on the client instance -- used to call the procedure. If you want to be able to cancel the request, you can use the `cancelable` method instead of running the procedure directly.
212
- */
213
- export type ClientMethod<P extends Procedure<Type, Type, Type>> = ((
214
- input: P["input"]["inferIn"],
215
- onProgress?: (progress: P["progress"]["inferOut"]) => void
216
- ) => Promise<P["success"]["inferOut"]>) & {
217
- /**
218
- * A method that returns a `CancelablePromise`. Cancel it by calling `.cancel(reason)` on it, and wait for the request to resolve by awaiting the `request` property on the returned object.
219
- */
220
- cancelable: (
221
- input: P["input"]["inferIn"],
222
- onProgress?: (progress: P["progress"]["inferOut"]) => void,
223
- requestId?: string
224
- ) => CancelablePromise<P["success"]["inferOut"]>
225
- }
226
-
227
- /**
228
- * Symbol used as the key for the procedures map on the server instance
229
- * @internal
230
- * @source
231
- */
232
- export const zImplementations = Symbol("SWARPC implementations")
233
-
234
- /**
235
- * Symbol used as the key for the procedures map on instances
236
- * @internal
237
- * @source
238
- */
239
- export const zProcedures = Symbol("SWARPC procedures")
1
+ /**
2
+ * @module
3
+ * @mergeModuleWith <project>
4
+ */
5
+
6
+ import { type, type Type } from "arktype";
7
+ import { RequestBoundLogger } from "./log.js";
8
+
9
+ /**
10
+ * A procedure declaration
11
+ */
12
+ export type Procedure<I extends Type, P extends Type, S extends Type> = {
13
+ /**
14
+ * ArkType type for the input (first argument) of the procedure, when calling it from the client.
15
+ */
16
+ input: I;
17
+ /**
18
+ * ArkType type for the data as the first argument given to the `onProgress` callback
19
+ * when calling the procedure from the client.
20
+ */
21
+ progress: P;
22
+ /**
23
+ * ArkType type for the output (return value) of the procedure, when calling it from the client.
24
+ */
25
+ success: S;
26
+ /**
27
+ * When should the procedure automatically add ArrayBuffers and other transferable objects
28
+ * to the [transfer list](https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/postMessage#transfer)
29
+ * when sending messages, both from the client to the server and vice versa.
30
+ *
31
+ * Transferring objects can improve performance by avoiding copies of large objects,
32
+ * but _moves_ them to the other context, meaning that they cannot be used in the original context after being sent.
33
+ *
34
+ * 'output-only' by default: only transferables sent from the server to the client will be transferred.
35
+ */
36
+ autotransfer?: "always" | "never" | "output-only";
37
+ };
38
+
39
+ /**
40
+ * A promise that you can cancel by calling `.cancel(reason)` on it:
41
+ *
42
+ * ```js
43
+ * const { request, cancel } = client.runProcedure.cancelable(input, onProgress)
44
+ * setTimeout(() => cancel("Cancelled by user"), 1000)
45
+ * const result = await request
46
+ * ```
47
+ */
48
+ export type CancelablePromise<T = unknown> = {
49
+ request: Promise<T>;
50
+ /**
51
+ * Abort the request.
52
+ * @param reason The reason for cancelling the request.
53
+ */
54
+ cancel: (reason: string) => void;
55
+ };
56
+
57
+ /**
58
+ * An implementation of a procedure
59
+ */
60
+ export type ProcedureImplementation<
61
+ I extends Type,
62
+ P extends Type,
63
+ S extends Type,
64
+ > = (
65
+ /**
66
+ * Input data for the procedure
67
+ */
68
+ input: I["inferOut"],
69
+ /**
70
+ * Callback to call with progress updates.
71
+ */
72
+ onProgress: (progress: P["inferIn"]) => void,
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
+ /**
86
+ * ID of the Node the request is being processed on.
87
+ */
88
+ nodeId: string;
89
+ },
90
+ ) => Promise<S["inferIn"]>;
91
+
92
+ /**
93
+ * Declarations of procedures by name.
94
+ *
95
+ * An example of declaring procedures:
96
+ * {@includeCode ../example/src/lib/procedures.ts}
97
+ */
98
+ export type ProceduresMap = Record<string, Procedure<Type, Type, Type>>;
99
+
100
+ /**
101
+ * Implementations of procedures by name
102
+ */
103
+ export type ImplementationsMap<Procedures extends ProceduresMap> = {
104
+ [F in keyof Procedures]: ProcedureImplementation<
105
+ Procedures[F]["input"],
106
+ Procedures[F]["progress"],
107
+ Procedures[F]["success"]
108
+ >;
109
+ };
110
+
111
+ /**
112
+ * Declaration of hooks to run on messages received from the server
113
+ */
114
+ export type Hooks<Procedures extends ProceduresMap> = {
115
+ /**
116
+ * Called when a procedure call has been successful.
117
+ */
118
+ success?: <Procedure extends keyof ProceduresMap>(
119
+ procedure: Procedure,
120
+ data: Procedures[Procedure]["success"]["inferOut"],
121
+ ) => void;
122
+ /**
123
+ * Called when a procedure call has failed.
124
+ */
125
+ error?: <Procedure extends keyof ProceduresMap>(
126
+ procedure: Procedure,
127
+ error: Error,
128
+ ) => void;
129
+ /**
130
+ * Called when a procedure call sends progress updates.
131
+ */
132
+ progress?: <Procedure extends keyof ProceduresMap>(
133
+ procedure: Procedure,
134
+ data: Procedures[Procedure]["progress"]["inferOut"],
135
+ ) => void;
136
+ };
137
+
138
+ export const PayloadInitializeSchema = type({
139
+ by: '"sw&rpc"',
140
+ functionName: '"#initialize"',
141
+ isInitializeRequest: "true",
142
+ localStorageData: "Record<string, unknown>",
143
+ nodeId: "string",
144
+ });
145
+
146
+ export type PayloadInitialize = typeof PayloadInitializeSchema.infer;
147
+
148
+ /**
149
+ * @source
150
+ */
151
+ export const PayloadHeaderSchema = type("<Name extends string>", {
152
+ by: '"sw&rpc"',
153
+ functionName: "Name",
154
+ requestId: "string >= 1",
155
+ });
156
+
157
+ export type PayloadHeader<
158
+ PM extends ProceduresMap,
159
+ Name extends keyof PM = keyof PM,
160
+ > = {
161
+ by: "sw&rpc";
162
+ functionName: Name & string;
163
+ requestId: string;
164
+ };
165
+
166
+ /**
167
+ * @source
168
+ */
169
+ export const PayloadCoreSchema = type("<I, P, S>", {
170
+ "input?": "I",
171
+ "progress?": "P",
172
+ "result?": "S",
173
+ "abort?": { reason: "string" },
174
+ "error?": { message: "string" },
175
+ });
176
+
177
+ export type PayloadCore<
178
+ PM extends ProceduresMap,
179
+ Name extends keyof PM = keyof PM,
180
+ > =
181
+ | {
182
+ input: PM[Name]["input"]["inferOut"];
183
+ }
184
+ | {
185
+ progress: PM[Name]["progress"]["inferOut"];
186
+ }
187
+ | {
188
+ result: PM[Name]["success"]["inferOut"];
189
+ }
190
+ | {
191
+ abort: { reason: string };
192
+ }
193
+ | {
194
+ error: { message: string };
195
+ };
196
+
197
+ /**
198
+ * @source
199
+ */
200
+ export const PayloadSchema = type
201
+ .scope({ PayloadCoreSchema, PayloadHeaderSchema, PayloadInitializeSchema })
202
+ .type("<Name extends string, I, P, S>", [
203
+ ["PayloadHeaderSchema<Name>", "&", "PayloadCoreSchema<I, P, S>"],
204
+ "|",
205
+ "PayloadInitializeSchema",
206
+ ]);
207
+
208
+ /**
209
+ * The effective payload as sent by the server to the client
210
+ */
211
+ export type Payload<
212
+ PM extends ProceduresMap,
213
+ Name extends keyof PM = keyof PM,
214
+ > = (PayloadHeader<PM, Name> & PayloadCore<PM, Name>) | PayloadInitialize;
215
+
216
+ /**
217
+ * A procedure's corresponding method on the client instance -- used to call the procedure. If you want to be able to cancel the request, you can use the `cancelable` method instead of running the procedure directly.
218
+ */
219
+ export type ClientMethod<P extends Procedure<Type, Type, Type>> = ((
220
+ input: P["input"]["inferIn"],
221
+ onProgress?: (progress: P["progress"]["inferOut"]) => void,
222
+ ) => Promise<P["success"]["inferOut"]>) & {
223
+ /**
224
+ * A method that returns a `CancelablePromise`. Cancel it by calling `.cancel(reason)` on it, and wait for the request to resolve by awaiting the `request` property on the returned object.
225
+ */
226
+ cancelable: (
227
+ input: P["input"]["inferIn"],
228
+ onProgress?: (progress: P["progress"]["inferOut"]) => void,
229
+ requestId?: string,
230
+ ) => CancelablePromise<P["success"]["inferOut"]>;
231
+ /**
232
+ * Send the request to specific nodes, or all nodes.
233
+ * Returns an array of results, one for each node the request was sent to.
234
+ * Each result is a {@link PromiseSettledResult}, with also an additional property, the node ID of the request
235
+ */
236
+ broadcast: (
237
+ input: P["input"]["inferIn"],
238
+ onProgress?: (progress: P["progress"]["inferOut"]) => void,
239
+ /** Number of nodes to send the request to. Leave undefined to send to all nodes */
240
+ nodes?: number,
241
+ ) => Promise<
242
+ Array<PromiseSettledResult<P["success"]["inferOut"]> & { node: string }>
243
+ >;
244
+ };
245
+
246
+ /**
247
+ * Symbol used as the key for the procedures map on the server instance
248
+ * @internal
249
+ * @source
250
+ */
251
+ export const zImplementations = Symbol("SWARPC implementations");
252
+
253
+ /**
254
+ * Symbol used as the key for the procedures map on instances
255
+ * @internal
256
+ * @source
257
+ */
258
+ export const zProcedures = Symbol("SWARPC procedures");
259
+
260
+ export type WorkerConstructor<
261
+ T extends Worker | SharedWorker = Worker | SharedWorker,
262
+ > = {
263
+ new (opts?: { name?: string }): T;
264
+ };
package/src/utils.ts CHANGED
@@ -1,34 +1,34 @@
1
- type Constructor<T> = new (...args: any[]) => T
2
-
3
- // TODO: keep it in sync with web standards, how?
4
- const transferableClasses: Constructor<Transferable>[] = [
5
- MessagePort,
6
- ReadableStream,
7
- WritableStream,
8
- TransformStream,
9
- ArrayBuffer,
10
- ]
11
-
12
- export function findTransferables(value: any): Transferable[] {
13
- if (value === null || value === undefined) {
14
- return []
15
- }
16
-
17
- if (typeof value === "object") {
18
- if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
19
- return [value]
20
- }
21
-
22
- if (transferableClasses.some((cls) => value instanceof cls)) {
23
- return [value as Transferable]
24
- }
25
-
26
- if (Array.isArray(value)) {
27
- return value.flatMap(findTransferables)
28
- }
29
-
30
- return Object.values(value).flatMap(findTransferables)
31
- }
32
-
33
- return []
34
- }
1
+ type Constructor<T> = new (...args: any[]) => T;
2
+
3
+ // TODO: keep it in sync with web standards, how?
4
+ const transferableClasses: Constructor<Transferable>[] = [
5
+ MessagePort,
6
+ ReadableStream,
7
+ WritableStream,
8
+ TransformStream,
9
+ ArrayBuffer,
10
+ ];
11
+
12
+ export function findTransferables(value: any): Transferable[] {
13
+ if (value === null || value === undefined) {
14
+ return [];
15
+ }
16
+
17
+ if (typeof value === "object") {
18
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
19
+ return [value];
20
+ }
21
+
22
+ if (transferableClasses.some((cls) => value instanceof cls)) {
23
+ return [value as Transferable];
24
+ }
25
+
26
+ if (Array.isArray(value)) {
27
+ return value.flatMap(findTransferables);
28
+ }
29
+
30
+ return Object.values(value).flatMap(findTransferables);
31
+ }
32
+
33
+ return [];
34
+ }