@kirill.konshin/worker 0.0.2 → 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.
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -13
- package/dist/worker.js +238 -220
- package/dist/worker.js.map +1 -1
- package/dist/workerUtils.js +22 -26
- package/dist/workerUtils.js.map +1 -1
- package/dist/wrpc.d.ts +1 -0
- package/dist/wrpc.d.ts.map +1 -1
- package/dist/wrpc.fixture.d.ts +26 -22
- package/dist/wrpc.fixture.d.ts.map +1 -1
- package/dist/wrpc.js +238 -235
- package/dist/wrpc.js.map +1 -1
- package/package.json +14 -4
- package/.ctirc +0 -20
- package/.turbo/turbo-build.log +0 -22
- package/.turbo/turbo-test.log +0 -97
- package/CHANGELOG.md +0 -7
- package/dist/index.js.map +0 -1
- package/src/index.ts +0 -3
- package/src/worker.test.ts +0 -197
- package/src/worker.ts +0 -347
- package/src/workerUtils.ts +0 -32
- package/src/wrpc.fixture.ts +0 -106
- package/src/wrpc.test.ts +0 -225
- package/src/wrpc.ts +0 -377
- package/tsconfig.json +0 -10
- package/turbo.json +0 -10
- package/vite.config.ts +0 -2
package/dist/wrpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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
|
+
{"version":3,"file":"wrpc.js","names":[],"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 if (signal.aborted) return Promise.reject('RaceAborted:' + signal.reason);\n return Promise.race([\n promise,\n new Promise((resolve, reject) => {\n /*\n * NOTE: no { signal } disposal here — abort algorithms (which remove signal-disposed\n * listeners) run BEFORE the abort event is dispatched, so a listener disposed by the very\n * signal it listens to would be removed without ever firing. { once } self-removes on fire.\n */\n signal.addEventListener('abort', () => reject('RaceAborted:' + signal.reason), { once: true });\n }),\n ]);\n}\n\n/*\n * Names that must never be used as responder method names: `then`/`catch`/`finally` would make the\n * caller thenable (`await caller` would dispatch a bogus RPC), and `toJSON`/`toString`/`constructor`\n * must keep resolving to the plain-object natives so the caller stays introspectable.\n */\nexport const RESERVED_NAMES: string[] = ['then', 'catch', 'finally', 'toJSON', 'toString', 'constructor'];\n\nfunction validateResponders(responders: Responders) {\n for (const key of Object.keys(responders)) {\n if (RESERVED_NAMES.includes(key)) throw new Error(`Responder name \"${key}\" is reserved`);\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 // no { signal } disposal — see the note in rejectOnSignal; { once } self-removes on fire\n signal.addEventListener(\n 'abort',\n () => logWarn(LogLevel.detail, context, 'LISTEN SIGNAL ABORT', signal.reason),\n {\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']>((resolve) =>\n listen(\n worker,\n ctx,\n (data) => {\n if (!condition(data)) return;\n waitController.abort('WaitFinished');\n resolve(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 validateResponders(responders);\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?.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.call(responders, payload); // no await! `.call` so responders can use `this`\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 let res: any;\n try {\n /*\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 */\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 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 /*\n * Run the generator's own finally/cleanup when the loop ended early (abort, break,\n * error). Best-effort, not awaited: if the generator is suspended on an await that\n * never settles, the queued return() never executes — cooperative cancellation via\n * the recovered AbortSignal remains the responder's own job.\n */\n if (!res?.done) {\n try {\n void Promise.resolve(iterator.return?.(undefined)).catch(() => {});\n } catch {\n // sync generator threw during cleanup\n }\n }\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 validateResponders(responders);\n\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 /*\n * Reserved names and symbols fall through to the plain object: `then` stays undefined so\n * the caller is not thenable (`await caller` would otherwise dispatch a bogus RPC), and\n * `toString`/`constructor` resolve to the inherited natives so the caller can be\n * introspected. validateResponders() forbids creating responders with these names.\n */\n if (typeof prop !== 'string' || RESERVED_NAMES.includes(prop)) {\n return Reflect.get(target, prop, receiver);\n }\n\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(worker, ctx, undefined, promiseSignal).then((data) => {\n const { ctx: ctxIn, payload } = data;\n if (ctxIn.error) {\n logError(LogLevel.error, ctx, 'PROMISE ERROR', payload);\n throw payload;\n }\n if (ctxIn.promise) {\n log(LogLevel.lifecycle, ctx, 'PROMISE RESOLVED', payload);\n return payload;\n }\n // a generator frame means the method was awaited instead of iterated\n logError(LogLevel.error, ctx, 'ERROR', 'Cannot await a generator method', data);\n cancel(true, 'AwaitedGenerator');\n throw new Error('Cannot await a generator method, use `for await ... of`', {\n cause: data,\n });\n });\n\n /*\n * Keep at least one rejection handler attached: the promise rejects unobserved when a\n * generator's first frame arrives before iteration starts, or when a fire-and-forget\n * promise call errors. Awaiters get their own branch via the bound then/catch below.\n */\n promise.catch(() => {});\n\n async function* generator() {\n let done = false;\n try {\n // React immediately (no { signal } disposal — see the note in rejectOnSignal)\n mainSignal.addEventListener('abort', () => cancel(true, mainSignal.reason), {\n once: true,\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 /*\n * && !ctxIn.error\n * cancel(false, 'Resolved');\n * return payloadIn;\n */\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 /*\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 */\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"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kirill.konshin/worker",
|
|
3
3
|
"description": "Worker Utilities",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"----- BUILD -----": "",
|
|
8
8
|
"clean": "rm -rf dist .tscache tsconfig.tsbuildinfo",
|
|
9
9
|
"build": "vite build",
|
|
10
|
-
"build:index": "ctix build",
|
|
11
|
-
"build:check-types": "attw --pack .",
|
|
12
10
|
"start": "yarn build --watch",
|
|
13
11
|
"wait": "wait-on ./dist/index.js",
|
|
14
12
|
"----- TEST -----": "",
|
|
@@ -37,5 +35,17 @@
|
|
|
37
35
|
"main": "./dist/index.js",
|
|
38
36
|
"module": "./dist/index.js",
|
|
39
37
|
"types": "./dist/index.d.ts",
|
|
40
|
-
"
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/kirill-konshin/utils.git",
|
|
41
|
+
"directory": "packages/worker"
|
|
42
|
+
},
|
|
43
|
+
"nx": {
|
|
44
|
+
"tags": [
|
|
45
|
+
"platform:worker"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist"
|
|
50
|
+
]
|
|
41
51
|
}
|
package/.ctirc
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"options": [
|
|
3
|
-
{
|
|
4
|
-
"mode": "create",
|
|
5
|
-
"project": "tsconfig.json",
|
|
6
|
-
"include": "src/**/*.{ts,tsx}",
|
|
7
|
-
"exclude": [
|
|
8
|
-
"**/*.stories.*",
|
|
9
|
-
"**/*.test.*",
|
|
10
|
-
"**/*.fixture.*"
|
|
11
|
-
],
|
|
12
|
-
"startFrom": "src",
|
|
13
|
-
"backup": false,
|
|
14
|
-
"overwrite": true,
|
|
15
|
-
"generationStyle": "default-alias-named-star",
|
|
16
|
-
"output": "src",
|
|
17
|
-
"verbose": true
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
}
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
[36mvite v7.0.6 [32mbuilding SSR bundle for production...[36m[39m
|
|
2
|
-
- ctix 'create' mode start, ...
|
|
3
|
-
✔ /home/runner/work/utils/utils/packages/worker/tsconfig.json loading complete!
|
|
4
|
-
✔ analysis export statements completed!
|
|
5
|
-
- build "index.ts" file start
|
|
6
|
-
- output file exists check, ...
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
✔ ctix 'create' mode complete!
|
|
10
|
-
transforming...
|
|
11
|
-
[32m✓[39m 4 modules transformed.
|
|
12
|
-
rendering chunks...
|
|
13
|
-
|
|
14
|
-
[vite:dts] Start generate declaration files...
|
|
15
|
-
[2mdist/[22m[36mindex.js [39m[1m[2m0.38 kB[22m[1m[22m[2m │ map: 0.09 kB[22m
|
|
16
|
-
[2mdist/[22m[36mworkerUtils.js [39m[1m[2m0.97 kB[22m[1m[22m[2m │ map: 1.83 kB[22m
|
|
17
|
-
[2mdist/[22m[36mworker.js [39m[1m[2m7.06 kB[22m[1m[22m[2m │ map: 16.69 kB[22m
|
|
18
|
-
[2mdist/[22m[36mwrpc.js [39m[1m[2m8.73 kB[22m[1m[22m[2m │ map: 20.98 kB[22m
|
|
19
|
-
[vite:dts] Declaration files built in 1924ms.
|
|
20
|
-
|
|
21
|
-
[32m✓ built in 5.20s[39m
|
|
22
|
-
Updated package.json with exports
|
package/.turbo/turbo-test.log
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
- ctix 'create' mode start, ...
|
|
2
|
-
✔ /home/runner/work/utils/utils/packages/worker/tsconfig.json loading complete!
|
|
3
|
-
✔ analysis export statements completed!
|
|
4
|
-
- build "index.ts" file start
|
|
5
|
-
- output file exists check, ...
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
✔ ctix 'create' mode complete!
|
|
9
|
-
|
|
10
|
-
[1m[46m RUN [49m[22m [36mv3.2.4 [39m[90m/home/runner/work/utils/utils/packages/worker[39m
|
|
11
|
-
[2mCoverage enabled with [22m[33mv8[39m
|
|
12
|
-
|
|
13
|
-
[90mstderr[2m | src/wrpc.test.ts
|
|
14
|
-
[22m[39mUsing an object as a third argument is deprecated. Vitest 4 will throw an error if the third argument is not a timeout number. Please use the second argument for options. See more at https://vitest.dev/guide/migration
|
|
15
|
-
|
|
16
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of return yield
|
|
17
|
-
[22m[39mDATA [33m1[39m
|
|
18
|
-
|
|
19
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of return yield
|
|
20
|
-
[22m[39mDATA [33m2[39m
|
|
21
|
-
|
|
22
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of return yield
|
|
23
|
-
[22m[39mDATA [33m3[39m
|
|
24
|
-
|
|
25
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of return yield
|
|
26
|
-
[22m[39mDATA { value: [33m1[39m, done: [33mfalse[39m }
|
|
27
|
-
|
|
28
|
-
[32m✓[39m src/worker.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m 14[2mms[22m[39m
|
|
29
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of return yield
|
|
30
|
-
[22m[39mDATA { value: [33m2[39m, done: [33mfalse[39m }
|
|
31
|
-
|
|
32
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of return yield
|
|
33
|
-
[22m[39mDATA { value: [33m3[39m, done: [33mfalse[39m }
|
|
34
|
-
|
|
35
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of return yield
|
|
36
|
-
[22m[39mDATA { value: [90mundefined[39m, done: [33mtrue[39m }
|
|
37
|
-
|
|
38
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of separate return & yield
|
|
39
|
-
[22m[39mDATA { progress: [33m0[39m }
|
|
40
|
-
|
|
41
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of separate return & yield
|
|
42
|
-
[22m[39mDATA { progress: [33m0.5[39m }
|
|
43
|
-
|
|
44
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mfor await ... of separate return & yield
|
|
45
|
-
[22m[39mDATA { progress: [33m1[39m, aborted: [33mfalse[39m }
|
|
46
|
-
|
|
47
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mdo ... while
|
|
48
|
-
[22m[39mDATA { value: { progress: [33m0[39m }, done: [33mfalse[39m }
|
|
49
|
-
|
|
50
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mdo ... while
|
|
51
|
-
[22m[39mDATA { value: { progress: [33m0.5[39m }, done: [33mfalse[39m }
|
|
52
|
-
|
|
53
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mdo ... while
|
|
54
|
-
[22m[39mDATA { value: { progress: [33m1[39m, aborted: [33mfalse[39m }, done: [33mfalse[39m }
|
|
55
|
-
|
|
56
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mdo ... while
|
|
57
|
-
[22m[39mDATA { value: [32m'foo'[39m, done: [33mtrue[39m }
|
|
58
|
-
|
|
59
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mbreak
|
|
60
|
-
[22m[39mDATA { progress: [33m0[39m }
|
|
61
|
-
|
|
62
|
-
[90mstdout[2m | src/wrpc.test.ts[2m > [22m[2mWRPC[2m > [22m[2mabort
|
|
63
|
-
[22m[39mDATA { progress: [33m0[39m }
|
|
64
|
-
|
|
65
|
-
[32m✓[39m src/wrpc.test.ts [2m([22m[2m13 tests[22m[2m | [22m[33m1 skipped[39m[2m)[22m[33m 705[2mms[22m[39m
|
|
66
|
-
[33m[2m✓[22m[39m WRPC[2m > [22mstop [33m 503[2mms[22m[39m
|
|
67
|
-
Failed to convert coverage for file:///home/runner/work/utils/utils/packages/worker/src/wrpc.ts.
|
|
68
|
-
TypeError: Cannot read properties of undefined (reading 'endCol')
|
|
69
|
-
at range.sliceRange (file:///home/runner/work/utils/utils/node_modules/@vitest/coverage-v8/dist/provider.js:556:55)
|
|
70
|
-
at CovSource.offsetToOriginalRelative (file:///home/runner/work/utils/utils/node_modules/@vitest/coverage-v8/dist/provider.js:1987:20)
|
|
71
|
-
at V8ToIstanbul._maybeRemapStartColEndCol (file:///home/runner/work/utils/utils/node_modules/@vitest/coverage-v8/dist/provider.js:2396:93)
|
|
72
|
-
at file:///home/runner/work/utils/utils/node_modules/@vitest/coverage-v8/dist/provider.js:2305:61
|
|
73
|
-
at Array.forEach (<anonymous>)
|
|
74
|
-
at file:///home/runner/work/utils/utils/node_modules/@vitest/coverage-v8/dist/provider.js:2303:21
|
|
75
|
-
at Array.forEach (<anonymous>)
|
|
76
|
-
at V8ToIstanbul.applyCoverage (file:///home/runner/work/utils/utils/node_modules/@vitest/coverage-v8/dist/provider.js:2302:13)
|
|
77
|
-
at V8CoverageProvider.v8ToIstanbul (file:///home/runner/work/utils/utils/node_modules/@vitest/coverage-v8/dist/provider.js:2732:14)
|
|
78
|
-
at async file:///home/runner/work/utils/utils/node_modules/@vitest/coverage-v8/dist/provider.js:2825:23
|
|
79
|
-
|
|
80
|
-
[2m Test Files [22m [1m[32m2 passed[39m[22m[90m (2)[39m
|
|
81
|
-
[2m Tests [22m [1m[32m20 passed[39m[22m[2m | [22m[33m1 skipped[39m[90m (21)[39m
|
|
82
|
-
[2m Start at [22m 03:26:56
|
|
83
|
-
[2m Duration [22m 1.27s[2m (transform 170ms, setup 0ms, collect 229ms, tests 720ms, environment 1ms, prepare 228ms)[22m
|
|
84
|
-
|
|
85
|
-
[34m % [39m[2mCoverage report from [22m[33mv8[39m
|
|
86
|
-
-----------------|---------|----------|---------|---------|-------------------
|
|
87
|
-
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
|
|
88
|
-
-----------------|---------|----------|---------|---------|-------------------
|
|
89
|
-
All files | 97.49 | 91.74 | 92.68 | 97.49 |
|
|
90
|
-
index.ts | 0 | 0 | 0 | 0 | 1-3
|
|
91
|
-
worker.ts | 100 | 98.71 | 100 | 100 | 57
|
|
92
|
-
workerUtils.ts | 100 | 75 | 100 | 100 | 17,22,26,29
|
|
93
|
-
wrpc.fixture.ts | 81.81 | 78.57 | 75 | 81.81 | 70,73-76,95-99
|
|
94
|
-
wrpc.ts | 100 | 100 | 100 | 100 |
|
|
95
|
-
-----------------|---------|----------|---------|---------|-------------------
|
|
96
|
-
Updated package.json with exports
|
|
97
|
-
Updated package.json with exports
|
package/CHANGELOG.md
DELETED
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
package/src/index.ts
DELETED
package/src/worker.test.ts
DELETED
|
@@ -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
|
-
});
|