@kirill.konshin/core 0.0.1 → 0.0.3

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.
Files changed (54) hide show
  1. package/.ctirc +19 -10
  2. package/.turbo/turbo-build.log +15 -19
  3. package/.turbo/turbo-test.log +25 -91
  4. package/CHANGELOG.md +14 -0
  5. package/README.md +1 -91
  6. package/dist/index.d.ts +0 -6
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -22
  9. package/dist/index.js.map +1 -1
  10. package/dist/mutex.d.ts +2 -2
  11. package/dist/mutex.d.ts.map +1 -1
  12. package/dist/mutex.js.map +1 -1
  13. package/package.json +9 -5
  14. package/src/index.ts +0 -6
  15. package/src/mutex.ts +2 -2
  16. package/dist/files.d.ts +0 -4
  17. package/dist/files.d.ts.map +0 -1
  18. package/dist/files.js +0 -28
  19. package/dist/files.js.map +0 -1
  20. package/dist/ls.d.ts +0 -6
  21. package/dist/ls.d.ts.map +0 -1
  22. package/dist/ls.js +0 -12
  23. package/dist/ls.js.map +0 -1
  24. package/dist/measure.d.ts +0 -35
  25. package/dist/measure.d.ts.map +0 -1
  26. package/dist/measure.js +0 -60
  27. package/dist/measure.js.map +0 -1
  28. package/dist/worker.d.ts +0 -98
  29. package/dist/worker.d.ts.map +0 -1
  30. package/dist/worker.js +0 -226
  31. package/dist/worker.js.map +0 -1
  32. package/dist/worker.test.d.ts +0 -2
  33. package/dist/worker.test.d.ts.map +0 -1
  34. package/dist/workerUtils.d.ts +0 -3
  35. package/dist/workerUtils.d.ts.map +0 -1
  36. package/dist/workerUtils.js +0 -31
  37. package/dist/workerUtils.js.map +0 -1
  38. package/dist/wrpc.d.ts +0 -45
  39. package/dist/wrpc.d.ts.map +0 -1
  40. package/dist/wrpc.fixture.d.ts +0 -26
  41. package/dist/wrpc.fixture.d.ts.map +0 -1
  42. package/dist/wrpc.js +0 -241
  43. package/dist/wrpc.js.map +0 -1
  44. package/dist/wrpc.test.d.ts +0 -1
  45. package/dist/wrpc.test.d.ts.map +0 -1
  46. package/src/files.ts +0 -29
  47. package/src/ls.ts +0 -15
  48. package/src/measure.ts +0 -109
  49. package/src/worker.test.ts +0 -197
  50. package/src/worker.ts +0 -347
  51. package/src/workerUtils.ts +0 -32
  52. package/src/wrpc.fixture.ts +0 -106
  53. package/src/wrpc.test.ts +0 -225
  54. package/src/wrpc.ts +0 -377
