@kirill.konshin/core 0.0.1 → 0.0.2

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 (50) 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 +8 -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/package.json +2 -4
  11. package/src/index.ts +0 -6
  12. package/dist/files.d.ts +0 -4
  13. package/dist/files.d.ts.map +0 -1
  14. package/dist/files.js +0 -28
  15. package/dist/files.js.map +0 -1
  16. package/dist/ls.d.ts +0 -6
  17. package/dist/ls.d.ts.map +0 -1
  18. package/dist/ls.js +0 -12
  19. package/dist/ls.js.map +0 -1
  20. package/dist/measure.d.ts +0 -35
  21. package/dist/measure.d.ts.map +0 -1
  22. package/dist/measure.js +0 -60
  23. package/dist/measure.js.map +0 -1
  24. package/dist/worker.d.ts +0 -98
  25. package/dist/worker.d.ts.map +0 -1
  26. package/dist/worker.js +0 -226
  27. package/dist/worker.js.map +0 -1
  28. package/dist/worker.test.d.ts +0 -2
  29. package/dist/worker.test.d.ts.map +0 -1
  30. package/dist/workerUtils.d.ts +0 -3
  31. package/dist/workerUtils.d.ts.map +0 -1
  32. package/dist/workerUtils.js +0 -31
  33. package/dist/workerUtils.js.map +0 -1
  34. package/dist/wrpc.d.ts +0 -45
  35. package/dist/wrpc.d.ts.map +0 -1
  36. package/dist/wrpc.fixture.d.ts +0 -26
  37. package/dist/wrpc.fixture.d.ts.map +0 -1
  38. package/dist/wrpc.js +0 -241
  39. package/dist/wrpc.js.map +0 -1
  40. package/dist/wrpc.test.d.ts +0 -1
  41. package/dist/wrpc.test.d.ts.map +0 -1
  42. package/src/files.ts +0 -29
  43. package/src/ls.ts +0 -15
  44. package/src/measure.ts +0 -109
  45. package/src/worker.test.ts +0 -197
  46. package/src/worker.ts +0 -347
  47. package/src/workerUtils.ts +0 -32
  48. package/src/wrpc.fixture.ts +0 -106
  49. package/src/wrpc.test.ts +0 -225
  50. package/src/wrpc.ts +0 -377
