@kirill.konshin/utils 0.0.14 → 0.0.15
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/.ctirc +2 -1
- package/.turbo/turbo-build.log +18 -13
- package/.turbo/turbo-test.log +78 -25
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/demo/{cache-demo-clean.ts → worker-demo.ts} +1 -1
- package/dist/bootstrap/controls.d.ts +5 -2
- package/dist/bootstrap/controls.d.ts.map +1 -1
- package/dist/bootstrap/controls.js +1 -0
- package/dist/bootstrap/controls.js.map +1 -1
- package/dist/bootstrap/controls.stories.d.ts +9 -0
- package/dist/bootstrap/controls.stories.d.ts.map +1 -0
- package/dist/bootstrap/error.stories.d.ts +8 -0
- package/dist/bootstrap/error.stories.d.ts.map +1 -0
- package/dist/bootstrap/field.stories.d.ts +8 -0
- package/dist/bootstrap/field.stories.d.ts.map +1 -0
- package/dist/bootstrap/globalLoading.stories.d.ts +7 -0
- package/dist/bootstrap/globalLoading.stories.d.ts.map +1 -0
- package/dist/bootstrap/index.js +2 -1
- package/dist/bootstrap/loading.stories.d.ts +9 -0
- package/dist/bootstrap/loading.stories.d.ts.map +1 -0
- package/dist/bootstrap/screen.stories.d.ts +14 -0
- package/dist/bootstrap/screen.stories.d.ts.map +1 -0
- package/dist/bootstrap/toaster.stories.d.ts +8 -0
- package/dist/bootstrap/toaster.stories.d.ts.map +1 -0
- package/dist/bootstrap/useModal.stories.d.ts +9 -0
- package/dist/bootstrap/useModal.stories.d.ts.map +1 -0
- package/dist/core/cache.d.ts +7 -8
- package/dist/core/cache.d.ts.map +1 -1
- package/dist/core/cache.js +7 -4
- package/dist/core/cache.js.map +1 -1
- package/dist/core/cache.test.d.ts +2 -0
- package/dist/core/cache.test.d.ts.map +1 -0
- package/dist/core/files.d.ts +3 -0
- package/dist/core/files.d.ts.map +1 -0
- package/dist/core/files.js +27 -0
- package/dist/core/files.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +14 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/ls.d.ts +6 -0
- package/dist/core/ls.d.ts.map +1 -0
- package/dist/core/ls.js +12 -0
- package/dist/core/ls.js.map +1 -0
- package/dist/core/worker.d.ts +23 -9
- package/dist/core/worker.d.ts.map +1 -1
- package/dist/core/worker.js +93 -90
- package/dist/core/worker.js.map +1 -1
- package/dist/core/worker.test.d.ts +2 -0
- package/dist/core/worker.test.d.ts.map +1 -0
- package/dist/core/workerUtils.d.ts +3 -0
- package/dist/core/workerUtils.d.ts.map +1 -0
- package/dist/core/workerUtils.js +31 -0
- package/dist/core/workerUtils.js.map +1 -0
- package/dist/core/wrpc.d.ts +45 -0
- package/dist/core/wrpc.d.ts.map +1 -0
- package/dist/core/wrpc.fixture.d.ts +29 -0
- package/dist/core/wrpc.fixture.d.ts.map +1 -0
- package/dist/core/wrpc.js +242 -0
- package/dist/core/wrpc.js.map +1 -0
- package/dist/core/wrpc.test.d.ts +1 -0
- package/dist/core/wrpc.test.d.ts.map +1 -0
- package/dist/mui/formControlFieldset.stories.d.ts +7 -0
- package/dist/mui/formControlFieldset.stories.d.ts.map +1 -0
- package/dist/react/form/form.d.ts +7 -6
- package/dist/react/form/form.d.ts.map +1 -1
- package/dist/react/form/form.js +14 -20
- package/dist/react/form/form.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +8 -2
- package/dist/react/index.js.map +1 -1
- package/dist/react/keyboard.d.ts +11 -0
- package/dist/react/keyboard.d.ts.map +1 -0
- package/dist/react/keyboard.js +38 -0
- package/dist/react/keyboard.js.map +1 -0
- package/package.json +5 -2
- package/src/bootstrap/controls.stories.tsx +2 -2
- package/src/bootstrap/controls.tsx +3 -3
- package/src/bootstrap/error.stories.tsx +3 -3
- package/src/bootstrap/field.stories.tsx +2 -2
- package/src/bootstrap/globalLoading.stories.tsx +2 -2
- package/src/bootstrap/loading.stories.tsx +2 -2
- package/src/bootstrap/screen.stories.tsx +2 -2
- package/src/bootstrap/toaster.stories.tsx +2 -2
- package/src/bootstrap/useModal.stories.tsx +3 -3
- package/src/core/README.md +91 -0
- package/src/core/cache.ts +12 -12
- package/src/core/files.ts +24 -0
- package/src/core/index.ts +4 -0
- package/src/core/ls.ts +11 -0
- package/src/core/worker.test.ts +197 -0
- package/src/core/worker.ts +106 -93
- package/src/core/workerUtils.ts +32 -0
- package/src/core/wrpc.fixture.ts +72 -0
- package/src/core/wrpc.test.ts +225 -0
- package/src/core/wrpc.ts +359 -0
- package/src/mui/formControlFieldset.stories.tsx +1 -1
- package/src/react/form/form.tsx +53 -33
- package/src/react/index.ts +1 -0
- package/src/react/keyboard.tsx +49 -0
- package/src/tailwind/README.md +8 -0
- package/tsconfig.json +2 -1
- package/vite.config.ts +10 -3
- package/vite.exports.ts +3 -3
- package/demo/cache-demo.ts +0 -343
package/src/core/worker.ts
CHANGED
|
@@ -1,36 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
return transferrable.some((t) => obj instanceof t) ? obj : null;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function getTransferrable(data: any = {}): Transferable[] {
|
|
6
|
-
if (!data) return [];
|
|
7
|
-
return Object.values<any>(data)
|
|
8
|
-
.reduce((r, v) => {
|
|
9
|
-
return Array.isArray(v) ? [...r, ...v.map(isTransferable)] : [...r, isTransferable(v)];
|
|
10
|
-
}, [])
|
|
11
|
-
.filter(Boolean);
|
|
12
|
-
}
|
|
1
|
+
import { getTransferrable } from './workerUtils';
|
|
13
2
|
|
|
14
|
-
//
|
|
15
|
-
const transferrable =
|
|
16
|
-
typeof window !== 'undefined'
|
|
17
|
-
? [
|
|
18
|
-
ArrayBuffer,
|
|
19
|
-
MessagePort,
|
|
20
|
-
ReadableStream,
|
|
21
|
-
WritableStream,
|
|
22
|
-
TransformStream,
|
|
23
|
-
// WebTransportReceiveStream, // ReferenceError: WebTransportReceiveStream is not defined
|
|
24
|
-
// WebTransportSendStream, // ReferenceError: WebTransportReceiveStream is not defined
|
|
25
|
-
// AudioData, // TS2304: Cannot find name AudioData
|
|
26
|
-
ImageBitmap,
|
|
27
|
-
VideoFrame,
|
|
28
|
-
OffscreenCanvas,
|
|
29
|
-
RTCDataChannel,
|
|
30
|
-
MediaSourceHandle,
|
|
31
|
-
// MIDIAccess, // ReferenceError: MIDIAccess is not defined
|
|
32
|
-
].filter(Boolean)
|
|
33
|
-
: [];
|
|
3
|
+
// Types
|
|
34
4
|
|
|
35
5
|
type WebWorker = Worker | (Window & typeof globalThis) | (WorkerGlobalScope & typeof globalThis);
|
|
36
6
|
|
|
@@ -44,6 +14,7 @@ export type Ctx<R, K extends keyof R> = {
|
|
|
44
14
|
id: string;
|
|
45
15
|
parentId?: string;
|
|
46
16
|
message: K;
|
|
17
|
+
done: boolean;
|
|
47
18
|
};
|
|
48
19
|
|
|
49
20
|
type Data<R, M extends keyof R> = MethodReturn<R, M> | MethodArg<R, M> | Error;
|
|
@@ -56,35 +27,37 @@ type Listener<R, M extends keyof R, D extends Data<R, M> = Data<R, M>, S extends
|
|
|
56
27
|
event: Event<R, M>,
|
|
57
28
|
) => void | Promise<void>;
|
|
58
29
|
|
|
59
|
-
|
|
30
|
+
// Constants
|
|
60
31
|
|
|
61
|
-
|
|
32
|
+
const ALL = '*';
|
|
62
33
|
|
|
63
|
-
|
|
64
|
-
return (++id).toString();
|
|
65
|
-
}
|
|
34
|
+
const RESPONDER = Symbol('responder');
|
|
66
35
|
|
|
67
36
|
function checkClosed(obj: any, label: string = 'external') {
|
|
68
|
-
if (obj?.closed) throw new Error(`Context
|
|
37
|
+
if (obj?.closed) throw new Error(`Context ${obj.id} is closed (${label})`, { cause: obj });
|
|
69
38
|
}
|
|
70
39
|
|
|
71
40
|
function checkMessage(obj: any, message: any) {
|
|
72
41
|
if (!message) throw new Error('New context must have message', { cause: obj });
|
|
73
42
|
}
|
|
74
43
|
|
|
44
|
+
// Classes
|
|
45
|
+
|
|
75
46
|
export class WorkerDialog<R extends RespondersBase<any>> {
|
|
76
47
|
readonly contexts = new Set<Context<R, keyof R>>();
|
|
77
48
|
protected closed = false;
|
|
49
|
+
protected currentId = 0; // IDs are intentionally not global
|
|
78
50
|
|
|
79
51
|
constructor(
|
|
80
52
|
public worker: WebWorker,
|
|
81
53
|
public responders: R,
|
|
82
54
|
public name: string,
|
|
55
|
+
public debug: number = 0, // 0 - none, 1 - general messages, 2 - sub/unsub, 3 - transport TODO Object?
|
|
83
56
|
) {
|
|
84
57
|
if (!this.worker) return; // Next.js SSR
|
|
85
58
|
|
|
86
59
|
for (const message in this.responders) {
|
|
87
|
-
if ([
|
|
60
|
+
if (!this.responders?.[message]?.[RESPONDER]) continue;
|
|
88
61
|
|
|
89
62
|
// root context with id=all
|
|
90
63
|
const rootContext = new ResponseContext<R, typeof message>(this, message, ALL);
|
|
@@ -94,33 +67,40 @@ export class WorkerDialog<R extends RespondersBase<any>> {
|
|
|
94
67
|
// create sub context with ID
|
|
95
68
|
const responseContext = context.withMessage(message, event.data.ctx.id);
|
|
96
69
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
70
|
+
/* v8 ignore start */
|
|
71
|
+
if (this.debug)
|
|
72
|
+
console.log('Root Listener', rootContext.info, {
|
|
73
|
+
rootContext: context,
|
|
74
|
+
data,
|
|
75
|
+
responseContext,
|
|
76
|
+
event,
|
|
77
|
+
responder: this.responders[message],
|
|
78
|
+
});
|
|
79
|
+
/* v8 ignore stop */
|
|
104
80
|
|
|
105
81
|
try {
|
|
106
|
-
responseContext.send(await (this.responders[message] as any)(data, responseContext));
|
|
82
|
+
responseContext.send(await (this.responders[message] as any)(data, responseContext), true);
|
|
107
83
|
responseContext.close('root.listener');
|
|
108
84
|
} catch (e) {
|
|
109
|
-
|
|
110
|
-
responseContext.
|
|
85
|
+
/* v8 ignore next */
|
|
86
|
+
if (this.debug) console.error(`Error in responder ${responseContext.info}`, { e });
|
|
87
|
+
responseContext.send(e, true);
|
|
111
88
|
}
|
|
112
|
-
});
|
|
89
|
+
}, true);
|
|
113
90
|
}
|
|
114
91
|
}
|
|
115
92
|
|
|
93
|
+
getID() {
|
|
94
|
+
return (++this.currentId).toString();
|
|
95
|
+
}
|
|
96
|
+
|
|
116
97
|
close() {
|
|
117
98
|
for (const context of this.contexts) context.close('root');
|
|
118
99
|
this.contexts.clear();
|
|
119
|
-
this.worker.onmessage = null;
|
|
120
100
|
}
|
|
121
101
|
|
|
122
102
|
withMessage<M extends keyof R>(message: M) {
|
|
123
|
-
return new RequestContext<R, M>(this, message, getID());
|
|
103
|
+
return new RequestContext<R, M>(this, message, this.getID()); // always new context
|
|
124
104
|
}
|
|
125
105
|
}
|
|
126
106
|
|
|
@@ -129,6 +109,7 @@ export class RespondersBase<R> {
|
|
|
129
109
|
message: M,
|
|
130
110
|
responder: F,
|
|
131
111
|
): F {
|
|
112
|
+
responder[RESPONDER] = true;
|
|
132
113
|
return responder as any;
|
|
133
114
|
}
|
|
134
115
|
}
|
|
@@ -144,17 +125,19 @@ abstract class Context<R, M extends keyof R> {
|
|
|
144
125
|
public id: string | undefined,
|
|
145
126
|
public parent: Context<R, keyof R> | undefined | null = null,
|
|
146
127
|
) {
|
|
147
|
-
checkClosed(
|
|
148
|
-
checkClosed(
|
|
128
|
+
checkClosed(dialog, 'newContext');
|
|
129
|
+
checkClosed(parent, 'newContext');
|
|
149
130
|
checkMessage(this, message);
|
|
150
|
-
this.id =
|
|
131
|
+
this.id = id ?? parent?.id;
|
|
151
132
|
if (!this.id) throw new Error('ID is required', { cause: this });
|
|
133
|
+
this.dialog.contexts.add(this);
|
|
152
134
|
}
|
|
153
135
|
|
|
154
136
|
abstract withMessage<M2 extends keyof R>(message: M2, id?: string): Context<R, M2>;
|
|
155
137
|
|
|
156
138
|
close(reason: string = 'external') {
|
|
157
|
-
|
|
139
|
+
/* v8 ignore next */
|
|
140
|
+
if (this.dialog.debug >= 2) console.log('Closing', this.info, reason, this);
|
|
158
141
|
|
|
159
142
|
this.dialog.contexts.delete(this);
|
|
160
143
|
|
|
@@ -171,33 +154,44 @@ abstract class Context<R, M extends keyof R> {
|
|
|
171
154
|
}
|
|
172
155
|
|
|
173
156
|
get info() {
|
|
174
|
-
return this.dialog.name
|
|
157
|
+
return `${this.dialog.name}:${this.parent?.id ?? ALL}/${this.id}:${this.message.toString()}[${this.closed ? 'closed' : 'open'},${this.type}]`;
|
|
175
158
|
}
|
|
176
159
|
|
|
177
|
-
|
|
160
|
+
get debugInfo() {
|
|
161
|
+
return {
|
|
162
|
+
id: this.id,
|
|
163
|
+
parentId: this.parent?.id,
|
|
164
|
+
message: this.message,
|
|
165
|
+
closed: this.closed,
|
|
166
|
+
type: this.type,
|
|
167
|
+
listeners: this.unsub.size,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
abstract type: 'request' | 'response' | string;
|
|
172
|
+
|
|
173
|
+
protected postMessage(data?: Data<R, M>, done = false) {
|
|
178
174
|
checkClosed(this, 'postMessage');
|
|
179
175
|
|
|
180
176
|
const transfer = getTransferrable(data);
|
|
181
177
|
|
|
182
|
-
|
|
178
|
+
const ctx = {
|
|
179
|
+
done,
|
|
180
|
+
id: this.id,
|
|
181
|
+
parentId: this.parent?.id,
|
|
182
|
+
message: this.message,
|
|
183
|
+
};
|
|
183
184
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
id: this.id,
|
|
189
|
-
parentId: this.parent?.id,
|
|
190
|
-
message: this.message,
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
transfer,
|
|
194
|
-
);
|
|
185
|
+
/* v8 ignore next */
|
|
186
|
+
if (this.dialog.debug >= 3) console.log('postMessage', this.info, { ctx, data, transfer }, this);
|
|
187
|
+
|
|
188
|
+
this.dialog.worker.postMessage({ data, ctx }, transfer);
|
|
195
189
|
|
|
196
190
|
return data;
|
|
197
191
|
}
|
|
198
192
|
|
|
199
|
-
send(data?: Data<R, M
|
|
200
|
-
return this.postMessage(data);
|
|
193
|
+
send(data?: Data<R, M>, done = false) {
|
|
194
|
+
return this.postMessage(data, done);
|
|
201
195
|
}
|
|
202
196
|
|
|
203
197
|
/**
|
|
@@ -216,33 +210,44 @@ abstract class Context<R, M extends keyof R> {
|
|
|
216
210
|
* un(); // closes
|
|
217
211
|
* ```
|
|
218
212
|
*/
|
|
219
|
-
listen(callback: Listener<R, M
|
|
213
|
+
listen(callback: Listener<R, M>, done = false): () => void {
|
|
220
214
|
checkClosed(this, 'listen');
|
|
221
215
|
|
|
222
|
-
|
|
216
|
+
const controller = new AbortController();
|
|
223
217
|
|
|
224
218
|
this.dialog.worker.addEventListener(
|
|
225
219
|
'message',
|
|
226
|
-
|
|
220
|
+
async (event: Event<R, M>) => {
|
|
227
221
|
try {
|
|
228
222
|
const {
|
|
229
223
|
data: { ctx, data },
|
|
230
224
|
} = event;
|
|
231
225
|
|
|
232
|
-
if (
|
|
226
|
+
if (
|
|
227
|
+
!ctx ||
|
|
228
|
+
(this.id !== ALL && this.id !== ctx.id) ||
|
|
229
|
+
this.message !== ctx.message ||
|
|
230
|
+
(done && !ctx.done)
|
|
231
|
+
)
|
|
232
|
+
return; // unknown message from devtools etc., or different context
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
/* v8 ignore next */
|
|
235
|
+
if (this.dialog.debug) console.log('Listener event', this.info, event, this);
|
|
235
236
|
|
|
236
237
|
await callback(data, this, event); // Requests can come here, but they're typed in responders
|
|
237
238
|
} catch (e) {
|
|
238
|
-
|
|
239
|
+
/* v8 ignore next */
|
|
240
|
+
if (this.dialog.debug) console.error(`Error in listener ${this.info}`, this, { event, e });
|
|
241
|
+
//TODO Respond with error?
|
|
239
242
|
}
|
|
240
|
-
}
|
|
243
|
+
},
|
|
244
|
+
{ signal: controller.signal },
|
|
241
245
|
);
|
|
242
246
|
|
|
243
247
|
const unsub = () => {
|
|
244
|
-
|
|
245
|
-
this.dialog.
|
|
248
|
+
/* v8 ignore next */
|
|
249
|
+
if (this.dialog.debug >= 2) console.log('Stop listening', this.info, this);
|
|
250
|
+
controller.abort();
|
|
246
251
|
this.unsub.delete(unsub);
|
|
247
252
|
};
|
|
248
253
|
|
|
@@ -253,16 +258,18 @@ abstract class Context<R, M extends keyof R> {
|
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
class RequestContext<R, M extends keyof R> extends Context<R, M> {
|
|
261
|
+
type = 'request';
|
|
262
|
+
|
|
256
263
|
withMessage<M2 extends keyof R>(message: M2, id?: string): RequestContext<R, M2> {
|
|
257
264
|
return new RequestContext<R, M2>(this.dialog, message, id, this);
|
|
258
265
|
}
|
|
259
266
|
|
|
260
|
-
send(data?: MethodArg<R, M
|
|
261
|
-
return super.send(data) as any;
|
|
267
|
+
send(data?: MethodArg<R, M>, done = false): MethodArg<R, M> {
|
|
268
|
+
return super.send(data, done) as any;
|
|
262
269
|
}
|
|
263
270
|
|
|
264
|
-
listen(callback: Listener<R, M, MethodReturn<R, M>, RequestContext<R, M
|
|
265
|
-
return super.listen(callback);
|
|
271
|
+
listen(callback: Listener<R, M, MethodReturn<R, M>, RequestContext<R, M>>, done = false): () => void {
|
|
272
|
+
return super.listen(callback, done);
|
|
266
273
|
}
|
|
267
274
|
|
|
268
275
|
/**
|
|
@@ -280,13 +287,16 @@ class RequestContext<R, M extends keyof R> extends Context<R, M> {
|
|
|
280
287
|
data?: MethodArg<R, M>,
|
|
281
288
|
callback?: (context: RequestContext<R, M>) => void,
|
|
282
289
|
): Promise<MethodReturn<R, M>> {
|
|
290
|
+
checkClosed(this, 'fetch');
|
|
291
|
+
|
|
283
292
|
const res = await Promise.all([
|
|
284
293
|
callback?.(this),
|
|
285
294
|
this.expect(), // will be closed in expect -> listener -> unsub
|
|
286
|
-
this.send(data),
|
|
295
|
+
this.send(data, true),
|
|
287
296
|
]);
|
|
288
297
|
|
|
289
|
-
|
|
298
|
+
/* v8 ignore next */
|
|
299
|
+
if (this.dialog.debug >= 2) console.log('Fetch', this.info, this);
|
|
290
300
|
|
|
291
301
|
this.close('fetch');
|
|
292
302
|
|
|
@@ -302,26 +312,29 @@ class RequestContext<R, M extends keyof R> extends Context<R, M> {
|
|
|
302
312
|
try {
|
|
303
313
|
if ((data as any) instanceof Error) throw data;
|
|
304
314
|
responseListener();
|
|
305
|
-
|
|
315
|
+
/* v8 ignore next */
|
|
316
|
+
if (this.dialog.debug >= 3) console.log('Expect', this.info, data, this);
|
|
306
317
|
resolve(data as any);
|
|
307
318
|
} catch (e) {
|
|
308
319
|
reject(e);
|
|
309
320
|
}
|
|
310
|
-
});
|
|
321
|
+
}, true);
|
|
311
322
|
});
|
|
312
323
|
}
|
|
313
324
|
}
|
|
314
325
|
|
|
315
326
|
class ResponseContext<R, M extends keyof R> extends Context<R, M> {
|
|
327
|
+
type = 'response';
|
|
328
|
+
|
|
316
329
|
withMessage<M2 extends keyof R>(message: M2, id?: string): ResponseContext<R, M2> {
|
|
317
330
|
return new ResponseContext<R, M2>(this.dialog, message, id, this);
|
|
318
331
|
}
|
|
319
332
|
|
|
320
|
-
listen(callback: Listener<R, M, MethodArg<R, M>, ResponseContext<R, M
|
|
321
|
-
return super.listen(callback);
|
|
333
|
+
listen(callback: Listener<R, M, MethodArg<R, M>, ResponseContext<R, M>>, done = false): () => void {
|
|
334
|
+
return super.listen(callback, done);
|
|
322
335
|
}
|
|
323
336
|
|
|
324
|
-
send(data?: MethodReturn<R, M
|
|
325
|
-
return super.send(data) as any;
|
|
337
|
+
send(data?: MethodReturn<R, M>, done = false): MethodReturn<R, M> {
|
|
338
|
+
return super.send(data, done) as any;
|
|
326
339
|
}
|
|
327
340
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const g = globalThis;
|
|
2
|
+
|
|
3
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects
|
|
4
|
+
const transferrable = [
|
|
5
|
+
g.ArrayBuffer,
|
|
6
|
+
g.MessagePort,
|
|
7
|
+
g.ReadableStream,
|
|
8
|
+
g.WritableStream,
|
|
9
|
+
g.TransformStream,
|
|
10
|
+
// WebTransportReceiveStream, // ReferenceError: WebTransportReceiveStream is not defined
|
|
11
|
+
// WebTransportSendStream, // ReferenceError: WebTransportReceiveStream is not defined
|
|
12
|
+
// AudioData, // TS2304: Cannot find name AudioData
|
|
13
|
+
g.ImageBitmap,
|
|
14
|
+
g.VideoFrame,
|
|
15
|
+
g.OffscreenCanvas,
|
|
16
|
+
g.RTCDataChannel,
|
|
17
|
+
g.MediaSourceHandle,
|
|
18
|
+
// MIDIAccess, // ReferenceError: MIDIAccess is not defined
|
|
19
|
+
].filter(Boolean);
|
|
20
|
+
|
|
21
|
+
export function isTransferable(obj: any): boolean {
|
|
22
|
+
return transferrable.some((t) => obj instanceof t) ? obj : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getTransferrable(data: any = {}): Transferable[] {
|
|
26
|
+
if (!data) return [];
|
|
27
|
+
return Object.values<any>(data)
|
|
28
|
+
.reduce((r, v) => {
|
|
29
|
+
return Array.isArray(v) ? [...r, ...v.map(isTransferable)] : [...r, isTransferable(v)];
|
|
30
|
+
}, [])
|
|
31
|
+
.filter(Boolean);
|
|
32
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { wrpc } from './wrpc';
|
|
2
|
+
|
|
3
|
+
export const responder = wrpc().createResponder(self, {
|
|
4
|
+
// note it's not async, and works properly
|
|
5
|
+
test: function* ({ buf, signal }: { signal?: AbortSignal; buf?: ArrayBuffer } = {}) {
|
|
6
|
+
yield { progress: 0 };
|
|
7
|
+
yield { progress: 0.5 };
|
|
8
|
+
yield { progress: 1, aborted: !!signal?.aborted };
|
|
9
|
+
return 'foo';
|
|
10
|
+
},
|
|
11
|
+
// note it's not async, and works properly
|
|
12
|
+
returnYield: function* () {
|
|
13
|
+
yield 1;
|
|
14
|
+
yield 2;
|
|
15
|
+
return yield 3;
|
|
16
|
+
},
|
|
17
|
+
bidirectional: function* (prefix = 1): Generator<string, string, number> {
|
|
18
|
+
let i = 0;
|
|
19
|
+
while (i < 3) {
|
|
20
|
+
prefix = (yield prefix + '.' + i) || prefix;
|
|
21
|
+
i++;
|
|
22
|
+
}
|
|
23
|
+
return 'done';
|
|
24
|
+
},
|
|
25
|
+
// eslint-disable-next-line require-yield
|
|
26
|
+
error: async function* () {
|
|
27
|
+
throw new Error('Test');
|
|
28
|
+
},
|
|
29
|
+
promise: async function () {
|
|
30
|
+
return { test: 'test' };
|
|
31
|
+
},
|
|
32
|
+
promiseError: async function () {
|
|
33
|
+
throw new Error('Test');
|
|
34
|
+
},
|
|
35
|
+
setTimeout: async ({ timeout = 1000 }: { timeout?: number } = {}) =>
|
|
36
|
+
new Promise((resolve) => setTimeout(resolve, timeout)),
|
|
37
|
+
|
|
38
|
+
setInterval: async function* ({ timeout = 1000 }: { timeout?: number } = {}) {
|
|
39
|
+
while (true) {
|
|
40
|
+
yield await this.setTimeout(timeout);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// self.onmessage = (e) => {
|
|
46
|
+
// console.log('MSG', e);
|
|
47
|
+
// if (e.data.msg === 'info') {
|
|
48
|
+
// self.postMessage({
|
|
49
|
+
// responder: 'test', // responder.toString(),
|
|
50
|
+
// });
|
|
51
|
+
// }
|
|
52
|
+
// };
|
|
53
|
+
|
|
54
|
+
// self.onmessage = (e) => {
|
|
55
|
+
// console.log('MSG', e);
|
|
56
|
+
// self.postMessage(`${e.data} world`);
|
|
57
|
+
// };
|
|
58
|
+
|
|
59
|
+
self.addEventListener('message', (e) => {
|
|
60
|
+
if (e.data.msg === 'info') {
|
|
61
|
+
self.postMessage({
|
|
62
|
+
msg: e.data.msg,
|
|
63
|
+
responder: 'test', // responder.toString(),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (e.data.msg === 'stop') {
|
|
67
|
+
responder.stop();
|
|
68
|
+
self.postMessage({
|
|
69
|
+
msg: e.data.msg,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|