package/dist/wrpc.js DELETED
@@ -1,241 +0,0 @@
1
- import { getTransferrable } from "./workerUtils.js";
2
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
3
- LogLevel2[LogLevel2["disabled"] = 0] = "disabled";
4
- LogLevel2[LogLevel2["error"] = 1] = "error";
5
- LogLevel2[LogLevel2["lifecycle"] = 2] = "lifecycle";
6
- LogLevel2[LogLevel2["detail"] = 3] = "detail";
7
- LogLevel2[LogLevel2["transport"] = 4] = "transport";
8
- return LogLevel2;
9
- })(LogLevel || {});
10
- function deriveController(signal) {
11
- const controller = new AbortController();
12
- return [controller, AbortSignal.any([controller.signal, signal].filter(Boolean))];
13
- }
14
- function rejectOnSignal(promise, signal) {
15
- return Promise.race([
16
- promise,
17
- new Promise((resolve, reject) => {
18
- signal.addEventListener("abort", () => reject("RaceAborted:" + signal.reason), { once: true, signal });
19
- })
20
- ]);
21
- }
22
- function wrpc({
23
- debug = 0,
24
- name = globalThis.importScripts ? "RESPONDER" : "CALLER"
25
- } = {}) {
26
- function logger(type) {
27
- return (level, ctx, ...args) => {
28
- if (!debug || debug < level) return;
29
- const { id, message, ...restCtx } = ctx || {};
30
- console[type](name, message || "*", id || "*", ...args, restCtx);
31
- };
32
- }
33
- const log = logger("log");
34
- const logWarn = logger("warn");
35
- const logError = logger("error");
36
- function send(worker, ctx, payload) {
37
- log(4, ctx, "SEND", payload, typeof payload);
38
- worker.postMessage({ ctx, payload }, getTransferrable(payload));
39
- }
40
- function listen(worker, context, listener, signal, once = false) {
41
- signal.addEventListener(
42
- "abort",
43
- () => logWarn(3, context, "LISTEN SIGNAL ABORT", signal.reason),
44
- {
45
- signal,
46
- once: true
47
- }
48
- );
49
- worker.addEventListener(
50
- "message",
51
- ({ data }) => {
52
- log(4, context, "LISTEN EVENT", data);
53
- if (!context || data?.ctx?.message === context.message && data.ctx?.id === context.id) listener(data);
54
- },
55
- { signal, once }
56
- );
57
- }
58
- function waitFor(worker, ctx, condition = (data) => true, signal) {
59
- const [waitController, waitSignal] = deriveController(signal);
60
- return new Promise(
61
- (res) => listen(
62
- worker,
63
- ctx,
64
- (data) => {
65
- if (!condition(data)) return;
66
- waitController.abort("WaitFinished");
67
- res(data);
68
- },
69
- waitSignal
70
- )
71
- );
72
- }
73
- function createResponder(worker, responders) {
74
- const mainController = new AbortController();
75
- worker.addEventListener("messageerror", (e) => logError(1, null, "MESSAGE ERROR", e), {
76
- signal: mainController.signal
77
- });
78
- listen(
79
- worker,
80
- null,
81
- async ({ ctx, payload = {} }) => {
82
- if (!ctx || !ctx.message || !ctx.id || ctx.abort || ctx.ack) return;
83
- try {
84
- const responder = responders[ctx.message];
85
- if (!responder) throw new Error("Unknown responder", { cause: ctx });
86
- const [subController, subSignal] = deriveController(mainController.signal);
87
- if (ctx.signalName) payload[ctx.signalName] = subSignal;
88
- const iterator = responder(payload);
89
- log(2, ctx, "START", iterator);
90
- if (!iterator[Symbol.asyncIterator] && !iterator[Symbol.iterator]) {
91
- send(worker, { ...ctx, promise: true }, await rejectOnSignal(iterator, subSignal));
92
- log(3, ctx, "PROMISE RESOLVED");
93
- return;
94
- }
95
- try {
96
- listen(
97
- worker,
98
- ctx,
99
- (data) => {
100
- if (!data.ctx.abort) return;
101
- subController.abort(data.ctx.abort);
102
- logWarn(2, ctx, "GOT ABORT");
103
- },
104
- subSignal
105
- );
106
- let res;
107
- let nextPayload = payload;
108
- do {
109
- res = await rejectOnSignal(iterator.next(nextPayload), subSignal);
110
- send(worker, { ...ctx, done: res.done }, res.value);
111
- if (res.done) {
112
- subController.abort("Done");
113
- break;
114
- }
115
- const nextData = await rejectOnSignal(
116
- waitFor(worker, ctx, (data) => !!data.ctx.ack, subSignal),
117
- subSignal
118
- );
119
- nextPayload = nextData.payload;
120
- log(3, ctx, "LOOP ACK");
121
- } while (!res.done && !subSignal.aborted);
122
- } catch (e) {
123
- if (!subSignal.aborted && !e.message.includes("RaceAborted")) {
124
- logError(1, ctx, "LOOP ERROR", e);
125
- send(worker, { ...ctx, error: true, done: true }, e);
126
- }
127
- } finally {
128
- log(3, ctx, "LOOP DONE");
129
- }
130
- } catch (e) {
131
- logError(1, ctx, "ERROR", e);
132
- send(worker, { ...ctx, error: true, done: true }, e);
133
- } finally {
134
- log(3, ctx, "DONE");
135
- }
136
- },
137
- mainController.signal
138
- );
139
- function stop() {
140
- mainController.abort("Stop");
141
- }
142
- return { responders, stop };
143
- }
144
- function createCaller(worker, responders) {
145
- worker.addEventListener("messageerror", (e) => logError(1, null, "MESSAGE ERROR", e));
146
- return new Proxy(responders, {
147
- get(target, prop, receiver) {
148
- function gen(payload) {
149
- const ctx = { message: prop.toString(), id: crypto.randomUUID() };
150
- let signal = null;
151
- if (payload && Object.is(payload, Object(payload))) {
152
- for (const [key, value] of Object.entries(payload)) {
153
- if (value instanceof AbortSignal) {
154
- log(3, ctx, "FOUND SIGNAL", key);
155
- ctx.signalName = key;
156
- signal = value;
157
- payload[key] = null;
158
- }
159
- }
160
- }
161
- const [mainController, mainSignal] = deriveController(signal);
162
- const cancel = (notify, reason = "") => {
163
- if (mainController.signal.aborted) return;
164
- logWarn(2, ctx, "CANCEL", reason, notify);
165
- if (reason) mainController.abort(reason);
166
- if (notify) send(worker, { ...ctx, abort: reason });
167
- };
168
- const [promiseController, promiseSignal] = deriveController(mainSignal);
169
- const promise = waitFor(
170
- worker,
171
- ctx,
172
- (data) => !!data.ctx.promise || !!data.ctx.error,
173
- promiseSignal
174
- ).then(({ ctx: ctx2, payload: payload2 }) => {
175
- if (ctx2.error) {
176
- logError(1, ctx2, "PROMISE ERROR", payload2);
177
- throw payload2;
178
- }
179
- log(2, ctx2, "PROMISE RESOLVED", payload2);
180
- return payload2;
181
- });
182
- async function* generator() {
183
- let done = false;
184
- try {
185
- mainSignal.addEventListener("abort", () => cancel(true, mainSignal.reason), {
186
- once: true,
187
- signal: mainSignal
188
- });
189
- log(3, ctx, "GENERATOR START");
190
- promiseController.abort("GeneratorUsed");
191
- do {
192
- const data = await rejectOnSignal(
193
- waitFor(worker, ctx, void 0, mainSignal),
194
- mainSignal
195
- );
196
- const { ctx: ctxIn, payload: value } = data;
197
- log(2, ctx, "EVENT", mainSignal.aborted, ctxIn, value);
198
- if (ctxIn.promise) {
199
- logError(1, ctx, "ERROR", "Unexpected promise in generator", data);
200
- throw new Error("Unexpected promise in generator", { cause: data });
201
- }
202
- if (ctxIn.error) {
203
- logError(1, ctx, "ERROR", data);
204
- cancel(false, "Error");
205
- throw value;
206
- }
207
- if (ctxIn.done) {
208
- done = true;
209
- return value;
210
- } else {
211
- const nextPayload = yield value;
212
- if (!mainSignal.aborted) send(worker, { ...ctx, ack: true }, nextPayload);
213
- }
214
- } while (!mainSignal.aborted);
215
- } finally {
216
- log(3, ctx, "DONE");
217
- cancel(true, mainSignal.aborted ? mainSignal.reason : done ? "Done" : "Break");
218
- }
219
- }
220
- const iterator = generator();
221
- send(worker, ctx, payload);
222
- iterator["then"] = promise.then.bind(promise);
223
- iterator["catch"] = promise.catch.bind(promise);
224
- iterator["finally"] = promise.finally.bind(promise);
225
- return iterator;
226
- }
227
- Object.defineProperty(gen, "name", { value: prop.toString() });
228
- target[prop] = gen;
229
- return gen;
230
- }
231
- });
232
- }
233
- return { createResponder, createCaller };
234
- }
235
- export {
236
- LogLevel,
237
- deriveController,
238
- rejectOnSignal,
239
- wrpc
240
- };
241
- //# sourceMappingURL=wrpc.js.map
package/dist/wrpc.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrpc.js","sources":["../src/wrpc.ts"],"sourcesContent":["import { getTransferrable } from './workerUtils';\n\n// Types\n\nexport type Responders = { [key: string]: (arg?: any) => Generator<any> | AsyncGenerator<any> | Promise<any> | any };\n\nexport type Context = {\n message: string;\n id: string;\n signalName?: string;\n done?: boolean;\n abort?: string;\n error?: boolean;\n ack?: boolean;\n promise?: boolean;\n};\n\nexport type Event = MessageEvent<{ ctx: Context; payload: any | Error }>;\n\nexport type WorkerLike = {\n postMessage: (payload: any, transferable: any[]) => void;\n addEventListener: (\n event: string,\n listener: (e: any) => void,\n options?: { signal?: AbortSignal; once?: boolean },\n ) => void;\n removeEventListener: (event: string, listener: (e: any) => void) => void;\n} & any;\n\nexport enum LogLevel {\n disabled = 0,\n error = 1,\n lifecycle = 2,\n detail = 3,\n transport = 4,\n}\n\n// Helpers\n\nexport function deriveController(signal?: AbortSignal): [AbortController, AbortSignal] {\n const controller = new AbortController();\n return [controller, AbortSignal.any([controller.signal, signal].filter(Boolean) as AbortSignal[])];\n}\n\nexport function rejectOnSignal(promise: Promise<any>, signal: AbortSignal): Promise<Event['data']> {\n return Promise.race([\n promise,\n new Promise((resolve, reject) => {\n signal.addEventListener('abort', () => reject('RaceAborted:' + signal.reason), { once: true, signal });\n }),\n ]);\n}\n\n// Lib\n\nexport function wrpc({\n debug = LogLevel.disabled,\n name = (globalThis as any).importScripts ? 'RESPONDER' : 'CALLER',\n}: {\n debug?: LogLevel;\n name?: string;\n} = {}): {\n createResponder: <T extends Responders>(\n worker: WorkerLike,\n responders: T,\n ) => {\n responders: T;\n stop: () => void;\n };\n createCaller: <T extends Responders>(worker: WorkerLike, responders: T) => T;\n} {\n // Logger\n\n function logger(type: string) {\n return (level: number, ctx: Context | null, ...args: any[]) => {\n if (!debug || debug < level) return;\n const { id, message, ...restCtx } = ctx || {};\n console[type](name, message || '*', id || '*', ...args, restCtx);\n };\n }\n\n const log = logger('log');\n const logWarn = logger('warn');\n const logError = logger('error');\n\n // Service\n\n function send(worker: WorkerLike, ctx: Context, payload?: any) {\n log(LogLevel.transport, ctx, 'SEND', payload, typeof payload);\n worker.postMessage({ ctx, payload }, getTransferrable(payload));\n }\n\n //TODO Listener error handling\n function listen(\n worker: WorkerLike,\n context: Context | null,\n listener: (event: Event['data']) => void,\n signal: AbortSignal,\n once: boolean = false,\n ) {\n signal.addEventListener(\n 'abort',\n () => logWarn(LogLevel.detail, context, 'LISTEN SIGNAL ABORT', signal.reason),\n {\n signal,\n once: true,\n },\n );\n worker.addEventListener(\n 'message',\n ({ data }: Event) => {\n log(LogLevel.transport, context, 'LISTEN EVENT', data);\n // no context = listen all, or matching context\n if (!context || (data?.ctx?.message === context.message && data.ctx?.id === context.id)) listener(data);\n },\n { signal, once },\n );\n }\n\n function waitFor(\n worker: WorkerLike,\n ctx: Context,\n condition: (data: Event['data']) => boolean = (data) => true,\n signal: AbortSignal,\n ) {\n const [waitController, waitSignal] = deriveController(signal);\n\n return new Promise<Event['data']>((res) =>\n listen(\n worker,\n ctx,\n (data) => {\n if (!condition(data)) return;\n waitController.abort('WaitFinished');\n res(data);\n },\n waitSignal,\n ),\n );\n }\n\n // Responder\n\n //TODO Name arg\n function createResponder<T extends Responders>(\n worker: WorkerLike,\n responders: T,\n ): {\n responders: T;\n stop: () => void;\n } {\n const mainController = new AbortController();\n\n worker.addEventListener('messageerror', (e) => logError(LogLevel.error, null, 'MESSAGE ERROR', e), {\n signal: mainController.signal,\n });\n\n listen(\n worker,\n null,\n async ({ ctx, payload = {} }) => {\n if (!ctx || !ctx.message || !ctx.id || ctx.abort || ctx.ack) return; // ignore invalid and service messages\n try {\n const responder = responders[ctx.message];\n\n if (!responder) throw new Error('Unknown responder', { cause: ctx });\n\n const [subController, subSignal] = deriveController(mainController.signal);\n\n // always recover signal so that promises also can abort\n if (ctx.signalName) payload[ctx.signalName] = subSignal;\n\n const iterator = responder(payload); // no await!\n\n log(LogLevel.lifecycle, ctx, 'START', iterator);\n\n // PROMISE\n\n // https://stackoverflow.com/a/19660350/5125659 the only right way is to call function and see what it returned\n if (!iterator[Symbol.asyncIterator] && !iterator[Symbol.iterator]) {\n // Errors are caught in generic handler\n send(worker, { ...ctx, promise: true }, await rejectOnSignal(iterator, subSignal));\n log(LogLevel.detail, ctx, 'PROMISE RESOLVED');\n return;\n }\n\n // GENERATOR\n\n try {\n // Listen outside the loop to react immediately\n // Could be a wait for since abort is a one-off, but listeners are disposed by signal anyway, and we don't need to block the async thread\n listen(\n worker,\n ctx,\n (data) => {\n if (!data.ctx.abort) return;\n subController.abort(data.ctx.abort);\n logWarn(LogLevel.lifecycle, ctx, 'GOT ABORT');\n },\n subSignal,\n );\n\n let res: any;\n let nextPayload: any = payload; // does not matter, will be ignored anyway\n do {\n res = await rejectOnSignal(iterator.next(nextPayload), subSignal);\n\n send(worker, { ...ctx, done: res.done }, res.value);\n\n if (res.done) {\n subController.abort('Done');\n break;\n }\n\n const nextData = await rejectOnSignal(\n waitFor(worker, ctx, (data) => !!data.ctx.ack, subSignal),\n subSignal,\n );\n nextPayload = nextData.payload;\n\n log(LogLevel.detail, ctx, 'LOOP ACK');\n } while (!res.done && !subSignal.aborted);\n } catch (e) {\n if (!subSignal.aborted && !e.message.includes('RaceAborted')) {\n logError(LogLevel.error, ctx, 'LOOP ERROR', e);\n send(worker, { ...ctx, error: true, done: true }, e);\n }\n } finally {\n log(LogLevel.detail, ctx, 'LOOP DONE');\n }\n } catch (e) {\n // Generator-specific error handling\n logError(LogLevel.error, ctx, 'ERROR', e);\n send(worker, { ...ctx, error: true, done: true }, e); // Generic error handling\n } finally {\n log(LogLevel.detail, ctx, 'DONE');\n }\n },\n mainController.signal,\n );\n\n function stop() {\n mainController.abort('Stop');\n }\n\n return { responders, stop }; // responders needed for typing\n }\n\n // Caller\n\n //TODO Name arg\n function createCaller<T extends Responders>(worker: WorkerLike, responders: T): T {\n worker.addEventListener('messageerror', (e) => logError(LogLevel.error, null, 'MESSAGE ERROR', e));\n\n //TODO Stop\n\n return new Proxy<T>(responders as any, {\n get(target, prop, receiver) {\n function gen(payload: any) {\n const ctx: Context = { message: prop.toString(), id: crypto.randomUUID() };\n\n let signal: AbortSignal = null as any;\n\n if (payload && Object.is(payload, Object(payload))) {\n for (const [key, value] of Object.entries(payload)) {\n if (value instanceof AbortSignal) {\n log(LogLevel.detail, ctx, 'FOUND SIGNAL', key);\n ctx.signalName = key;\n signal = value;\n payload[key] = null; // will be recovered in responder\n }\n }\n }\n\n const [mainController, mainSignal] = deriveController(signal);\n\n const cancel = (notify: boolean, reason: string = '') => {\n if (mainController.signal.aborted) return;\n logWarn(LogLevel.lifecycle, ctx, 'CANCEL', reason, notify);\n if (reason) mainController.abort(reason); // stop listening\n if (notify) send(worker, { ...ctx, abort: reason }); // worker will break & acknowledge, but it's not important\n };\n\n // Generator\n\n const [promiseController, promiseSignal] = deriveController(mainSignal);\n\n const promise = waitFor(\n worker,\n ctx,\n (data) => !!data.ctx.promise || !!data.ctx.error,\n promiseSignal,\n ).then(({ ctx, payload }) => {\n if (ctx.error) {\n logError(LogLevel.error, ctx, 'PROMISE ERROR', payload);\n throw payload;\n }\n log(LogLevel.lifecycle, ctx, 'PROMISE RESOLVED', payload);\n return payload;\n });\n\n async function* generator() {\n let done = false;\n try {\n // React immediately\n mainSignal.addEventListener('abort', () => cancel(true, mainSignal.reason), {\n once: true,\n signal: mainSignal,\n });\n\n log(LogLevel.detail, ctx, 'GENERATOR START');\n promiseController.abort('GeneratorUsed');\n\n do {\n const data = await rejectOnSignal(\n waitFor(worker, ctx, undefined, mainSignal),\n mainSignal,\n );\n\n const { ctx: ctxIn, payload: value } = data;\n\n log(LogLevel.lifecycle, ctx, 'EVENT', mainSignal.aborted, ctxIn, value);\n\n if (ctxIn.promise) {\n // && !ctxIn.error\n // cancel(false, 'Resolved');\n // return payloadIn;\n logError(LogLevel.error, ctx, 'ERROR', 'Unexpected promise in generator', data);\n throw new Error('Unexpected promise in generator', { cause: data });\n }\n\n if (ctxIn.error) {\n logError(LogLevel.error, ctx, 'ERROR', data);\n cancel(false, 'Error');\n throw value;\n }\n\n if (ctxIn.done) {\n done = true;\n return value;\n } else {\n const nextPayload = yield value;\n if (!mainSignal.aborted) send(worker, { ...ctx, ack: true }, nextPayload);\n }\n } while (!mainSignal.aborted);\n } finally {\n // prevent further cycles in worker once main is closed/cancelled/aborted\n log(LogLevel.detail, ctx, 'DONE');\n cancel(true, mainSignal.aborted ? mainSignal.reason : done ? 'Done' : 'Break');\n }\n }\n\n const iterator = generator();\n\n send(worker, ctx, payload);\n\n // FIXME Nasty hack, return both iterator and promise\n // Consuming code will get both, but IDE should show only proper type\n // Consuming code will likely use only one except someone calls `for await ... of await` which is not useful in any case\n iterator['then'] = promise.then.bind(promise);\n iterator['catch'] = promise.catch.bind(promise);\n iterator['finally'] = promise.finally.bind(promise);\n return iterator;\n }\n\n //TODO mimic-function(gen, fn); but no access to original\n Object.defineProperty(gen, 'name', { value: prop.toString() });\n\n (target as any)[prop] = gen; // so that fn is visible after use, maybe not needed\n\n return gen;\n },\n }) as T;\n }\n\n return { createResponder, createCaller };\n}\n"],"names":["LogLevel","ctx","payload"],"mappings":"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"}
@@ -1 +0,0 @@
1
- //# sourceMappingURL=wrpc.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrpc.test.d.ts","sourceRoot":"","sources":["../src/wrpc.test.ts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC"}
package/src/files.ts DELETED
@@ -1,29 +0,0 @@
1
- export const downloadFile = async (file: File): Promise<void> => {
2
- //FIXME https://github.com/eligrey/FileSaver.js/issues/471
3
- const { saveAs } = require('file-saver');
4
-
5
- //FIXME https://github.com/eligrey/FileSaver.js/issues/731
6
- return saveAs(file, file.name);
7
- };
8
-
9
- export const createFile = async (text: any, filename = 'file.json', type = 'application/json'): Promise<File> => {
10
- return new File([new Blob([text], { type })], filename);
11
- };
12
-
13
- export const openFile = (): Promise<string> =>
14
- new Promise((res, rej) => {
15
- const input = document.createElement('input');
16
- input.type = 'file';
17
- input.addEventListener('change', function readFile(e) {
18
- input.removeEventListener('change', readFile);
19
- // @ts-expect-error file is always there
20
- const [file] = e.target.files;
21
- if (!file) return;
22
- const reader = new FileReader();
23
- reader.onload = (e) => res(e.target?.result as string);
24
- reader.onerror = rej;
25
- reader.onabort = rej;
26
- reader.readAsText(file);
27
- });
28
- input.click();
29
- });
package/src/ls.ts DELETED
@@ -1,15 +0,0 @@
1
- export function getStorage(pref: string): {
2
- read: (key: string) => null | any;
3
- write: (key: string, value: any) => void;
4
- remove: (key: string) => void;
5
- } {
6
- const prefix = (key) => `${pref}-${key}`;
7
-
8
- const ls = (typeof window !== 'undefined' ? localStorage : null) as Storage;
9
-
10
- const read = (key: string): any => JSON.parse(ls?.getItem(prefix(key)) || 'null');
11
- const write = (key: string, value: any): void => ls?.setItem(prefix(key), JSON.stringify(value));
12
- const remove = (key: string): void => ls?.removeItem(prefix(key));
13
-
14
- return { read, write, remove };
15
- }
package/src/measure.ts DELETED
@@ -1,109 +0,0 @@
1
- import { bold, magenta, cyan, yellow, red, green } from 'colors/safe';
2
-
3
- export const colored: {
4
- important: typeof bold;
5
- subject: typeof magenta;
6
- arg: typeof cyan;
7
- sup: typeof yellow;
8
- err: typeof red;
9
- ok: typeof green;
10
- } = {
11
- important: bold,
12
- subject: magenta,
13
- arg: cyan,
14
- sup: yellow,
15
- err: red,
16
- ok: green,
17
- };
18
-
19
- const noColor = (str) => str;
20
-
21
- export const uncolored: {
22
- important: (str: any) => any;
23
- subject: (str: any) => any;
24
- arg: (str: any) => any;
25
- sup: (str: any) => any;
26
- err: (str: any) => any;
27
- ok: (str: any) => any;
28
- } = {
29
- important: noColor,
30
- subject: noColor,
31
- arg: noColor,
32
- sup: noColor,
33
- err: noColor,
34
- ok: noColor,
35
- };
36
-
37
- export function createMeasurer({
38
- colors = true,
39
- prepend = 'LOG',
40
- padding = 10,
41
- }: {
42
- colors?: boolean;
43
- prepend?: string;
44
- padding?: number;
45
- }): {
46
- measure: (
47
- what: string,
48
- step: string,
49
- supplemental?: string,
50
- ...args: any[]
51
- ) => {
52
- done: (result?: string, ...args2: any[]) => void;
53
- fail: (result?: string, ...args2: any[]) => void;
54
- log: (...args2: any[]) => void;
55
- };
56
- important: (str: any) => any;
57
- subject: (str: any) => any;
58
- arg: (str: any) => any;
59
- sup: (str: any) => any;
60
- ok: (str: any) => any;
61
- err: (str: any) => any;
62
- } {
63
- const { important, subject, arg, sup, ok, err } = colors ? colored : uncolored;
64
-
65
- function measure(
66
- what: string,
67
- step: string,
68
- supplemental: string = '',
69
- ...args: any[]
70
- ): {
71
- done: (result?: string, ...args2: any[]) => void;
72
- fail: (result?: string, ...args2: any[]) => void;
73
- log: (...args2: any[]) => void;
74
- } {
75
- const time = performance.now();
76
-
77
- const prefix = [
78
- prepend,
79
- important(subject(what.substring(0, padding).padStart(padding, ' '))),
80
- arg(step),
81
- sup(supplemental),
82
- ...args,
83
- ];
84
-
85
- const done = (result: string = ok('DONE'), ...args2: any[]): void => {
86
- console.log(
87
- ...[
88
- ...prefix,
89
- important(result),
90
- ...args2,
91
- 'in',
92
- important((performance.now() - time).toFixed(0)),
93
- 'ms',
94
- ].filter(Boolean),
95
- );
96
- console.groupEnd();
97
- };
98
-
99
- const fail = (result: string = err('FAIL'), ...args2: any[]): void => done(result, ...args2);
100
-
101
- const log = (...args2: any[]): void => {
102
- console.log(...prefix, ...args2);
103
- };
104
-
105
- return { done, fail, log };
106
- }
107
-
108
- return { measure, important, subject, arg, sup, ok, err };
109
- }
@@ -1,197 +0,0 @@
1
- import { expect, describe, test, vi } from 'vitest';
2
- import { RespondersBase, WorkerDialog } from './worker';
3
- import EventEmitter from 'node:events';
4
-
5
- // Prepare
6
-
7
- //TODO @vitest/web-worker can ve used, coverage is collected, but this gives more direct access to worker internals
8
- // class WorkerPeer extends EventEmitter implements WorkerLike {
9
- class WorkerPeer extends EventEmitter {
10
- peer: WorkerPeer;
11
-
12
- setPeer(peer: WorkerPeer) {
13
- this.peer = peer;
14
- }
15
-
16
- postMessage(data: any) {
17
- this.peer?.emit('message', { data });
18
- }
19
-
20
- addEventListener(event: string, listener: (e: any) => void, options) {
21
- this[options['once'] ? 'once' : 'on'](event, listener);
22
- options?.signal?.addEventListener('abort', () => this.off(event, listener));
23
- }
24
-
25
- removeEventListener(event: string, listener: (e: any) => void) {
26
- this.off(event, listener);
27
- }
28
- }
29
-
30
- class Responders extends RespondersBase<Responders> {
31
- lastContext: any; // for testing purposes
32
-
33
- test = this.create(
34
- 'test',
35
- async (
36
- { n, abort = false, buf }: { n: number; abort?: boolean; buf?: ArrayBuffer }, // should have all possible combinations for strong typing
37
- ctx,
38
- ) => {
39
- this.lastContext = ctx;
40
-
41
- const res: number[] = [];
42
-
43
- let aborted = false;
44
- let progress = 0;
45
-
46
- ctx.listen(({ abort }) => {
47
- if (abort) aborted = true;
48
- });
49
-
50
- for (const i of Array(n).keys()) {
51
- if (aborted) break;
52
- progress = (i + 1) / n;
53
- ctx.send({ progress });
54
- res.push(i);
55
- }
56
-
57
- return { res, progress, aborted };
58
- },
59
- );
60
-
61
- error = this.create(
62
- 'error',
63
- async (
64
- _, // should have all possible combinations for strong typing
65
- ctx,
66
- ) => {
67
- this.lastContext = ctx;
68
- throw new Error('Test');
69
- },
70
- );
71
- }
72
-
73
- const createWorker = () => {
74
- const mainWorker = new WorkerPeer();
75
- const mainDialog = new WorkerDialog(
76
- mainWorker as never as Worker,
77
- {} as WorkerDialog<Responders>['responders'],
78
- 'Main',
79
- );
80
-
81
- const worker = new WorkerPeer();
82
- const workerDialog = new WorkerDialog(worker as never as Worker, new Responders(), 'Worker');
83
-
84
- mainWorker.setPeer(worker);
85
- worker.setPeer(mainWorker);
86
-
87
- return { mainWorker, mainDialog, worker, workerDialog };
88
- };
89
-
90
- // Tests
91
-
92
- describe('Worker', async () => {
93
- test('simple', async () => {
94
- const { mainDialog, workerDialog } = createWorker();
95
-
96
- const l = vi.fn();
97
- let mctx;
98
-
99
- const result = await mainDialog.withMessage('test').fetch({ n: 3, buf: new ArrayBuffer() }, (ctx) => {
100
- mctx = ctx;
101
- ctx.listen(l);
102
- });
103
-
104
- expect(l).toBeCalledTimes(4); // 3x progress, last
105
- expect(result).toStrictEqual({ res: [0, 1, 2], progress: 1, aborted: false });
106
-
107
- expect(mainDialog.contexts.size).toBe(0);
108
- expect(mctx.debugInfo.closed).toBe(true);
109
-
110
- expect(workerDialog.contexts.size).toBe(2);
111
- expect(workerDialog.contexts.values().next().value.debugInfo).toStrictEqual({
112
- id: '*',
113
- parentId: undefined,
114
- message: 'test',
115
- closed: false,
116
- type: 'response',
117
- listeners: 1,
118
- });
119
- expect(workerDialog.responders.lastContext.debugInfo).toStrictEqual({
120
- id: '1',
121
- parentId: '*',
122
- message: 'test',
123
- closed: true,
124
- type: 'response',
125
- listeners: 0,
126
- });
127
- });
128
-
129
- test('abort', async () => {
130
- const { mainDialog } = createWorker();
131
-
132
- const result = await mainDialog.withMessage('test').fetch({ n: 3 }, (ctx) => {
133
- ctx.listen(() => {
134
- ctx.send({ abort: true });
135
- });
136
- });
137
-
138
- expect(result).toStrictEqual({ res: [0], progress: 0.3333333333333333, aborted: true });
139
- });
140
-
141
- test('faulty listener', async () => {
142
- const { mainDialog } = createWorker();
143
-
144
- await mainDialog.withMessage('test').fetch({ n: 3 }, (ctx) => {
145
- ctx.listen(() => {
146
- throw new Error('Listener');
147
- });
148
- });
149
-
150
- expect.assertions(0); // nothing happened
151
- });
152
-
153
- test('error', async () => {
154
- const { mainDialog } = createWorker();
155
- await expect(async () => mainDialog.withMessage('error').fetch()).rejects.toThrowError('Test');
156
- });
157
-
158
- test('close', async () => {
159
- const { mainDialog, workerDialog } = createWorker();
160
-
161
- mainDialog.close();
162
- workerDialog.close();
163
-
164
- expect(mainDialog.contexts.size).toBe(0);
165
- expect(workerDialog.contexts.size).toBe(0);
166
- });
167
-
168
- test('empty message', async () => {
169
- expect(() => createWorker().mainDialog.withMessage(undefined as any)).toThrowError(
170
- 'New context must have message',
171
- );
172
- });
173
-
174
- test('empty ID', async () => {
175
- const { mainDialog } = createWorker();
176
- const first = mainDialog.withMessage('test');
177
- expect(() => first.withMessage('error', '')).toThrowError('ID is required');
178
- });
179
-
180
- test('nesting', async () => {
181
- const { mainDialog } = createWorker();
182
- const first = mainDialog.withMessage('test');
183
- const second = first.withMessage('error', 'x');
184
- const third = second.withMessage('error');
185
- expect(mainDialog.contexts.size).toBe(3);
186
- expect(first.info).toBe('Main:*/1:test[open,request]');
187
- first.close();
188
- expect(mainDialog.contexts.size).toBe(0);
189
- expect(first.debugInfo.closed).toBe(true);
190
- expect(second.debugInfo.closed).toBe(true);
191
- expect(third.debugInfo.closed).toBe(true);
192
- expect(first.info).toBe('Main:*/1:test[closed,request]'); // id bumped from 0
193
- expect(second.info).toBe('Main:1/x:error[closed,request]'); // new id
194
- expect(third.info).toBe('Main:x/x:error[closed,request]'); // id kept
195
- await expect(() => first.fetch({ n: 1 })).rejects.toThrowError(/Context 1 is closed/);
196
- });
197
- });