forked from EdgeTX/buddy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.ts
105 lines (95 loc) · 3.1 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
* There are some browser requests which cannot be made inside web workers. So although
* our graphql context exists in the webworker, we need to be able to make calls back
* out to the window process to get the information we need.
*/
import { isObject } from "type-guards";
type FunctionResponseType<T> = {
type: string;
args: { id: number } & ({ data: T } | { error: Error });
};
type FunctionRequestType<A extends unknown[]> = {
type: string;
args: { id: number; args: A };
};
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const waitForResponse = <T>(
workerSelf: typeof globalThis,
requestType: string,
requestId: number
) =>
new Promise<T>((resolve, reject) => {
const listener = (message: MessageEvent<unknown>): void => {
if (
isObject(message.data) &&
"type" in message.data &&
"args" in message.data
) {
const response = message.data as FunctionResponseType<T>;
if (response.type === requestType && response.args.id === requestId) {
if ("data" in response.args) {
resolve(response.args.data);
} else if ("error" in response.args) {
reject(response.args.error);
}
workerSelf.removeEventListener("message", listener);
}
}
};
workerSelf.addEventListener("message", listener);
}) as T extends Promise<unknown> ? T : Promise<T>;
let incrementedId = 0;
const getCallId = (): number => {
incrementedId += 1;
incrementedId %= 1000;
return incrementedId;
};
/**
* Creates a handler for the given function
* so that it can be called cross boundary. The returned result
* is sent back to the caller in the web worker
*/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export const createCrossBoundryFunction = <
F extends (...args: never[]) => unknown | Promise<unknown>
>(
name: string,
handler: F
) => {
type Return = ReturnType<F>;
type Params = Parameters<F>;
const type = `call${name}`;
return {
call: (workerSelf: typeof globalThis, ...params: Params) => {
const id = getCallId();
workerSelf.postMessage({
type,
args: { args: params, id },
} as FunctionRequestType<Params>);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return waitForResponse<Return>(workerSelf, type, id);
},
listen: (worker: Worker) => {
worker.addEventListener("message", async (event) => {
const request = event.data as FunctionRequestType<Params>;
if (isObject(request) && request.type === type) {
try {
const response = (await handler(...request.args.args)) as Return;
worker.postMessage({
type,
args: {
id: request.args.id,
data: response,
},
} as FunctionResponseType<Return>);
} catch (e) {
worker.postMessage({
type,
args: { id: request.args.id, error: e as Error },
} as FunctionResponseType<Return>);
}
}
});
},
};
};