swarpc 0.6.0 → 0.7.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 +44 -0
- package/dist/src/client.d.ts +22 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +159 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +2 -0
- package/dist/src/log.d.ts +20 -0
- package/dist/src/log.d.ts.map +1 -0
- package/dist/src/log.js +45 -0
- package/dist/src/server.d.ts +15 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +132 -0
- package/dist/src/types.d.ts +260 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +28 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/{utils.js → src/utils.js} +0 -6
- package/dist/tests/core.procedures.d.ts +45 -0
- package/dist/tests/core.procedures.d.ts.map +1 -0
- package/dist/tests/core.procedures.js +49 -0
- package/dist/tests/core.test.d.ts +2 -0
- package/dist/tests/core.test.d.ts.map +1 -0
- package/dist/tests/core.test.js +100 -0
- package/dist/tests/core.worker.d.ts +2 -0
- package/dist/tests/core.worker.d.ts.map +1 -0
- package/dist/tests/core.worker.js +30 -0
- package/dist/vite.config.d.ts +3 -0
- package/dist/vite.config.d.ts.map +1 -0
- package/dist/vite.config.js +7 -0
- package/package.json +10 -6
- package/src/client.ts +245 -0
- package/src/index.ts +3 -0
- package/src/log.ts +62 -0
- package/src/server.ts +193 -0
- package/src/types.ts +66 -12
- package/src/utils.ts +0 -6
- package/dist/swarpc.d.ts +0 -25
- package/dist/swarpc.d.ts.map +0 -1
- package/dist/swarpc.js +0 -264
- package/dist/types.d.ts +0 -114
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -8
- package/dist/utils.d.ts.map +0 -1
- package/src/swarpc.ts +0 -359
- /package/dist/{utils.d.ts → src/utils.d.ts} +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { type Type } from "arktype";
|
|
2
|
+
/**
|
|
3
|
+
* A procedure declaration
|
|
4
|
+
*/
|
|
5
|
+
export type Procedure<I extends Type, P extends Type, S extends Type> = {
|
|
6
|
+
/**
|
|
7
|
+
* ArkType type for the input (first argument) of the procedure, when calling it from the client.
|
|
8
|
+
*/
|
|
9
|
+
input: I;
|
|
10
|
+
/**
|
|
11
|
+
* ArkType type for the data as the first argument given to the `onProgress` callback
|
|
12
|
+
* when calling the procedure from the client.
|
|
13
|
+
*/
|
|
14
|
+
progress: P;
|
|
15
|
+
/**
|
|
16
|
+
* ArkType type for the output (return value) of the procedure, when calling it from the client.
|
|
17
|
+
*/
|
|
18
|
+
success: S;
|
|
19
|
+
/**
|
|
20
|
+
* When should the procedure automatically add ArrayBuffers and other transferable objects
|
|
21
|
+
* to the [transfer list](https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/postMessage#transfer)
|
|
22
|
+
* when sending messages, both from the client to the server and vice versa.
|
|
23
|
+
*
|
|
24
|
+
* Transferring objects can improve performance by avoiding copies of large objects,
|
|
25
|
+
* but _moves_ them to the other context, meaning that they cannot be used in the original context after being sent.
|
|
26
|
+
*
|
|
27
|
+
* 'output-only' by default: only transferables sent from the server to the client will be transferred.
|
|
28
|
+
*/
|
|
29
|
+
autotransfer?: "always" | "never" | "output-only";
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A promise that you can cancel by calling `.cancel(reason)` on it:
|
|
33
|
+
*
|
|
34
|
+
* ```js
|
|
35
|
+
* const { request, cancel } = client.runProcedure.cancelable(input, onProgress)
|
|
36
|
+
* setTimeout(() => cancel("Cancelled by user"), 1000)
|
|
37
|
+
* const result = await request
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export type CancelablePromise<T> = {
|
|
41
|
+
request: Promise<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Abort the request.
|
|
44
|
+
* @param reason The reason for cancelling the request.
|
|
45
|
+
*/
|
|
46
|
+
cancel: (reason: string) => Promise<void>;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* An implementation of a procedure
|
|
50
|
+
*/
|
|
51
|
+
export type ProcedureImplementation<I extends Type, P extends Type, S extends Type> = (input: I["inferOut"], onProgress: (progress: P["inferIn"]) => void, abortSignal?: AbortSignal) => Promise<S["inferIn"]>;
|
|
52
|
+
/**
|
|
53
|
+
* Declarations of procedures by name.
|
|
54
|
+
*/
|
|
55
|
+
export type ProceduresMap = Record<string, Procedure<Type, Type, Type>>;
|
|
56
|
+
/**
|
|
57
|
+
* Implementations of procedures by name
|
|
58
|
+
*/
|
|
59
|
+
export type ImplementationsMap<Procedures extends ProceduresMap> = {
|
|
60
|
+
[F in keyof Procedures]: ProcedureImplementation<Procedures[F]["input"], Procedures[F]["progress"], Procedures[F]["success"]>;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Declaration of hooks to run on messages received from the server
|
|
64
|
+
*/
|
|
65
|
+
export type Hooks<Procedures extends ProceduresMap> = {
|
|
66
|
+
/**
|
|
67
|
+
* Called when a procedure call has been successful.
|
|
68
|
+
*/
|
|
69
|
+
success?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, data: Procedures[Procedure]["success"]["inferOut"]) => void;
|
|
70
|
+
/**
|
|
71
|
+
* Called when a procedure call has failed.
|
|
72
|
+
*/
|
|
73
|
+
error?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, error: Error) => void;
|
|
74
|
+
/**
|
|
75
|
+
* Called when a procedure call sends progress updates.
|
|
76
|
+
*/
|
|
77
|
+
progress?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, data: Procedures[Procedure]["progress"]["inferOut"]) => void;
|
|
78
|
+
};
|
|
79
|
+
export declare const PayloadHeaderSchema: import("arktype").Generic<[["Name", string]], {
|
|
80
|
+
readonly by: "\"sw&rpc\"";
|
|
81
|
+
readonly functionName: "Name";
|
|
82
|
+
readonly requestId: "string >= 1";
|
|
83
|
+
}, {}, {}>;
|
|
84
|
+
export type PayloadHeader<PM extends ProceduresMap, Name extends keyof PM = keyof PM> = {
|
|
85
|
+
by: "sw&rpc";
|
|
86
|
+
functionName: Name & string;
|
|
87
|
+
requestId: string;
|
|
88
|
+
};
|
|
89
|
+
export declare const PayloadCoreSchema: import("arktype").Generic<[["I", unknown], ["P", unknown], ["S", unknown]], {
|
|
90
|
+
readonly "input?": "I";
|
|
91
|
+
readonly "progress?": "P";
|
|
92
|
+
readonly "result?": "S";
|
|
93
|
+
readonly "abort?": {
|
|
94
|
+
readonly reason: "string";
|
|
95
|
+
};
|
|
96
|
+
readonly "error?": {
|
|
97
|
+
readonly message: "string";
|
|
98
|
+
};
|
|
99
|
+
}, {}, {}>;
|
|
100
|
+
export type PayloadCore<PM extends ProceduresMap, Name extends keyof PM = keyof PM> = {
|
|
101
|
+
input: PM[Name]["input"]["inferOut"];
|
|
102
|
+
} | {
|
|
103
|
+
progress: PM[Name]["progress"]["inferOut"];
|
|
104
|
+
} | {
|
|
105
|
+
result: PM[Name]["success"]["inferOut"];
|
|
106
|
+
} | {
|
|
107
|
+
abort: {
|
|
108
|
+
reason: string;
|
|
109
|
+
};
|
|
110
|
+
} | {
|
|
111
|
+
error: {
|
|
112
|
+
message: string;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
export declare const PayloadSchema: import("arktype").Generic<[["Name", string], ["I", unknown], ["P", unknown], ["S", unknown]], readonly ["PayloadHeaderSchema<Name>", "&", "PayloadCoreSchema<I, P, S>"], {
|
|
116
|
+
PayloadCoreSchema: import("arktype/internal/scope.ts").bindGenericToScope<import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
|
|
117
|
+
readonly "input?": "I";
|
|
118
|
+
readonly "progress?": "P";
|
|
119
|
+
readonly "result?": "S";
|
|
120
|
+
readonly "abort?": {
|
|
121
|
+
readonly reason: "string";
|
|
122
|
+
};
|
|
123
|
+
readonly "error?": {
|
|
124
|
+
readonly message: "string";
|
|
125
|
+
};
|
|
126
|
+
}, {}, {}>, {
|
|
127
|
+
PayloadCoreSchema: import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
|
|
128
|
+
readonly "input?": "I";
|
|
129
|
+
readonly "progress?": "P";
|
|
130
|
+
readonly "result?": "S";
|
|
131
|
+
readonly "abort?": {
|
|
132
|
+
readonly reason: "string";
|
|
133
|
+
};
|
|
134
|
+
readonly "error?": {
|
|
135
|
+
readonly message: "string";
|
|
136
|
+
};
|
|
137
|
+
}, {}, {}>;
|
|
138
|
+
PayloadHeaderSchema: import("@ark/schema").GenericAst<[["Name", string]], {
|
|
139
|
+
readonly by: "\"sw&rpc\"";
|
|
140
|
+
readonly functionName: "Name";
|
|
141
|
+
readonly requestId: "string >= 1";
|
|
142
|
+
}, {}, {}>;
|
|
143
|
+
} & {}>;
|
|
144
|
+
PayloadHeaderSchema: import("arktype/internal/scope.ts").bindGenericToScope<import("@ark/schema").GenericAst<[["Name", string]], {
|
|
145
|
+
readonly by: "\"sw&rpc\"";
|
|
146
|
+
readonly functionName: "Name";
|
|
147
|
+
readonly requestId: "string >= 1";
|
|
148
|
+
}, {}, {}>, {
|
|
149
|
+
PayloadCoreSchema: import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
|
|
150
|
+
readonly "input?": "I";
|
|
151
|
+
readonly "progress?": "P";
|
|
152
|
+
readonly "result?": "S";
|
|
153
|
+
readonly "abort?": {
|
|
154
|
+
readonly reason: "string";
|
|
155
|
+
};
|
|
156
|
+
readonly "error?": {
|
|
157
|
+
readonly message: "string";
|
|
158
|
+
};
|
|
159
|
+
}, {}, {}>;
|
|
160
|
+
PayloadHeaderSchema: import("@ark/schema").GenericAst<[["Name", string]], {
|
|
161
|
+
readonly by: "\"sw&rpc\"";
|
|
162
|
+
readonly functionName: "Name";
|
|
163
|
+
readonly requestId: "string >= 1";
|
|
164
|
+
}, {}, {}>;
|
|
165
|
+
} & {}>;
|
|
166
|
+
}, {
|
|
167
|
+
PayloadCoreSchema: import("arktype/internal/scope.ts").bindGenericToScope<import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
|
|
168
|
+
readonly "input?": "I";
|
|
169
|
+
readonly "progress?": "P";
|
|
170
|
+
readonly "result?": "S";
|
|
171
|
+
readonly "abort?": {
|
|
172
|
+
readonly reason: "string";
|
|
173
|
+
};
|
|
174
|
+
readonly "error?": {
|
|
175
|
+
readonly message: "string";
|
|
176
|
+
};
|
|
177
|
+
}, {}, {}>, {
|
|
178
|
+
PayloadCoreSchema: import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
|
|
179
|
+
readonly "input?": "I";
|
|
180
|
+
readonly "progress?": "P";
|
|
181
|
+
readonly "result?": "S";
|
|
182
|
+
readonly "abort?": {
|
|
183
|
+
readonly reason: "string";
|
|
184
|
+
};
|
|
185
|
+
readonly "error?": {
|
|
186
|
+
readonly message: "string";
|
|
187
|
+
};
|
|
188
|
+
}, {}, {}>;
|
|
189
|
+
PayloadHeaderSchema: import("@ark/schema").GenericAst<[["Name", string]], {
|
|
190
|
+
readonly by: "\"sw&rpc\"";
|
|
191
|
+
readonly functionName: "Name";
|
|
192
|
+
readonly requestId: "string >= 1";
|
|
193
|
+
}, {}, {}>;
|
|
194
|
+
} & {}>;
|
|
195
|
+
PayloadHeaderSchema: import("arktype/internal/scope.ts").bindGenericToScope<import("@ark/schema").GenericAst<[["Name", string]], {
|
|
196
|
+
readonly by: "\"sw&rpc\"";
|
|
197
|
+
readonly functionName: "Name";
|
|
198
|
+
readonly requestId: "string >= 1";
|
|
199
|
+
}, {}, {}>, {
|
|
200
|
+
PayloadCoreSchema: import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
|
|
201
|
+
readonly "input?": "I";
|
|
202
|
+
readonly "progress?": "P";
|
|
203
|
+
readonly "result?": "S";
|
|
204
|
+
readonly "abort?": {
|
|
205
|
+
readonly reason: "string";
|
|
206
|
+
};
|
|
207
|
+
readonly "error?": {
|
|
208
|
+
readonly message: "string";
|
|
209
|
+
};
|
|
210
|
+
}, {}, {}>;
|
|
211
|
+
PayloadHeaderSchema: import("@ark/schema").GenericAst<[["Name", string]], {
|
|
212
|
+
readonly by: "\"sw&rpc\"";
|
|
213
|
+
readonly functionName: "Name";
|
|
214
|
+
readonly requestId: "string >= 1";
|
|
215
|
+
}, {}, {}>;
|
|
216
|
+
} & {}>;
|
|
217
|
+
}>;
|
|
218
|
+
/**
|
|
219
|
+
* The effective payload as sent by the server to the client
|
|
220
|
+
*/
|
|
221
|
+
export type Payload<PM extends ProceduresMap, Name extends keyof PM = keyof PM> = PayloadHeader<PM, Name> & PayloadCore<PM, Name>;
|
|
222
|
+
/**
|
|
223
|
+
* 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.
|
|
224
|
+
*/
|
|
225
|
+
export type ClientMethod<P extends Procedure<Type, Type, Type>> = ((input: P["input"]["inferIn"], onProgress?: (progress: P["progress"]["inferOut"]) => void) => Promise<P["success"]["inferOut"]>) & {
|
|
226
|
+
/**
|
|
227
|
+
* 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.
|
|
228
|
+
*/
|
|
229
|
+
cancelable: (input: P["input"]["inferIn"], onProgress?: (progress: P["progress"]["inferOut"]) => void, requestId?: string) => CancelablePromise<P["success"]["inferOut"]>;
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* Symbol used as the key for the procedures map on the server instance
|
|
233
|
+
*/
|
|
234
|
+
export declare const zImplementations: unique symbol;
|
|
235
|
+
/**
|
|
236
|
+
* Symbol used as the key for the procedures map on instances
|
|
237
|
+
*/
|
|
238
|
+
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
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type } from "arktype";
|
|
2
|
+
export const PayloadHeaderSchema = type("<Name extends string>", {
|
|
3
|
+
by: '"sw&rpc"',
|
|
4
|
+
functionName: "Name",
|
|
5
|
+
requestId: "string >= 1",
|
|
6
|
+
});
|
|
7
|
+
export const PayloadCoreSchema = type("<I, P, S>", {
|
|
8
|
+
"input?": "I",
|
|
9
|
+
"progress?": "P",
|
|
10
|
+
"result?": "S",
|
|
11
|
+
"abort?": { reason: "string" },
|
|
12
|
+
"error?": { message: "string" },
|
|
13
|
+
});
|
|
14
|
+
export const PayloadSchema = type
|
|
15
|
+
.scope({ PayloadCoreSchema, PayloadHeaderSchema })
|
|
16
|
+
.type("<Name extends string, I, P, S>", [
|
|
17
|
+
"PayloadHeaderSchema<Name>",
|
|
18
|
+
"&",
|
|
19
|
+
"PayloadCoreSchema<I, P, S>",
|
|
20
|
+
]);
|
|
21
|
+
/**
|
|
22
|
+
* Symbol used as the key for the procedures map on the server instance
|
|
23
|
+
*/
|
|
24
|
+
export const zImplementations = Symbol("SWARPC implementations");
|
|
25
|
+
/**
|
|
26
|
+
* Symbol used as the key for the procedures map on instances
|
|
27
|
+
*/
|
|
28
|
+
export const zProcedures = Symbol("SWARPC procedures");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAWA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,GAAG,GAAG,YAAY,EAAE,CAsB5D"}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
// TODO: keep it in sync with web standards, how?
|
|
2
2
|
const transferableClasses = [
|
|
3
|
-
OffscreenCanvas,
|
|
4
|
-
ImageBitmap,
|
|
5
3
|
MessagePort,
|
|
6
|
-
MediaSourceHandle,
|
|
7
4
|
ReadableStream,
|
|
8
5
|
WritableStream,
|
|
9
6
|
TransformStream,
|
|
10
|
-
AudioData,
|
|
11
|
-
VideoFrame,
|
|
12
|
-
RTCDataChannel,
|
|
13
7
|
ArrayBuffer,
|
|
14
8
|
];
|
|
15
9
|
export function findTransferables(value) {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export declare const procedures: {
|
|
2
|
+
readonly hello: {
|
|
3
|
+
readonly input: import("arktype/internal/methods/string.ts").StringType<string, {}>;
|
|
4
|
+
readonly progress: import("arktype").BaseType<undefined, {}>;
|
|
5
|
+
readonly success: import("arktype/internal/methods/string.ts").StringType<string, {}>;
|
|
6
|
+
};
|
|
7
|
+
readonly helloWithProgress: {
|
|
8
|
+
readonly input: import("arktype/internal/methods/string.ts").StringType<string, {}>;
|
|
9
|
+
readonly progress: import("arktype/internal/methods/object.ts").ObjectType<{
|
|
10
|
+
current: number;
|
|
11
|
+
total: number;
|
|
12
|
+
}, {}>;
|
|
13
|
+
readonly success: import("arktype/internal/methods/string.ts").StringType<string, {}>;
|
|
14
|
+
};
|
|
15
|
+
readonly cancellable: {
|
|
16
|
+
readonly input: import("arktype/internal/methods/string.ts").StringType<string, {}>;
|
|
17
|
+
readonly progress: import("arktype/internal/methods/object.ts").ObjectType<{
|
|
18
|
+
current: number;
|
|
19
|
+
total: number;
|
|
20
|
+
}, {}>;
|
|
21
|
+
readonly success: import("arktype/internal/methods/string.ts").StringType<string, {}>;
|
|
22
|
+
};
|
|
23
|
+
readonly complexData: {
|
|
24
|
+
readonly input: import("arktype/internal/methods/object.ts").ObjectType<{
|
|
25
|
+
name: string;
|
|
26
|
+
age: number;
|
|
27
|
+
custom: (In: string) => import("arktype/internal/attributes.ts").To<import("@ark/util").Json>;
|
|
28
|
+
hobbies: import("arktype/internal/attributes.ts").Default<string[], never[]>;
|
|
29
|
+
address: (In: {
|
|
30
|
+
street: string;
|
|
31
|
+
city: string;
|
|
32
|
+
zip: string;
|
|
33
|
+
houseno?: number | undefined;
|
|
34
|
+
}) => import("arktype").Out<string>;
|
|
35
|
+
}, {}>;
|
|
36
|
+
readonly progress: import("arktype/internal/methods/object.ts").ObjectType<{
|
|
37
|
+
message: string;
|
|
38
|
+
percent: number;
|
|
39
|
+
}, {}>;
|
|
40
|
+
readonly success: import("arktype/internal/methods/object.ts").ObjectType<{
|
|
41
|
+
message: string;
|
|
42
|
+
}, {}>;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=core.procedures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.procedures.d.ts","sourceRoot":"","sources":["../../tests/core.procedures.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDW,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type } from "arktype";
|
|
2
|
+
export const procedures = {
|
|
3
|
+
hello: {
|
|
4
|
+
input: type("string"),
|
|
5
|
+
progress: type("undefined"),
|
|
6
|
+
success: type("string"),
|
|
7
|
+
},
|
|
8
|
+
helloWithProgress: {
|
|
9
|
+
input: type("string"),
|
|
10
|
+
progress: type({
|
|
11
|
+
current: "number",
|
|
12
|
+
total: "number",
|
|
13
|
+
}),
|
|
14
|
+
success: type("string"),
|
|
15
|
+
},
|
|
16
|
+
cancellable: {
|
|
17
|
+
input: type("string"),
|
|
18
|
+
progress: type({
|
|
19
|
+
current: "number",
|
|
20
|
+
total: "number",
|
|
21
|
+
}),
|
|
22
|
+
success: type("string"),
|
|
23
|
+
},
|
|
24
|
+
complexData: {
|
|
25
|
+
input: type({
|
|
26
|
+
name: "string",
|
|
27
|
+
age: "number",
|
|
28
|
+
custom: "string.json.parse",
|
|
29
|
+
hobbies: type("string[]").default(() => []),
|
|
30
|
+
address: type([
|
|
31
|
+
{
|
|
32
|
+
"houseno?": "number",
|
|
33
|
+
street: "string",
|
|
34
|
+
city: "string",
|
|
35
|
+
zip: "string",
|
|
36
|
+
},
|
|
37
|
+
"=>",
|
|
38
|
+
(address) => `${address.houseno ? address.houseno + " " : ""}${address.street}, ${address.city} ${address.zip}`,
|
|
39
|
+
]),
|
|
40
|
+
}),
|
|
41
|
+
progress: type({
|
|
42
|
+
message: "string",
|
|
43
|
+
percent: "number",
|
|
44
|
+
}),
|
|
45
|
+
success: type({
|
|
46
|
+
message: "string",
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.test.d.ts","sourceRoot":"","sources":["../../tests/core.test.ts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAA"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import "@vitest/web-worker";
|
|
2
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
3
|
+
import { Client } from "../src/index.js";
|
|
4
|
+
import { procedures } from "./core.procedures.js";
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
import Worker from "./core.worker.js?worker";
|
|
7
|
+
const client = Client(procedures, { worker: new Worker(), loglevel: "warn" });
|
|
8
|
+
test("Simple exchange", async () => {
|
|
9
|
+
const answer = await client.hello("world");
|
|
10
|
+
expect(answer).toBe("Hello world");
|
|
11
|
+
});
|
|
12
|
+
test("Progress info", async () => {
|
|
13
|
+
const callback = vi.fn();
|
|
14
|
+
const answer = await client.helloWithProgress("world", callback);
|
|
15
|
+
expect(answer).toBe("Hello with progress world");
|
|
16
|
+
expect(callback.mock.calls).toStrictEqual([
|
|
17
|
+
[{ current: 0, total: 10 }],
|
|
18
|
+
[{ current: 1, total: 10 }],
|
|
19
|
+
[{ current: 2, total: 10 }],
|
|
20
|
+
[{ current: 3, total: 10 }],
|
|
21
|
+
[{ current: 4, total: 10 }],
|
|
22
|
+
[{ current: 5, total: 10 }],
|
|
23
|
+
[{ current: 6, total: 10 }],
|
|
24
|
+
[{ current: 7, total: 10 }],
|
|
25
|
+
[{ current: 8, total: 10 }],
|
|
26
|
+
[{ current: 9, total: 10 }],
|
|
27
|
+
]);
|
|
28
|
+
});
|
|
29
|
+
describe("Cancellable procedure", () => {
|
|
30
|
+
const progress = vi.fn();
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
progress.mockReset();
|
|
33
|
+
});
|
|
34
|
+
test("Can be cancelled", async () => {
|
|
35
|
+
const { cancel } = client.cancellable.cancelable("test", progress);
|
|
36
|
+
await new Promise((resolve) => setTimeout(resolve, 2));
|
|
37
|
+
await cancel("test cancellation");
|
|
38
|
+
expect(progress.mock.calls.length).toBeLessThan(10);
|
|
39
|
+
});
|
|
40
|
+
test("Can be completed", async () => {
|
|
41
|
+
const { request } = client.cancellable.cancelable("test", progress);
|
|
42
|
+
const answer = await request;
|
|
43
|
+
expect(answer).toBe("Cancellable hello test");
|
|
44
|
+
expect(progress.mock.calls.length).toBe(10);
|
|
45
|
+
});
|
|
46
|
+
test("Can be run normally", async () => {
|
|
47
|
+
const answer = await client.cancellable("test", progress);
|
|
48
|
+
expect(answer).toBe("Cancellable hello test");
|
|
49
|
+
expect(progress.mock.calls.length).toBe(10);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("Complex data", () => {
|
|
53
|
+
test("Processes complex data", async () => {
|
|
54
|
+
const response = await client.complexData({
|
|
55
|
+
name: "test",
|
|
56
|
+
custom: '{"key": "value"}',
|
|
57
|
+
address: {
|
|
58
|
+
street: "Main St",
|
|
59
|
+
city: "Testville",
|
|
60
|
+
zip: "12345",
|
|
61
|
+
houseno: 42,
|
|
62
|
+
},
|
|
63
|
+
age: 30,
|
|
64
|
+
});
|
|
65
|
+
expect(response).toEqual({
|
|
66
|
+
message: "Processed data for test",
|
|
67
|
+
addr: "42 Main St, Testville 12345",
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
test("Handles missing optional fields", async () => {
|
|
71
|
+
const response = await client.complexData({
|
|
72
|
+
name: "test",
|
|
73
|
+
custom: '{"key": "value"}',
|
|
74
|
+
address: {
|
|
75
|
+
street: "Main St",
|
|
76
|
+
city: "Testville",
|
|
77
|
+
zip: "12345",
|
|
78
|
+
},
|
|
79
|
+
age: 30,
|
|
80
|
+
});
|
|
81
|
+
expect(response).toEqual({
|
|
82
|
+
message: "Processed data for test",
|
|
83
|
+
addr: "Main St, Testville 12345",
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
test("Errors on invalid data", async () => {
|
|
87
|
+
await expect(client.complexData({
|
|
88
|
+
name: "test",
|
|
89
|
+
custom: '{"key": "value"}',
|
|
90
|
+
address: {
|
|
91
|
+
street: "Main St",
|
|
92
|
+
city: "Testville",
|
|
93
|
+
zip: "12345",
|
|
94
|
+
// @ts-expect-error
|
|
95
|
+
houseno: "not-a-number", // Invalid type
|
|
96
|
+
},
|
|
97
|
+
age: 30,
|
|
98
|
+
})).rejects.toThrowErrorMatchingInlineSnapshot(`[TraversalError: address.houseno must be a number (was a string)]`);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.worker.d.ts","sourceRoot":"","sources":["../../tests/core.worker.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Server } from "../src/index.js";
|
|
2
|
+
import { procedures } from "./core.procedures.js";
|
|
3
|
+
const server = Server(procedures, { worker: self, loglevel: "warn" });
|
|
4
|
+
server.hello(async (input) => {
|
|
5
|
+
return `Hello ${input}`;
|
|
6
|
+
});
|
|
7
|
+
server.helloWithProgress(async (input, onProgress) => {
|
|
8
|
+
for (let i = 0; i < 10; i++) {
|
|
9
|
+
onProgress({ current: i, total: 10 });
|
|
10
|
+
}
|
|
11
|
+
return `Hello with progress ${input}`;
|
|
12
|
+
});
|
|
13
|
+
server.cancellable(async (input, onProgress, abortSignal) => {
|
|
14
|
+
let aborted = false;
|
|
15
|
+
abortSignal?.addEventListener("abort", () => {
|
|
16
|
+
aborted = true;
|
|
17
|
+
});
|
|
18
|
+
for (let i = 0; i < 10; i++) {
|
|
19
|
+
if (aborted)
|
|
20
|
+
return "cancelled";
|
|
21
|
+
onProgress({ current: i, total: 10 });
|
|
22
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
23
|
+
}
|
|
24
|
+
return `Cancellable hello ${input}`;
|
|
25
|
+
});
|
|
26
|
+
server.complexData(async (input, onProgress) => {
|
|
27
|
+
onProgress({ message: "Processing data", percent: 50 });
|
|
28
|
+
return { message: `Processed data for ${input.name}`, addr: input.address };
|
|
29
|
+
});
|
|
30
|
+
server.start(self);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.config.d.ts","sourceRoot":"","sources":["../vite.config.ts"],"names":[],"mappings":";AAEA,wBAKE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swarpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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",
|
|
@@ -24,22 +24,26 @@
|
|
|
24
24
|
"dist",
|
|
25
25
|
"src"
|
|
26
26
|
],
|
|
27
|
-
"main": "dist/
|
|
28
|
-
"types": "dist/
|
|
27
|
+
"main": "dist/index.js",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "tsc",
|
|
31
31
|
"dev": "tsc --watch",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "vitest",
|
|
34
|
+
"typedoc": "typedoc src/index.ts src/types.ts --readme README.md",
|
|
34
35
|
"version": "kacl release && prettier -w CHANGELOG.md && git add CHANGELOG.md"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
37
38
|
"arktype": "^2.1.20"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
41
|
+
"@vitest/web-worker": "^3.2.4",
|
|
40
42
|
"kacl": "^1.1.1",
|
|
41
43
|
"prettier": "^3.6.2",
|
|
42
44
|
"typedoc": "^0.28.7",
|
|
43
|
-
"typescript": "^5.8.3"
|
|
45
|
+
"typescript": "^5.8.3",
|
|
46
|
+
"vite": "^7.0.6",
|
|
47
|
+
"vitest": "^3.2.4"
|
|
44
48
|
}
|
|
45
49
|
}
|