package/src/worker.ts DELETED
@@ -1,347 +0,0 @@
1
- import { getTransferrable } from './workerUtils';
2
-
3
- // Types
4
-
5
- type WebWorker = Worker | (Window & typeof globalThis) | (WorkerGlobalScope & typeof globalThis);
6
-
7
- type MethodReturn<R, M extends keyof R> = R[M] extends (...args: any[]) => any
8
- ? Partial<Awaited<ReturnType<R[M]>>>
9
- : never;
10
-
11
- type MethodArg<R, M extends keyof R> = R[M] extends (arg: infer A, ...args: any[]) => any ? Partial<A> : never;
12
-
13
- export type Ctx<R, K extends keyof R> = {
14
- id: string;
15
- parentId?: string;
16
- message: K;
17
- done: boolean;
18
- };
19
-
20
- type Data<R, M extends keyof R> = MethodReturn<R, M> | MethodArg<R, M> | Error;
21
-
22
- type Event<R, M extends keyof R> = MessageEvent<{ data: Data<R, M>; ctx: Ctx<R, M> }>;
23
-
24
- type Listener<R, M extends keyof R, D extends Data<R, M> = Data<R, M>, S extends Context<R, M> = Context<R, M>> = (
25
- data: D,
26
- self: S,
27
- event: Event<R, M>,
28
- ) => void | Promise<void>;
29
-
30
- // Constants
31
-
32
- const ALL = '*';
33
-
34
- const RESPONDER = Symbol('responder');
35
-
36
- function checkClosed(obj: any, label: string = 'external') {
37
- if (obj?.closed) throw new Error(`Context ${obj.id} is closed (${label})`, { cause: obj });
38
- }
39
-
40
- function checkMessage(obj: any, message: any) {
41
- if (!message) throw new Error('New context must have message', { cause: obj });
42
- }
43
-
44
- // Classes
45
-
46
- export class WorkerDialog<R extends RespondersBase<any>> {
47
- readonly contexts: Set<Context<R, keyof R>> = new Set();
48
- protected closed = false;
49
- protected currentId = 0; // IDs are intentionally not global
50
-
51
- constructor(
52
- public worker: WebWorker,
53
- public responders: R,
54
- public name: string,
55
- public debug: number = 0, // 0 - none, 1 - general messages, 2 - sub/unsub, 3 - transport TODO Object?
56
- ) {
57
- if (!this.worker) return; // Next.js SSR
58
-
59
- for (const message in this.responders) {
60
- if (!this.responders?.[message]?.[RESPONDER]) continue;
61
-
62
- // root context with id=all
63
- const rootContext = new ResponseContext<R, typeof message>(this, message, ALL);
64
-
65
- //TODO: unsub(); ? Probably not needed, root must be always listening until closed by WorkerDialog
66
- rootContext.listen(async (data, context, event) => {
67
- // create sub context with ID
68
- const responseContext = context.withMessage(message, event.data.ctx.id);
69
-
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 */
80
-
81
- try {
82
- responseContext.send(await (this.responders[message] as any)(data, responseContext), true);
83
- responseContext.close('root.listener');
84
- } catch (e) {
85
- /* v8 ignore next */
86
- if (this.debug) console.error(`Error in responder ${responseContext.info}`, { e });
87
- responseContext.send(e, true);
88
- }
89
- }, true);
90
- }
91
- }
92
-
93
- getID(): string {
94
- return (++this.currentId).toString();
95
- }
96
-
97
- close(): void {
98
- for (const context of this.contexts) context.close('root');
99
- this.contexts.clear();
100
- }
101
-
102
- withMessage<M extends keyof R>(message: M): RequestContext<R, M> {
103
- return new RequestContext<R, M>(this, message, this.getID()); // always new context
104
- }
105
- }
106
-
107
- export class RespondersBase<R> {
108
- create<M extends keyof R, F extends (input: any, context: ResponseContext<R, M>) => any>(
109
- message: M,
110
- responder: F,
111
- ): F {
112
- responder[RESPONDER] = true;
113
- return responder as any;
114
- }
115
- }
116
-
117
- // can only be used to create actual contexts, carries the context data
118
- abstract class Context<R, M extends keyof R> {
119
- protected readonly unsub: Set<() => void> = new Set();
120
- protected closed = false;
121
-
122
- constructor(
123
- protected readonly dialog: WorkerDialog<any>,
124
- public message: M,
125
- public id: string | undefined,
126
- public parent: Context<R, keyof R> | undefined | null = null,
127
- ) {
128
- checkClosed(dialog, 'newContext');
129
- checkClosed(parent, 'newContext');
130
- checkMessage(this, message);
131
- this.id = id ?? parent?.id;
132
- if (!this.id) throw new Error('ID is required', { cause: this });
133
- this.dialog.contexts.add(this);
134
- }
135
-
136
- abstract withMessage<M2 extends keyof R>(message: M2, id?: string): Context<R, M2>;
137
-
138
- close(reason: string = 'external'): void {
139
- /* v8 ignore next */
140
- if (this.dialog.debug >= 2) console.log('Closing', this.info, reason, this);
141
-
142
- this.dialog.contexts.delete(this);
143
-
144
- // Unsubscribe from all listeners
145
- for (const unsub of this.unsub) unsub();
146
- this.unsub.clear();
147
-
148
- // Remove all child contexts
149
- this.dialog.contexts.forEach((context) => {
150
- if (context.parent?.id === this.id) context.close('parent');
151
- });
152
-
153
- this.closed = true;
154
- }
155
-
156
- get info() {
157
- return `${this.dialog.name}:${this.parent?.id ?? ALL}/${this.id}:${this.message.toString()}[${this.closed ? 'closed' : 'open'},${this.type}]`;
158
- }
159
-
160
- get debugInfo(): {
161
- id?: string;
162
- parentId?: string;
163
- message: string | number | symbol;
164
- closed: boolean;
165
- type: string;
166
- listeners: number;
167
- } {
168
- return {
169
- id: this.id,
170
- parentId: this.parent?.id,
171
- message: this.message,
172
- closed: this.closed,
173
- type: this.type,
174
- listeners: this.unsub.size,
175
- };
176
- }
177
-
178
- abstract type: 'request' | 'response' | string;
179
-
180
- protected postMessage(data?: Data<R, M>, done = false): Data<R, M> | undefined {
181
- checkClosed(this, 'postMessage');
182
-
183
- const transfer = getTransferrable(data);
184
-
185
- const ctx = {
186
- done,
187
- id: this.id,
188
- parentId: this.parent?.id,
189
- message: this.message,
190
- };
191
-
192
- /* v8 ignore next */
193
- if (this.dialog.debug >= 3) console.log('postMessage', this.info, { ctx, data, transfer }, this);
194
-
195
- this.dialog.worker.postMessage({ data, ctx }, transfer);
196
-
197
- return data;
198
- }
199
-
200
- send(data?: Data<R, M>, done = false): Data<R, M> | undefined {
201
- return this.postMessage(data, done);
202
- }
203
-
204
- /**
205
- * Does not automatically close, assumes consumer will close
206
- *
207
- * ```ts
208
- * dialog.request('x').fetch((c) => {
209
- * c.response('x').listen(() => { ... }); // listens to "x", will be closed by upper level fetch
210
- * c.request('x').fetcH(y, (c) => { ... }); // closes
211
- * }) // closes
212
- *
213
- * const un = dialog.context('x').listen((e) => { ... }); // listens to 'x'
214
- * un(); // closes
215
- *
216
- * const un = dialog.context('x').listen((e) => { ... }); // listens to 'x'
217
- * un(); // closes
218
- * ```
219
- */
220
- listen(callback: Listener<R, M>, done = false): () => void {
221
- checkClosed(this, 'listen');
222
-
223
- const controller = new AbortController();
224
-
225
- this.dialog.worker.addEventListener(
226
- 'message',
227
- async (event: Event<R, M>) => {
228
- try {
229
- const {
230
- data: { ctx, data },
231
- } = event;
232
-
233
- if (
234
- !ctx ||
235
- (this.id !== ALL && this.id !== ctx.id) ||
236
- this.message !== ctx.message ||
237
- (done && !ctx.done)
238
- )
239
- return; // unknown message from devtools etc., or different context
240
-
241
- /* v8 ignore next */
242
- if (this.dialog.debug) console.log('Listener event', this.info, event, this);
243
-
244
- await callback(data, this, event); // Requests can come here, but they're typed in responders
245
- } catch (e) {
246
- /* v8 ignore next */
247
- if (this.dialog.debug) console.error(`Error in listener ${this.info}`, this, { event, e });
248
- //TODO Respond with error?
249
- }
250
- },
251
- { signal: controller.signal },
252
- );
253
-
254
- const unsub = () => {
255
- /* v8 ignore next */
256
- if (this.dialog.debug >= 2) console.log('Stop listening', this.info, this);
257
- controller.abort();
258
- this.unsub.delete(unsub);
259
- };
260
-
261
- this.unsub.add(unsub);
262
-
263
- return unsub;
264
- }
265
- }
266
-
267
- class RequestContext<R, M extends keyof R> extends Context<R, M> {
268
- type = 'request';
269
-
270
- withMessage<M2 extends keyof R>(message: M2, id?: string): RequestContext<R, M2> {
271
- return new RequestContext<R, M2>(this.dialog, message, id, this);
272
- }
273
-
274
- send(data?: MethodArg<R, M>, done = false): MethodArg<R, M> {
275
- return super.send(data, done) as any;
276
- }
277
-
278
- listen(callback: Listener<R, M, MethodReturn<R, M>, RequestContext<R, M>>, done = false): () => void {
279
- return super.listen(callback, done);
280
- }
281
-
282
- /**
283
- * Returns response
284
- * If you need to work with ctx, use callback
285
- * Closed automatically on response
286
- *
287
- * dialog.fetch('x', (c) => {
288
- * c.listen(() => { ... }); // will be closed by upper level fetch
289
- * c.fetcH(y, (c) => { ... }); // closes c
290
- * c.context().fetcH(y, (c) => { ... }); // closes sub, not closes c, will be closed by upper level fetch
291
- * })
292
- */
293
- async fetch(
294
- data?: MethodArg<R, M>,
295
- callback?: (context: RequestContext<R, M>) => void,
296
- ): Promise<MethodReturn<R, M>> {
297
- checkClosed(this, 'fetch');
298
-
299
- const res = await Promise.all([
300
- callback?.(this),
301
- this.expect(), // will be closed in expect -> listener -> unsub
302
- this.send(data, true),
303
- ]);
304
-
305
- /* v8 ignore next */
306
- if (this.dialog.debug >= 2) console.log('Fetch', this.info, this);
307
-
308
- this.close('fetch');
309
-
310
- return res[1];
311
- }
312
-
313
- protected async expect(): Promise<MethodReturn<R, M>> {
314
- checkClosed(this, 'expect');
315
-
316
- //FIXME Manual promise type
317
- return new Promise((resolve, reject) => {
318
- const responseListener = this.listen(async (data) => {
319
- try {
320
- if ((data as any) instanceof Error) throw data;
321
- responseListener();
322
- /* v8 ignore next */
323
- if (this.dialog.debug >= 3) console.log('Expect', this.info, data, this);
324
- resolve(data as any);
325
- } catch (e) {
326
- reject(e);
327
- }
328
- }, true);
329
- });
330
- }
331
- }
332
-
333
- class ResponseContext<R, M extends keyof R> extends Context<R, M> {
334
- type = 'response';
335
-
336
- withMessage<M2 extends keyof R>(message: M2, id?: string): ResponseContext<R, M2> {
337
- return new ResponseContext<R, M2>(this.dialog, message, id, this);
338
- }
339
-
340
- listen(callback: Listener<R, M, MethodArg<R, M>, ResponseContext<R, M>>, done = false): () => void {
341
- return super.listen(callback, done);
342
- }
343
-
344
- send(data?: MethodReturn<R, M>, done = false): MethodReturn<R, M> {
345
- return super.send(data, done) as any;
346
- }
347
- }
@@ -1,32 +0,0 @@
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
- }
@@ -1,106 +0,0 @@
1
- import { wrpc } from './wrpc';
2
-
3
- export const responder: {
4
- responders: {
5
- // note it's not async, and works properly
6
- // @ts-ignore
7
- test: ({ buf, signal }?: { signal?: AbortSignal; buf?: ArrayBuffer }) => Generator<
8
- {
9
- progress: number;
10
- aborted?: boolean;
11
- },
12
- string,
13
- unknown
14
- >;
15
- // note it's not async, and works properly
16
- returnYield: () => Generator<1 | 3 | 2, any, unknown>;
17
- bidirectional: (prefix?: any) => Generator<string, string, number>;
18
-
19
- error: () => AsyncGenerator<never, never, unknown>;
20
- promise: () => Promise<{
21
- test: string;
22
- }>;
23
- promiseError: () => Promise<never>;
24
- setTimeout: ({ timeout }?: { timeout?: number }) => Promise<unknown>;
25
- setInterval: ({ timeout }?: { timeout?: number }) => AsyncGenerator<any, never, unknown>;
26
- };
27
- stop: () => void;
28
- } = wrpc().createResponder(self, {
29
- // note it's not async, and works properly
30
- test: function* ({ buf, signal }: { signal?: AbortSignal; buf?: ArrayBuffer } = {}): Generator<
31
- {
32
- progress: number;
33
- aborted?: boolean;
34
- },
35
- string,
36
- unknown
37
- > {
38
- yield { progress: 0 };
39
- yield { progress: 0.5 };
40
- yield { progress: 1, aborted: !!signal?.aborted };
41
- return 'foo';
42
- },
43
- // note it's not async, and works properly
44
- returnYield: function* (): Generator<1 | 3 | 2, any, unknown> {
45
- yield 1;
46
- yield 2;
47
- return yield 3;
48
- },
49
- bidirectional: function* (prefix = 1): Generator<string, string, number> {
50
- let i = 0;
51
- while (i < 3) {
52
- prefix = (yield prefix + '.' + i) || prefix;
53
- i++;
54
- }
55
- return 'done';
56
- },
57
- // eslint-disable-next-line require-yield
58
- error: async function* (): AsyncGenerator<never, never, unknown> {
59
- throw new Error('Test');
60
- },
61
- promise: async function (): Promise<{
62
- test: string;
63
- }> {
64
- return { test: 'test' };
65
- },
66
- promiseError: async function (): Promise<never> {
67
- throw new Error('Test');
68
- },
69
- setTimeout: async ({ timeout = 1000 }: { timeout?: number } = {}) =>
70
- new Promise((resolve) => setTimeout(resolve, timeout)),
71
-
72
- setInterval: async function* ({ timeout = 1000 }: { timeout?: number } = {}) {
73
- while (true) {
74
- yield await this.setTimeout(timeout);
75
- }
76
- },
77
- });
78
-
79
- // self.onmessage = (e) => {
80
- // console.log('MSG', e);
81
- // if (e.data.msg === 'info') {
82
- // self.postMessage({
83
- // responder: 'test', // responder.toString(),
84
- // });
85
- // }
86
- // };
87
-
88
- // self.onmessage = (e) => {
89
- // console.log('MSG', e);
90
- // self.postMessage(`${e.data} world`);
91
- // };
92
-
93
- self.addEventListener('message', (e) => {
94
- if (e.data.msg === 'info') {
95
- self.postMessage({
96
- msg: e.data.msg,
97
- responder: 'test', // responder.toString(),
98
- });
99
- }
100
- if (e.data.msg === 'stop') {
101
- responder.stop();
102
- self.postMessage({
103
- msg: e.data.msg,
104
- });
105
- }
106
- });