From 2de9f97b58d5d9672d1ab411e997d68be4c7a404 Mon Sep 17 00:00:00 2001 From: Wolfger Schramm Date: Wed, 26 Jun 2024 09:33:22 +0200 Subject: [PATCH] implement ShadowEnv.onMessageToView --- packages/shadow-ents-e2e/public/mod-hello.js | 3 ++- packages/shadow-ents/src/constants.ts | 5 +++++ packages/shadow-ents/src/entities/Entity.ts | 8 ++++---- packages/shadow-ents/src/entities/Kernel.ts | 14 +++++--------- .../src/entities/ShadowObjectsGuide.md | 4 ++++ .../shadow-ents/src/view/IShadowObjectEnvProxy.ts | 3 +++ .../shadow-ents/src/view/LocalShadowObjectEnv.ts | 12 ++++++++++-- packages/shadow-ents/src/view/RemoteWorkerEnv.ts | 12 ++++++++---- packages/shadow-ents/src/view/ShadowEnv.ts | 9 +++++++++ packages/shadow-ents/src/worker/MessageRouter.ts | 15 +++++++++------ 10 files changed, 59 insertions(+), 26 deletions(-) diff --git a/packages/shadow-ents-e2e/public/mod-hello.js b/packages/shadow-ents-e2e/public/mod-hello.js index abe4c77..a4f424b 100644 --- a/packages/shadow-ents-e2e/public/mod-hello.js +++ b/packages/shadow-ents-e2e/public/mod-hello.js @@ -7,7 +7,8 @@ function foo({entity, useProperty}) { console.log('foo.xyz changed to', val); }); - entity.dispatchLocalEvent('helloFromFoo', {xyz: xyz()}); + // entity.dispatchViewEvent('helloFromFoo', {xyz: xyz()}); + entity.dispatchMessageToView('helloFromFoo', {xyz: xyz()}); } export const shadowObjects = { diff --git a/packages/shadow-ents/src/constants.ts b/packages/shadow-ents/src/constants.ts index a5280ac..2af9fff 100644 --- a/packages/shadow-ents/src/constants.ts +++ b/packages/shadow-ents/src/constants.ts @@ -37,6 +37,11 @@ export const AppliedChangeTrail = 'appliedChangeTrail'; export const ImportedModule = 'importedModule'; export const Destroyed = 'destroyed'; +/** + * The `messageToView` event is fired when the kernel receives a message from an entity (to its view component counterpart) + */ +export const MessageToView = 'messageToView'; + export const WorkerLoadTimeout = 16000; export const WorkerReadyTimeout = 16000; // TODO remove WorkerReadyTimeout export const WorkerConfigureTimeout = 16000; diff --git a/packages/shadow-ents/src/entities/Entity.ts b/packages/shadow-ents/src/entities/Entity.ts index 6262062..df9b38f 100644 --- a/packages/shadow-ents/src/entities/Entity.ts +++ b/packages/shadow-ents/src/entities/Entity.ts @@ -195,18 +195,18 @@ export class Entity extends Eventize { } } - dispatchEventToView(type: string, data?: unknown, transferables?: Transferable[]) { + dispatchMessageToView(type: string, data?: unknown, transferables?: Transferable[]) { this.#kernel.dispatchMessageToView({uuid: this.#uuid, type, data, transferables}); } - dispatchLocalEvents(events: IComponentEvent[]) { + dispatchViewEvents(events: IComponentEvent[]) { for (const {type, data} of events) { this.emit(onViewEvent, type, data); } } - dispatchLocalEvent(type: string, data: unknown) { - this.dispatchLocalEvents([{type, data}]); + dispatchViewEvent(type: string, data: unknown) { + this.dispatchViewEvents([{type, data}]); } #getPropSignal(key: string): SignalObject { diff --git a/packages/shadow-ents/src/entities/Kernel.ts b/packages/shadow-ents/src/entities/Kernel.ts index b23979c..f2b3540 100644 --- a/packages/shadow-ents/src/entities/Kernel.ts +++ b/packages/shadow-ents/src/entities/Kernel.ts @@ -8,7 +8,7 @@ import { type CompareFunc, type SignalReader, } from '@spearwolf/signalize'; -import {ComponentChangeType} from '../constants.js'; +import {ComponentChangeType, MessageToView} from '../constants.js'; import type {IComponentChangeType, IComponentEvent, ShadowObjectConstructor, ShadowObjectType, SyncEvent} from '../types.js'; import {Entity} from './Entity.js'; import {Registry} from './Registry.js'; @@ -41,12 +41,6 @@ enum ShadowObjectAction { * Which shadow-objects are created is determined by the token. */ export class Kernel extends Eventize { - /** - * The `messageToView` event is fired when the kernel receives a message from an entity (to its view component counterpart) - * XXX kernel message event is not used yet - */ - static MessageToView = 'messageToView'; - registry: Registry; #entities: Map = new Map(); @@ -209,7 +203,7 @@ export class Kernel extends Eventize { } dispatchEventsToEntity(uuid: string, events: IComponentEvent[]): void { - this.getEntity(uuid)?.dispatchLocalEvents(events); + this.getEntity(uuid)?.dispatchViewEvents(events); } changeProperties(uuid: string, properties: [string, unknown][]): void { @@ -229,7 +223,9 @@ export class Kernel extends Eventize { } dispatchMessageToView(message: MessageToViewEvent): void { - this.emit(Kernel.MessageToView, message); + queueMicrotask(() => { + this.emit(MessageToView, message); + }); } /** diff --git a/packages/shadow-ents/src/entities/ShadowObjectsGuide.md b/packages/shadow-ents/src/entities/ShadowObjectsGuide.md index a14b08c..d38e7cb 100644 --- a/packages/shadow-ents/src/entities/ShadowObjectsGuide.md +++ b/packages/shadow-ents/src/entities/ShadowObjectsGuide.md @@ -61,6 +61,10 @@ The entity reference. The entity is the logical container for the shadow objects _TODO:_ describe entity events +> **entity.uuid:** _string_ + +The uuid of the entity. which is also the uuid of the [view component](../view/ViewComponent.md). + > **onDestroy:** `(callback: () => any)` _TODO:_ add description diff --git a/packages/shadow-ents/src/view/IShadowObjectEnvProxy.ts b/packages/shadow-ents/src/view/IShadowObjectEnvProxy.ts index f57bd22..3facd32 100644 --- a/packages/shadow-ents/src/view/IShadowObjectEnvProxy.ts +++ b/packages/shadow-ents/src/view/IShadowObjectEnvProxy.ts @@ -1,3 +1,4 @@ +import type {MessageToViewEvent} from '../core.js'; import type {ChangeTrailType} from '../types.js'; export interface IShadowObjectEnvProxy { @@ -8,4 +9,6 @@ export interface IShadowObjectEnvProxy { applyChangeTrail(data: ChangeTrailType): Promise; destroy(): void; + + onMessageToView?: (event: Omit) => any; } diff --git a/packages/shadow-ents/src/view/LocalShadowObjectEnv.ts b/packages/shadow-ents/src/view/LocalShadowObjectEnv.ts index 6bf1039..7034e4c 100644 --- a/packages/shadow-ents/src/view/LocalShadowObjectEnv.ts +++ b/packages/shadow-ents/src/view/LocalShadowObjectEnv.ts @@ -1,5 +1,5 @@ -import {ShadowObjectsExport} from '../constants.js'; -import {Kernel} from '../entities/Kernel.js'; +import {MessageToView, ShadowObjectsExport} from '../constants.js'; +import {Kernel, type MessageToViewEvent} from '../entities/Kernel.js'; import type {Registry} from '../entities/Registry.js'; import {importModule} from '../entities/importModule.js'; import {toUrlString} from '../toUrlString.js'; @@ -18,6 +18,14 @@ export class LocalShadowObjectEnv implements IShadowObjectEnvProxy { constructor(registry?: Registry) { this.kernel = new Kernel(registry); + + this.kernel.on(MessageToView, (message: MessageToViewEvent) => { + if ((this as IShadowObjectEnvProxy).onMessageToView != null) { + const {type, uuid} = message; + const data = structuredClone(message.data, {transfer: message.transferables}); + (this as IShadowObjectEnvProxy).onMessageToView({type, uuid, data}); + } + }); } start(): Promise { diff --git a/packages/shadow-ents/src/view/RemoteWorkerEnv.ts b/packages/shadow-ents/src/view/RemoteWorkerEnv.ts index 1260f18..0ad75e1 100644 --- a/packages/shadow-ents/src/view/RemoteWorkerEnv.ts +++ b/packages/shadow-ents/src/view/RemoteWorkerEnv.ts @@ -7,6 +7,7 @@ import { Destroyed, ImportedModule, Loaded, + MessageToView, WorkerChangeTrailTimeout, WorkerConfigureTimeout, WorkerDestroyTimeout, @@ -65,7 +66,7 @@ export class RemoteWorkerEnv implements IShadowObjectEnvProxy { return this.workerLoaded.then(() => { if (this.isDestroyed) { - throw 'RemoteWorkerEnv: worker was destoyed'; + throw 'RemoteWorkerEnv: worker was destroyed'; } return undefined; }); @@ -77,7 +78,7 @@ export class RemoteWorkerEnv implements IShadowObjectEnvProxy { await waitForMessageOfType(worker, Loaded, WorkerLoadTimeout); if (this.isDestroyed) { - throw 'RemoteWorkerEnv: worker was destoyed'; + throw 'RemoteWorkerEnv: worker was destroyed'; } worker.addEventListener('message', this.onMessageFromWorker.bind(this)); @@ -93,8 +94,11 @@ export class RemoteWorkerEnv implements IShadowObjectEnvProxy { } onMessageFromWorker(event: MessageEvent) { - // TODO implement onMessageFromWorker - console.debug('RemoteWorkerEnv: message from worker', event); + if (event.data?.type === MessageToView) { + (this as IShadowObjectEnvProxy).onMessageToView?.(event.data.data); + } else { + console.debug('RemoteWorkerEnv: message from worker', event); + } } applyChangeTrail(changeTrail: ChangeTrailType): Promise { diff --git a/packages/shadow-ents/src/view/ShadowEnv.ts b/packages/shadow-ents/src/view/ShadowEnv.ts index f0c34ac..c9637db 100644 --- a/packages/shadow-ents/src/view/ShadowEnv.ts +++ b/packages/shadow-ents/src/view/ShadowEnv.ts @@ -1,6 +1,7 @@ import {eventize, type EventizeApi} from '@spearwolf/eventize'; import {createEffect, type SignalReader} from '@spearwolf/signalize'; import {signal, signalReader} from '@spearwolf/signalize/decorators'; +import type {MessageToViewEvent} from '../core.js'; import type {ComponentContext} from './ComponentContext.js'; import type {IShadowObjectEnvProxy} from './IShadowObjectEnvProxy.js'; @@ -69,6 +70,10 @@ export class ShadowEnv { const prevProxy = this.#shaObjEnvProxy; this.#shaObjEnvProxy = proxy ?? undefined; + if (this.#shaObjEnvProxy) { + this.#shaObjEnvProxy.onMessageToView = this.#onMessageToView.bind(this); + } + if (prevProxy) { prevProxy.destroy(); } @@ -124,4 +129,8 @@ export class ShadowEnv { } } } + + #onMessageToView(event: Omit) { + console.log('ShadowEnv: onMessageToView', event.type, event.data); + } } diff --git a/packages/shadow-ents/src/worker/MessageRouter.ts b/packages/shadow-ents/src/worker/MessageRouter.ts index ec4da14..4740c6e 100644 --- a/packages/shadow-ents/src/worker/MessageRouter.ts +++ b/packages/shadow-ents/src/worker/MessageRouter.ts @@ -6,10 +6,11 @@ import { Destroyed, ImportedModule, Init, + MessageToView, Ready, ShadowObjectsExport, } from '../constants.js'; -import {Kernel} from '../entities/Kernel.js'; +import {Kernel, type MessageToViewEvent} from '../entities/Kernel.js'; import {shadowObjects} from '../entities/ShadowObject.js'; import {importModule} from '../entities/importModule.js'; import {toUrlString} from '../toUrlString.js'; @@ -47,11 +48,7 @@ export class MessageRouter { this.postMessage = options?.postMessage ?? self.postMessage.bind(self); - this.kernel.on(Kernel.MessageToView, (event) => { - console.log('[MessageRouter] TODO messageToView', event); - // TODO postMessage to view-component - }); - // TODO unsubscribe + this.kernel.on(MessageToView, 'onMessageToView', this); } route(event: MessageEvent) { @@ -78,6 +75,11 @@ export class MessageRouter { } } + onMessageToView(event: MessageToViewEvent) { + const {transferables: transfer, ...data} = event; + this.postMessage({type: MessageToView, data}, {transfer}); + } + async #configure(data: ConfigurePayloadData) { try { const module = await import(/* @vite-ignore */ toUrlString(data.importModule)); @@ -125,6 +127,7 @@ export class MessageRouter { #onDestroy(data: any) { console.debug('[MessageRouter] on destroy', data); + this.kernel.off(this); this.#importedModules.clear(); this.postMessage({type: Destroyed}); }