diff --git a/.vscode/launch.json b/.vscode/launch.json index 4db7965..518fcbe 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ { "type": "chrome", "request": "launch", - "name": "Launch Chrome against localhost", + "name": "Chrome", "url": "http://localhost:23001", "webRoot": "${workspaceFolder}", "userDataDir": "${workspaceFolder}/.vscode/profile" diff --git a/CHANGELOG.md b/CHANGELOG.md index ed28e1c..8097409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 3.1.0 - 2024-10-01 + +- ComChannelEndpoint has been extracted from WorkerTask + - Now Worker, MessageChannel or DedicatedWorkerGlobalScope can be channel endpoints. Both ends of the communication channel can use the same implementation to send message and await responses if needed. + - Added new example **HelloWorldComChannelEndpoint** + ## 3.0.0 - 2024-01-05 - Make the worker lifecylce no longer mandatory if not using `WorkerTaskDirector`. diff --git a/README.md b/README.md index 28c87f0..1a10dd8 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Build applications with workers with less boiler plate code. There are multiple examples available demonstarting the features described above (listed from simpler to more advanced): - Using [wtd-core](https://www.npmjs.com/package/wtd-core) only: + - **ComChannelEndpoint: Hello World**: [html](https://github.com/kaisalmen/wtd/blob/main/packages/examples/helloWorldComChannelEndpoint.html), [ts](https://github.com/kaisalmen/wtd/blob/main/packages/examples/src/helloWorld/HelloWorldComChannelEndpoint.ts), [worker](https://github.com/kaisalmen/wtd/blob/main/packages/examples/src/worker/HelloWorldComChannelEndpointWorker.ts) - **WorkerTask: Hello World**: [html](https://github.com/kaisalmen/wtd/blob/main/packages/examples/helloWorldWorkerTask.html), [ts](https://github.com/kaisalmen/wtd/blob/main/packages/examples/src/helloWorld/HelloWorldWorkerTask.ts), [worker](https://github.com/kaisalmen/wtd/blob/main/packages/examples/src/worker/HelloWorldWorker.ts) - **WorkerTaskDirector: Hello World**: [html](https://github.com/kaisalmen/wtd/blob/main/packages/examples/helloWorldWorkerTaskDirector.html), [ts](https://github.com/kaisalmen/wtd/blob/main/packages/examples/src/helloWorld/helloWorldWorkerTaskDirector.ts), [worker](https://github.com/kaisalmen/wtd/blob/main/packages/examples/src/worker/HelloWorldWorker.ts) - **WorkerTask: Inter-Worker Communication**: [html](https://github.com/kaisalmen/wtd/blob/main/packages/examples/workerCom.html), [ts](https://github.com/kaisalmen/wtd/blob/main/packages/examples/src/com/WorkerCom.ts), **Worker**: [1](https://github.com/kaisalmen/wtd/blob/main/packages/examples/src/worker/Com1Worker.ts) and [2](https://github.com/kaisalmen/wtd/blob/main/packages/examples/src/worker/Com2Worker.ts) diff --git a/index.html b/index.html index 417f399..fb58856 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,8 @@

Examples

+ ComChannelEndpoint: Hello World +
WorkerTask: Hello World
WorkerTaskDirector: Hello World diff --git a/packages/examples/build/vite.config.HelloWorldComChannelEndpointWorker.ts b/packages/examples/build/vite.config.HelloWorldComChannelEndpointWorker.ts new file mode 100644 index 0000000..3df439e --- /dev/null +++ b/packages/examples/build/vite.config.HelloWorldComChannelEndpointWorker.ts @@ -0,0 +1,17 @@ +import path from 'path'; +import { defineConfig } from 'vite'; + +const config = defineConfig({ + build: { + lib: { + entry: path.resolve(__dirname, '../src/worker/HelloWorldComChannelEndpointWorker.ts'), + name: 'HelloWorldComChannelEndpointWorker', + fileName: (format) => `HelloWorldComChannelEndpointWorker-${format}.js`, + formats: ['es', 'iife'] + }, + outDir: 'src/worker/generated', + emptyOutDir: false + } +}); + +export default config; diff --git a/packages/examples/helloWorldComChannelEndpoint.html b/packages/examples/helloWorldComChannelEndpoint.html new file mode 100644 index 0000000..ab88dd7 --- /dev/null +++ b/packages/examples/helloWorldComChannelEndpoint.html @@ -0,0 +1,15 @@ + + + + ComChannelEndpoint: Hello World + + + + + + + +
ComChannelEndpoint: Hello World
+ + + diff --git a/packages/examples/scripts/buildWorker.mts b/packages/examples/scripts/buildWorker.mts index d5efbac..6f4a6fa 100644 --- a/packages/examples/scripts/buildWorker.mts +++ b/packages/examples/scripts/buildWorker.mts @@ -3,6 +3,9 @@ import shell from 'shelljs'; shell.rm('-f', './src/worker/generated/HelloWorldWorker*.js'); shell.exec('vite -c build/vite.config.HelloWorldWorker.ts build'); +shell.rm('-f', './src/worker/generated/HelloWorldComChannelEndpointWorker*.js'); +shell.exec('vite -c build/vite.config.HelloWorldComChannelEndpointWorker.ts build'); + shell.rm('-f', './src/worker/generated/Com1Worker*.js'); shell.exec('vite -c build/vite.config.Com1Worker.ts build'); diff --git a/packages/examples/src/helloWorld/HelloWorldComChannelEndpoint.ts b/packages/examples/src/helloWorld/HelloWorldComChannelEndpoint.ts new file mode 100644 index 0000000..e74e0ca --- /dev/null +++ b/packages/examples/src/helloWorld/HelloWorldComChannelEndpoint.ts @@ -0,0 +1,47 @@ +import { WorkerMessage, ComChannelEndpoint, RawPayload } from 'wtd-core'; + +/** + * Hello World example just using the ComChannelEndpoint + */ +class HelloWorldComChannelEndpointExample { + + async run() { + const url = new URL(import.meta.env.DEV ? '../worker/HelloWorldComChannelEndpointWorker.ts' : '../worker/generated/HelloWorldComChannelEndpointWorker-es.js', import.meta.url); + const endpoint = new ComChannelEndpoint({ + endpointName: 'HelloWorldWorker', + endpointId: 1, + endpointConfig: { + $type: 'WorkerConfigParams', + url, + workerType: 'module', + }, + verbose: true + }); + endpoint.connect(); + + try { + const t0 = performance.now(); + + const result = await endpoint.sentMessage({ + message: WorkerMessage.createNew({ + cmd: 'hello_world' + }), + awaitAnswer: true, + answer: 'hello_world_confirm' + },); + + const rawPayload = result.payloads[0] as RawPayload; + const answer = `Worker said: command: ${result.cmd} message: ${rawPayload.message.raw?.hello}`; + const t1 = performance.now(); + + const msg = `${answer}\nWorker execution has been completed after ${t1 - t0}ms.`; + console.log(msg); + alert(msg); + } catch (e) { + console.error(e); + } + } +} + +const app = new HelloWorldComChannelEndpointExample(); +app.run(); diff --git a/packages/examples/src/worker/HelloWorldComChannelEndpointWorker.ts b/packages/examples/src/worker/HelloWorldComChannelEndpointWorker.ts new file mode 100644 index 0000000..d6cbe04 --- /dev/null +++ b/packages/examples/src/worker/HelloWorldComChannelEndpointWorker.ts @@ -0,0 +1,41 @@ +import { RawPayload, WorkerMessage, ComRouter, ComChannelEndpoint } from 'wtd-core'; + +/// + +declare const self: DedicatedWorkerGlobalScope; + +class ExampleComRouterWorker implements ComRouter { + + private endpointFs?: ComChannelEndpoint; + + setComChannelEndpoint(comChannelEndpoint: ComChannelEndpoint): void { + this.endpointFs = comChannelEndpoint; + } + + async hello_world(message: WorkerMessage) { + // burn some time + for (let i = 0; i < 25000000; i++) { + i++; + } + + await this.endpointFs?.sentAnswer({ + message: WorkerMessage.createFromExisting(message, { + overrideCmd: 'hello_world_confirm', + overridePayloads: new RawPayload({ + hello: 'Hello! I just incremented "i" 25 million times.' + }) + }), + awaitAnswer: false + }); + } +} + +new ComChannelEndpoint({ + endpointId: 2000, + endpointConfig: { + $type: 'WorkerConfigDirect', + worker: self + }, + verbose: true, + endpointName: 'HelloWorldComChannelEndpointWorker' +}).connect(new ExampleComRouterWorker()); diff --git a/packages/examples/src/worker/InfiniteWorkerExternalGeometry.ts b/packages/examples/src/worker/InfiniteWorkerExternalGeometry.ts index bc032a9..ecb22aa 100644 --- a/packages/examples/src/worker/InfiniteWorkerExternalGeometry.ts +++ b/packages/examples/src/worker/InfiniteWorkerExternalGeometry.ts @@ -16,9 +16,9 @@ class InfiniteWorkerExternalGeometry implements WorkerTaskWorker { private bufferGeometry?: BufferGeometry = undefined; init(message: WorkerMessage) { - const wtm = WorkerMessage.unpack(message, false); - if (wtm.payloads.length > 0) { - this.bufferGeometry = (wtm.payloads[0] as MeshPayload).message.bufferGeometry as BufferGeometry; + const wm = WorkerMessage.unpack(message, false); + if (wm.payloads.length > 0) { + this.bufferGeometry = (wm.payloads[0] as MeshPayload).message.bufferGeometry as BufferGeometry; } const initComplete = WorkerMessage.createFromExisting(message, { diff --git a/packages/wtd-core/src/ComChannelEndpoint.ts b/packages/wtd-core/src/ComChannelEndpoint.ts index 1ab724e..81dd1ba 100644 --- a/packages/wtd-core/src/ComChannelEndpoint.ts +++ b/packages/wtd-core/src/ComChannelEndpoint.ts @@ -144,6 +144,11 @@ export class ComChannelEndpoint { }); } + sentAnswer(def: WorkerMessageDef): Promise { + def.message.answer = true; + return this.sentMessage(def); + } + protected updateAwaitHandlers(wm: WorkerMessage, awaitHandlers: AwaitHandler[]) { wm.endpointdId = this.endpointId; wm.uuid = this.buildUuid(); diff --git a/packages/wtd-core/src/WorkerMessage.ts b/packages/wtd-core/src/WorkerMessage.ts index a8344f2..70bc128 100644 --- a/packages/wtd-core/src/WorkerMessage.ts +++ b/packages/wtd-core/src/WorkerMessage.ts @@ -46,6 +46,7 @@ export class WorkerMessage { static createFromExisting(message: WorkerMessage, options?: { overrideCmd?: WorkerCommand, overrideUuid?: string, + overridePayloads?: Payload | Payload[], answer?: boolean }) { const wm = WorkerMessage.createNew(message); @@ -59,6 +60,8 @@ export class WorkerMessage { if (options?.answer !== undefined) { wm.answer = options.answer; } + wm.addPayload(options?.overridePayloads); + return wm; } diff --git a/vite.config.ts b/vite.config.ts index 18a5117..724f859 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,9 +8,9 @@ export default defineConfig(({ command }) => { rollupOptions: { input: { main: path.resolve(__dirname, 'index.html'), - helloWorld: path.resolve(__dirname, 'packages/examples/helloWorld.html'), - helloWorldStandard: path.resolve(__dirname, 'packages/examples/helloWorldStandard.html'), helloWorldWorkerTask: path.resolve(__dirname, 'packages/examples/helloWorldWorkerTask.html'), + helloWorldComChannelEndpoint: path.resolve(__dirname, 'packages/examples/helloWorldComChannelEndpoint.html'), + helloWorldWorkerTaskDirector: path.resolve(__dirname, 'packages/examples/helloWorldWorkerTaskDirector.html'), transferables: path.resolve(__dirname, 'packages/examples/transferables.html'), threejs: path.resolve(__dirname, 'packages/examples/threejs.html'), potentiallyInfinite: path.resolve(__dirname, 'packages/examples/potentially_infinite.html')