vivth 1.1.2 → 1.2.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/.vivth-temp/README.src.md +35 -0
- package/README.md +1215 -433
- package/README.src.md +5 -2
- package/bun.lock +228 -0
- package/index.mjs +17 -4
- package/package.json +6 -3
- package/src/bundler/CompileJS.mjs +258 -0
- package/src/bundler/CreateESPlugin.mjs +24 -0
- package/src/bundler/EsBundler.mjs +27 -13
- package/src/bundler/FSInline.mjs +57 -0
- package/src/bundler/FSInlineAnalyzer.mjs +197 -0
- package/src/bundler/FSInlineBundled.mjs +34 -0
- package/src/bundler/adds/ToBundledJSPlugin.mjs +77 -0
- package/src/bundler/adds/externals.mjs +8 -0
- package/src/bundler/adds/pluginVivthBundle.mjs +5 -0
- package/src/class/Console.mjs +48 -27
- package/src/class/Derived.mjs +55 -7
- package/src/class/Effect.mjs +100 -39
- package/src/class/EnvSignal.mjs +5 -5
- package/src/class/EventSignal.mjs +55 -5
- package/src/class/FileSafe.mjs +124 -0
- package/src/class/ListDerived.mjs +6 -3
- package/src/class/ListSignal.mjs +11 -11
- package/src/class/LitExp.mjs +405 -0
- package/src/class/Paths.mjs +27 -1
- package/src/class/QChannel.mjs +79 -28
- package/src/class/SafeExit.mjs +31 -11
- package/src/class/Setup.mjs +5 -2
- package/src/class/Signal.mjs +26 -24
- package/src/class/WorkerMainThread.mjs +100 -133
- package/src/class/WorkerMainThreadBundled.mjs +216 -0
- package/src/class/WorkerThread.mjs +38 -32
- package/src/common/Base64URL.mjs +10 -5
- package/src/common/Base64URLFromFile.mjs +24 -0
- package/src/common/keys.mjs +3 -0
- package/src/doc/JSautoDOC.mjs +32 -56
- package/src/doc/parsedFile.mjs +37 -36
- package/src/function/CreateImmutable.mjs +9 -9
- package/src/function/EventCheck.mjs +2 -2
- package/src/function/EventObject.mjs +5 -5
- package/src/function/GetRuntime.mjs +38 -0
- package/src/function/IsAsync.mjs +2 -2
- package/src/function/LazyFactory.mjs +13 -13
- package/src/function/Timeout.mjs +2 -2
- package/src/function/Try.mjs +17 -12
- package/src/function/TryAsync.mjs +5 -5
- package/src/function/TrySync.mjs +5 -5
- package/src/function/TsToMjs.mjs +5 -4
- package/src/types/LitExpKeyType.mjs +5 -0
- package/src/types/QCBReturn.mjs +1 -1
- package/src/types/Runtime.mjs +7 -0
- package/types/dev/fsInline.d.mts +1 -0
- package/types/dev/test.d.mts +1 -0
- package/types/dev/testWorker.d.mts +7 -0
- package/types/dev/testbundle.d.mts +1 -0
- package/types/dev/workerThreadClass.d.mts +13 -0
- package/types/index.d.mts +13 -4
- package/types/src/bundler/CompileJS.d.mts +78 -0
- package/types/src/bundler/CreateESPlugin.d.mts +17 -0
- package/types/src/bundler/EsBundler.d.mts +33 -4
- package/types/src/bundler/FSInline.d.mts +43 -0
- package/types/src/bundler/FSInlineAnalyzer.d.mts +36 -0
- package/types/src/bundler/FSInlineBundled.d.mts +22 -0
- package/types/src/bundler/adds/ToBundledJSPlugin.d.mts +16 -0
- package/types/src/bundler/adds/externals.d.mts +1 -0
- package/types/src/bundler/adds/pluginVivthBundle.d.mts +1 -0
- package/types/src/class/Console.d.mts +36 -5
- package/types/src/class/Derived.d.mts +132 -5
- package/types/src/class/Effect.d.mts +106 -7
- package/types/src/class/EnvSignal.d.mts +8 -8
- package/types/src/class/EventSignal.d.mts +151 -7
- package/types/src/class/FileSafe.d.mts +90 -0
- package/types/src/class/ListDerived.d.mts +8 -5
- package/types/src/class/ListSignal.d.mts +123 -18
- package/types/src/class/LitExp.d.mts +361 -0
- package/types/src/class/Paths.d.mts +18 -1
- package/types/src/class/QChannel.d.mts +69 -22
- package/types/src/class/SafeExit.d.mts +24 -9
- package/types/src/class/Setup.d.mts +6 -5
- package/types/src/class/Signal.d.mts +73 -17
- package/types/src/class/WorkerMainThread.d.mts +39 -37
- package/types/src/class/WorkerMainThreadBundled.d.mts +85 -0
- package/types/src/class/WorkerThread.d.mts +32 -27
- package/types/src/common/Base64URL.d.mts +22 -1
- package/types/src/common/Base64URLFromFile.d.mts +16 -0
- package/types/src/common/keys.d.mts +1 -0
- package/types/src/doc/JSautoDOC.d.mts +3 -19
- package/types/src/doc/parsedFile.d.mts +72 -13
- package/types/src/function/CreateImmutable.d.mts +27 -2
- package/types/src/function/EventCheck.d.mts +15 -0
- package/types/src/function/EventObject.d.mts +17 -2
- package/types/src/function/GetRuntime.d.mts +2 -0
- package/types/src/function/IsAsync.d.mts +18 -0
- package/types/src/function/LazyFactory.d.mts +35 -2
- package/types/src/function/Timeout.d.mts +16 -0
- package/types/src/function/Try.d.mts +52 -1
- package/types/src/function/TryAsync.d.mts +22 -1
- package/types/src/function/TrySync.d.mts +16 -1
- package/types/src/function/TsToMjs.d.mts +19 -0
- package/types/src/types/LitExpKeyType.d.mts +1 -0
- package/types/src/types/QCBReturn.d.mts +1 -1
- package/types/src/types/Runtime.d.mts +1 -0
- package/dev/index.mjs +0 -28
- package/src/bundler/CompileMJS.mjs +0 -110
- package/src/function/WriteFileSafe.mjs +0 -37
- package/types/src/bundler/A.d.mts +0 -1
- package/types/src/bundler/CompileMJS.d.mts +0 -8
- package/types/src/function/WriteFileSafe.d.mts +0 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import { Base64URL } from '../common/Base64URL.mjs';
|
|
4
3
|
import { closeWorkerThreadEventObject } from '../common/eventObjects.mjs';
|
|
4
|
+
import { GetRuntime } from '../function/GetRuntime.mjs';
|
|
5
5
|
import { Try } from '../function/Try.mjs';
|
|
6
6
|
import { TryAsync } from '../function/TryAsync.mjs';
|
|
7
7
|
import { Console } from './Console.mjs';
|
|
@@ -12,19 +12,24 @@ import { SafeExit } from './SafeExit.mjs';
|
|
|
12
12
|
import { Signal } from './Signal.mjs';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* @template
|
|
16
|
-
* @typedef {import('./WorkerResult.mjs').WorkerResult<
|
|
15
|
+
* @template POST
|
|
16
|
+
* @typedef {import('./WorkerResult.mjs').WorkerResult<POST>} WorkerResult
|
|
17
17
|
*/
|
|
18
18
|
/**
|
|
19
19
|
* @typedef {import('./WorkerThread.mjs').WorkerThread} WorkerThread
|
|
20
|
+
* @typedef {import('../common/lazie.mjs').unwrapLazy} unwrapLazy
|
|
20
21
|
*/
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* @description
|
|
24
25
|
* - class helper to create `Worker` instance;
|
|
26
|
+
* - before any `Worker` functionaily to be used, you need to setup it with `WorkerThread.setup` and `WorkerMainThread.setup` before runing anytyhing;
|
|
25
27
|
* @template {WorkerThread} WT
|
|
26
28
|
*/
|
|
27
29
|
export class WorkerMainThread {
|
|
30
|
+
/**
|
|
31
|
+
* @type {boolean}
|
|
32
|
+
*/
|
|
28
33
|
static #isRegistered = false;
|
|
29
34
|
/**
|
|
30
35
|
* @description
|
|
@@ -33,13 +38,14 @@ export class WorkerMainThread {
|
|
|
33
38
|
* @param {typeof WorkerMainThread["workerClass"]} param0.workerClass
|
|
34
39
|
* - example:
|
|
35
40
|
* ```js
|
|
36
|
-
*
|
|
41
|
+
* import { Worker } from 'node:worker_threads';
|
|
37
42
|
* ```
|
|
38
43
|
* @param {typeof WorkerMainThread["pathValidator"]} param0.pathValidator
|
|
39
44
|
* - example:
|
|
40
45
|
* ```js
|
|
41
46
|
* async (workerPath, root, base) => {
|
|
42
|
-
*
|
|
47
|
+
* const truePathCheck = `${root}/${base}/${workerPath}`;
|
|
48
|
+
* const res = await fetch(truePathCheck);
|
|
43
49
|
* // might also check wheter it need base or not
|
|
44
50
|
* return await res.ok;
|
|
45
51
|
* }
|
|
@@ -48,10 +54,11 @@ export class WorkerMainThread {
|
|
|
48
54
|
* - additonal realtivePath from rootPath;
|
|
49
55
|
* - default: '';
|
|
50
56
|
* @example
|
|
57
|
+
* import { Worker } from 'node:worker_threads';
|
|
51
58
|
* import { WorkerMainThread } from 'vivth';
|
|
52
59
|
*
|
|
53
60
|
* WorkerMainThread.setup({
|
|
54
|
-
* workerClass:
|
|
61
|
+
* workerClass: Worker,
|
|
55
62
|
* basePath: 'public/assets/js/workers',
|
|
56
63
|
* pathValidator: async (workerPath, root, base) => {
|
|
57
64
|
* const res = await fetch(`${root}/${base}/${workerPath}`);
|
|
@@ -77,7 +84,7 @@ export class WorkerMainThread {
|
|
|
77
84
|
* @description
|
|
78
85
|
* - reference for `Worker` class;
|
|
79
86
|
* - edit via `setup`;
|
|
80
|
-
* @type {
|
|
87
|
+
* @type {typeof Worker|typeof import('worker_threads').Worker}
|
|
81
88
|
*/
|
|
82
89
|
static workerClass;
|
|
83
90
|
/**
|
|
@@ -98,31 +105,38 @@ export class WorkerMainThread {
|
|
|
98
105
|
type: 'module',
|
|
99
106
|
});
|
|
100
107
|
/**
|
|
108
|
+
* @template {WorkerThread} WT
|
|
101
109
|
* @description
|
|
102
110
|
* - create Worker_instance;
|
|
103
111
|
* @param {string} handler
|
|
104
|
-
* - if `isInline` === `false`, `handler` should be:
|
|
105
|
-
* >- pointing to worker thread file; WHICH
|
|
106
|
-
* >- the path must be relative to `projectRoot`;
|
|
107
|
-
* - if `isInline` === `true`, `handler` should be
|
|
108
|
-
* >- string literal of prebundled worker thread script; OR
|
|
109
|
-
* >- manually made string literal of worker thread script;
|
|
110
112
|
* @param {Omit<WorkerOptions|import('worker_threads').WorkerOptions, 'eval'|'type'>} [options]
|
|
111
|
-
* @
|
|
113
|
+
* @returns {WorkerMainThread<WT>}
|
|
114
|
+
* @example
|
|
115
|
+
* import { WorkerMainThread } from 'vivth';
|
|
116
|
+
*
|
|
117
|
+
* export const myDoubleWorker = WorkerMainThread.newVivthWorker('./doubleWorkerThread.mjs');
|
|
118
|
+
*/
|
|
119
|
+
static newVivthWorker = (handler, options = {}) => {
|
|
120
|
+
return new WorkerMainThread(handler, options);
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* @private
|
|
124
|
+
* @param {Parameters<typeof WorkerMainThread<WT>["newVivthWorker"]>[0]} handler
|
|
125
|
+
* @param {Parameters<typeof WorkerMainThread<WT>["newVivthWorker"]>[1]} [options]
|
|
112
126
|
* @example
|
|
113
127
|
* import { WorkerMainThread } from 'vivth';
|
|
114
128
|
*
|
|
115
|
-
* export const myDoubleWorker =
|
|
129
|
+
* export const myDoubleWorker = WorkerMainThread.newVivthWorker('./doubleWorkerThread.mjs');
|
|
116
130
|
*/
|
|
117
|
-
constructor(handler, options = {}
|
|
131
|
+
constructor(handler, options = {}) {
|
|
118
132
|
/**
|
|
119
|
-
* @param {WT["
|
|
133
|
+
* @param {WT["RECEIVE"]} ev
|
|
120
134
|
* @returns {void}
|
|
121
135
|
*/
|
|
122
136
|
const listener = (ev) => {
|
|
123
137
|
this.#proxyReceiver.value = ev;
|
|
124
138
|
};
|
|
125
|
-
WorkerMainThread.#workerFilehandler(handler, options, this, listener
|
|
139
|
+
WorkerMainThread.#workerFilehandler(handler, options, this, listener);
|
|
126
140
|
}
|
|
127
141
|
/**
|
|
128
142
|
* @type {import('./Signal.mjs').Signal<import('./WorkerResult.mjs').WorkerResult<WT["Post"]>|MessageEvent<import('./WorkerResult.mjs').WorkerResult<WT["Post"]>>>}
|
|
@@ -134,144 +148,97 @@ export class WorkerMainThread {
|
|
|
134
148
|
* | import('worker_threads').WorkerOptions} options
|
|
135
149
|
* @param {WorkerMainThread} worker
|
|
136
150
|
* @param {(any:any)=>void} listener
|
|
137
|
-
* @param {boolean} isInline
|
|
138
151
|
* @returns {Promise<void>}
|
|
139
152
|
*/
|
|
140
|
-
static #workerFilehandler = async (handler, options, worker, listener
|
|
153
|
+
static #workerFilehandler = async (handler, options, worker, listener) => {
|
|
141
154
|
let resolvedPath;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
base: WorkerMainThread.basePath,
|
|
149
|
-
});
|
|
155
|
+
const pathValidator = WorkerMainThread.pathValidator;
|
|
156
|
+
const [resolvedPath_, error] = await TryAsync(async () => {
|
|
157
|
+
return await pathValidator({
|
|
158
|
+
worker: handler,
|
|
159
|
+
root: Paths.root,
|
|
160
|
+
base: WorkerMainThread.basePath,
|
|
150
161
|
});
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
162
|
+
});
|
|
163
|
+
if (error) {
|
|
164
|
+
Console.error({
|
|
165
|
+
error,
|
|
166
|
+
pathValidator,
|
|
167
|
+
message: 'invalid pathValidator inputed to `WorkerMainThread`;',
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
resolvedPath = resolvedPath_;
|
|
172
|
+
const runtime = GetRuntime();
|
|
173
|
+
const workerClass = WorkerMainThread.workerClass;
|
|
174
|
+
if (!workerClass) {
|
|
175
|
+
Console.error('invalid `Worker` inputed to `WorkerMainThread`;');
|
|
176
|
+
return;
|
|
163
177
|
}
|
|
164
|
-
const [
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
throw new Error('invalid `Worker` inputed to `WorkerMainThread`;');
|
|
178
|
+
const [, errorCreatingWorker] = await Try({
|
|
179
|
+
browser: async () => {
|
|
180
|
+
if (runtime !== 'browser') {
|
|
181
|
+
throw new Error('not a browser');
|
|
169
182
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
let worker_;
|
|
176
|
-
if (!isInline) {
|
|
177
|
-
worker_ = worker.#worker.value = new WorkerClass(resolvedPath, {
|
|
178
|
-
...options,
|
|
179
|
-
...WorkerMainThread.#options,
|
|
180
|
-
});
|
|
181
|
-
} else {
|
|
182
|
-
const inlineURL = Base64URL(handler, 'application/javascript', btoa);
|
|
183
|
-
worker_ = worker.#worker.value = new WorkerClass(inlineURL, {
|
|
184
|
-
...options,
|
|
185
|
-
...WorkerMainThread.#options,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
if (!('onmessage' in worker_)) {
|
|
189
|
-
throw new Error();
|
|
190
|
-
}
|
|
191
|
-
worker_.onmessage = listener;
|
|
192
|
-
if (SafeExit.instance) {
|
|
193
|
-
SafeExit.instance.addCallback(async () => {
|
|
194
|
-
worker_.onmessage = null;
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
nonBrowser: async () => {
|
|
199
|
-
const worker_ = (worker.#worker.value = new WorkerClass(resolvedPath, {
|
|
200
|
-
...options,
|
|
201
|
-
...WorkerMainThread.#options,
|
|
202
|
-
}));
|
|
203
|
-
if (!('addEventListener' in worker_)) {
|
|
204
|
-
throw new Error();
|
|
205
|
-
}
|
|
206
|
-
worker_.addEventListener('message', listener);
|
|
207
|
-
if (SafeExit.instance) {
|
|
208
|
-
SafeExit.instance.addCallback(async () => {
|
|
209
|
-
worker_.removeEventListener('message', listener);
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
},
|
|
183
|
+
let worker_;
|
|
184
|
+
worker_ = worker.#worker.value = new workerClass(resolvedPath, {
|
|
185
|
+
...options,
|
|
186
|
+
...WorkerMainThread.#options,
|
|
213
187
|
});
|
|
214
|
-
if (
|
|
215
|
-
throw new Error();
|
|
188
|
+
if (!('onmessage' in worker_)) {
|
|
189
|
+
throw new Error('not a browser');
|
|
216
190
|
}
|
|
191
|
+
worker_.onmessage = listener;
|
|
217
192
|
if (SafeExit.instance) {
|
|
218
193
|
SafeExit.instance.addCallback(async () => {
|
|
219
|
-
|
|
220
|
-
worker.terminate();
|
|
194
|
+
worker_.onmessage = null;
|
|
221
195
|
});
|
|
222
196
|
}
|
|
223
197
|
},
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
198
|
+
nonBrowser: async () => {
|
|
199
|
+
const worker_ = (worker.#worker.value = new workerClass(resolvedPath, {
|
|
200
|
+
...options,
|
|
201
|
+
...WorkerMainThread.#options,
|
|
202
|
+
}));
|
|
203
|
+
if ('addEventListener' in worker_) {
|
|
204
|
+
worker_.addEventListener('message', listener);
|
|
205
|
+
if (SafeExit.instance) {
|
|
206
|
+
SafeExit.instance.addCallback(async () => {
|
|
207
|
+
worker_.removeEventListener('message', listener);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
} else if ('addListener' in worker_) {
|
|
211
|
+
worker_.addListener('message', listener);
|
|
212
|
+
if (SafeExit.instance) {
|
|
213
|
+
SafeExit.instance.addCallback(async () => {
|
|
214
|
+
worker_.removeListener('message', listener);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
throw new Error('not a standard non browser');
|
|
242
219
|
}
|
|
243
220
|
},
|
|
244
221
|
});
|
|
245
|
-
if (
|
|
246
|
-
Console.error(
|
|
222
|
+
if (errorCreatingWorker) {
|
|
223
|
+
Console.error(errorCreatingWorker);
|
|
247
224
|
return;
|
|
248
225
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
* - check whether js run in browser
|
|
257
|
-
* @type {boolean}
|
|
258
|
-
*/
|
|
259
|
-
static get isBrowser() {
|
|
260
|
-
if (WorkerMainThread.#isBrowser === undefined) {
|
|
261
|
-
WorkerMainThread.#isBrowser =
|
|
262
|
-
typeof window !== 'undefined' &&
|
|
263
|
-
typeof location === 'object' &&
|
|
264
|
-
typeof location.origin === 'string';
|
|
226
|
+
if (SafeExit.instance) {
|
|
227
|
+
SafeExit.instance.addCallback(async () => {
|
|
228
|
+
if (worker.#worker.value) {
|
|
229
|
+
worker.#worker.value.postMessage(closeWorkerThreadEventObject);
|
|
230
|
+
}
|
|
231
|
+
worker.terminate();
|
|
232
|
+
});
|
|
265
233
|
}
|
|
266
|
-
|
|
267
|
-
}
|
|
234
|
+
};
|
|
268
235
|
/**
|
|
269
236
|
* lazyly generated because node version need to await
|
|
270
237
|
* @type {Signal<Worker | import('worker_threads').Worker>}
|
|
271
238
|
*/
|
|
272
239
|
#worker = new Signal(undefined);
|
|
273
240
|
/**
|
|
274
|
-
* @type {Signal<WT["
|
|
241
|
+
* @type {Signal<WT["RECEIVE"]>}
|
|
275
242
|
*/
|
|
276
243
|
#proxyPost = new Signal(undefined);
|
|
277
244
|
#handler = new Effect(async ({ subscribe }) => {
|
|
@@ -301,7 +268,7 @@ export class WorkerMainThread {
|
|
|
301
268
|
/**
|
|
302
269
|
* @description
|
|
303
270
|
* - result signal of the processed message;
|
|
304
|
-
* @type {Derived<WorkerResult<WT["
|
|
271
|
+
* @type {Derived<WorkerResult<WT["POST"]>>}
|
|
305
272
|
* @example
|
|
306
273
|
* import { Effect } from 'vivth';
|
|
307
274
|
* import { myDoubleWorker } from './myDoubleWorker.mjs';
|
|
@@ -322,7 +289,7 @@ export class WorkerMainThread {
|
|
|
322
289
|
/**
|
|
323
290
|
* @description
|
|
324
291
|
* - callback to send message to the worker thread;
|
|
325
|
-
* @type {(event: WT["
|
|
292
|
+
* @type {(event: WT["RECEIVE"])=>void}
|
|
326
293
|
* @example
|
|
327
294
|
* import { myDoubleWorker } from './myDoubleWorker.mjs';
|
|
328
295
|
*
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { FSInline } from '../bundler/FSInline.mjs';
|
|
4
|
+
import { Base64URL } from '../common/Base64URL.mjs';
|
|
5
|
+
import { closeWorkerThreadEventObject } from '../common/eventObjects.mjs';
|
|
6
|
+
import { GetRuntime } from '../function/GetRuntime.mjs';
|
|
7
|
+
import { Try } from '../function/Try.mjs';
|
|
8
|
+
import { Console } from './Console.mjs';
|
|
9
|
+
import { Derived } from './Derived.mjs';
|
|
10
|
+
import { Effect } from './Effect.mjs';
|
|
11
|
+
import { Paths } from './Paths.mjs';
|
|
12
|
+
import { SafeExit } from './SafeExit.mjs';
|
|
13
|
+
import { Signal } from './Signal.mjs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @template POST
|
|
17
|
+
* @typedef {import('./WorkerResult.mjs').WorkerResult<POST>} WorkerResult
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {import('./WorkerThread.mjs').WorkerThread} WorkerThread
|
|
21
|
+
* @typedef {import('../common/lazie.mjs').unwrapLazy} unwrapLazy
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @template {WorkerThread} WT
|
|
26
|
+
*/
|
|
27
|
+
export class WorkerMainThread {
|
|
28
|
+
/**
|
|
29
|
+
* @type {boolean}
|
|
30
|
+
*/
|
|
31
|
+
static #isRegistered = false;
|
|
32
|
+
/**
|
|
33
|
+
* @param {Object} param0
|
|
34
|
+
* @param {typeof WorkerMainThread["workerClass"]} param0.workerClass
|
|
35
|
+
* @param {typeof WorkerMainThread["pathValidator"]} param0.pathValidator
|
|
36
|
+
* @param {typeof WorkerMainThread["basePath"]} [param0.basePath]
|
|
37
|
+
*/
|
|
38
|
+
static setup = ({ workerClass, pathValidator, basePath = '' }) => {
|
|
39
|
+
if (!Paths.root) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (WorkerMainThread.#isRegistered) {
|
|
43
|
+
Console.warn({ message: 'WorkerMainThread.setup, can only be called once' });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
WorkerMainThread.#isRegistered = true;
|
|
47
|
+
WorkerMainThread.workerClass = workerClass;
|
|
48
|
+
WorkerMainThread.pathValidator = pathValidator;
|
|
49
|
+
WorkerMainThread.basePath = basePath;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* @type {typeof Worker|typeof import('worker_threads').Worker}
|
|
53
|
+
*/
|
|
54
|
+
static workerClass;
|
|
55
|
+
/**
|
|
56
|
+
* @type {string}
|
|
57
|
+
*/
|
|
58
|
+
static basePath;
|
|
59
|
+
/**
|
|
60
|
+
* @type {(paths:{worker: string, root:string, base: string})=>Promise<string>}
|
|
61
|
+
*/
|
|
62
|
+
static pathValidator;
|
|
63
|
+
static #options = /** @type {import('worker_threads').WorkerOptions & { type?: 'module' }} */ ({
|
|
64
|
+
type: 'module',
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* @template {WorkerThread} WT
|
|
68
|
+
* @param {string} handler
|
|
69
|
+
* @param {Omit<WorkerOptions|import('worker_threads').WorkerOptions, 'eval'|'type'>} [options]
|
|
70
|
+
* @returns {WorkerMainThread<WT>}
|
|
71
|
+
*/
|
|
72
|
+
static newVivthWorker = (handler, options = {}) => {
|
|
73
|
+
return new WorkerMainThread(handler, options);
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* @param {Parameters<typeof WorkerMainThread<WT>["newVivthWorker"]>[0]} handler
|
|
77
|
+
* @param {Parameters<typeof WorkerMainThread<WT>["newVivthWorker"]>[1]} [options]
|
|
78
|
+
*/
|
|
79
|
+
constructor(handler, options = {}) {
|
|
80
|
+
/**
|
|
81
|
+
* @param {WT["RECEIVE"]} ev
|
|
82
|
+
* @returns {void}
|
|
83
|
+
*/
|
|
84
|
+
const listener = (ev) => {
|
|
85
|
+
this.#proxyReceiver.value = ev;
|
|
86
|
+
};
|
|
87
|
+
WorkerMainThread.#workerFilehandler(handler, options, this, listener);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* @type {import('./Signal.mjs').Signal<import('./WorkerResult.mjs').WorkerResult<WT["Post"]>|MessageEvent<import('./WorkerResult.mjs').WorkerResult<WT["Post"]>>>}
|
|
91
|
+
*/
|
|
92
|
+
#proxyReceiver = new Signal(undefined);
|
|
93
|
+
/**
|
|
94
|
+
* @param {string} handler
|
|
95
|
+
* @param { WorkerOptions
|
|
96
|
+
* | import('worker_threads').WorkerOptions} options
|
|
97
|
+
* @param {WorkerMainThread} worker
|
|
98
|
+
* @param {(any:any)=>void} listener
|
|
99
|
+
* @returns {Promise<void>}
|
|
100
|
+
*/
|
|
101
|
+
static #workerFilehandler = async (handler, options, worker, listener) => {
|
|
102
|
+
let resolvedPath;
|
|
103
|
+
WorkerMainThread.#options.eval = true;
|
|
104
|
+
resolvedPath = (await FSInline.vivthFSInlineFile(handler)).toString('utf-8');
|
|
105
|
+
const runtime = GetRuntime();
|
|
106
|
+
const workerClass = WorkerMainThread.workerClass;
|
|
107
|
+
if (!workerClass) {
|
|
108
|
+
Console.error('invalid `Worker` inputed to `WorkerMainThread`;');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const [, errorCreatingWorker] = await Try({
|
|
112
|
+
browser: async () => {
|
|
113
|
+
if (runtime !== 'browser') {
|
|
114
|
+
throw new Error('not a browser');
|
|
115
|
+
}
|
|
116
|
+
let worker_;
|
|
117
|
+
const inlineURL = Base64URL(handler, 'application/javascript', btoa);
|
|
118
|
+
worker_ = worker.#worker.value = new workerClass(inlineURL, {
|
|
119
|
+
...options,
|
|
120
|
+
...WorkerMainThread.#options,
|
|
121
|
+
});
|
|
122
|
+
if (!('onmessage' in worker_)) {
|
|
123
|
+
throw new Error('not a browser');
|
|
124
|
+
}
|
|
125
|
+
worker_.onmessage = listener;
|
|
126
|
+
if (SafeExit.instance) {
|
|
127
|
+
SafeExit.instance.addCallback(async () => {
|
|
128
|
+
worker_.onmessage = null;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
nonBrowser: async () => {
|
|
133
|
+
const worker_ = (worker.#worker.value = new workerClass(resolvedPath, {
|
|
134
|
+
...options,
|
|
135
|
+
...WorkerMainThread.#options,
|
|
136
|
+
}));
|
|
137
|
+
if ('addEventListener' in worker_) {
|
|
138
|
+
worker_.addEventListener('message', listener);
|
|
139
|
+
if (SafeExit.instance) {
|
|
140
|
+
SafeExit.instance.addCallback(async () => {
|
|
141
|
+
worker_.removeEventListener('message', listener);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
} else if ('addListener' in worker_) {
|
|
145
|
+
worker_.addListener('message', listener);
|
|
146
|
+
if (SafeExit.instance) {
|
|
147
|
+
SafeExit.instance.addCallback(async () => {
|
|
148
|
+
worker_.removeListener('message', listener);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
throw new Error('not a standard non browser');
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
if (errorCreatingWorker) {
|
|
157
|
+
Console.error(errorCreatingWorker);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (SafeExit.instance) {
|
|
161
|
+
SafeExit.instance.addCallback(async () => {
|
|
162
|
+
if (worker.#worker.value) {
|
|
163
|
+
worker.#worker.value.postMessage(closeWorkerThreadEventObject);
|
|
164
|
+
}
|
|
165
|
+
worker.terminate();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* lazyly generated because node version need to await
|
|
171
|
+
* @type {Signal<Worker | import('worker_threads').Worker>}
|
|
172
|
+
*/
|
|
173
|
+
#worker = new Signal(undefined);
|
|
174
|
+
/**
|
|
175
|
+
* @type {Signal<WT["RECEIVE"]>}
|
|
176
|
+
*/
|
|
177
|
+
#proxyPost = new Signal(undefined);
|
|
178
|
+
#handler = new Effect(async ({ subscribe }) => {
|
|
179
|
+
const postData = subscribe(this.#proxyPost).value;
|
|
180
|
+
const worker = subscribe(this.#worker).value;
|
|
181
|
+
if (!worker || postData === undefined) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
worker.postMessage(postData);
|
|
185
|
+
});
|
|
186
|
+
/**
|
|
187
|
+
* @type {()=>void}
|
|
188
|
+
*/
|
|
189
|
+
terminate = () => {
|
|
190
|
+
/**
|
|
191
|
+
* this is more for browser, as most of this are automatically cleaned with `SafeExit`;
|
|
192
|
+
*/
|
|
193
|
+
this.#worker.value?.terminate();
|
|
194
|
+
this.#worker.remove.ref();
|
|
195
|
+
this.#handler.options.removeEffect();
|
|
196
|
+
this.#proxyPost.remove.ref();
|
|
197
|
+
this.#proxyReceiver.remove.ref();
|
|
198
|
+
this.receiverSignal.remove.ref();
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* @type {Derived<WorkerResult<WT["POST"]>>}
|
|
202
|
+
*/
|
|
203
|
+
receiverSignal = new Derived(async ({ subscribe }) => {
|
|
204
|
+
const val = subscribe(this.#proxyReceiver).value;
|
|
205
|
+
if (val instanceof MessageEvent) {
|
|
206
|
+
return val.data;
|
|
207
|
+
}
|
|
208
|
+
return val;
|
|
209
|
+
});
|
|
210
|
+
/**
|
|
211
|
+
* @type {(event: WT["RECEIVE"])=>void}
|
|
212
|
+
*/
|
|
213
|
+
postMessage = (data) => {
|
|
214
|
+
this.#proxyPost.value = data;
|
|
215
|
+
};
|
|
216
|
+
}
|