import type { ComponentMessage, HostMessage, TransactionResponse, } from '../types'; /** * Callback function for handling component messages. * * @typeParam T - The type of data contained in the component message * @typeParam R - The response type carried by completion messages * @param message - The message object containing the component data and type * @public */ export type MessageCallback> = ( message: ComponentMessage, ) => void; /** * Manages message event communication through MessagePorts. * * This class handles bidirectional communication between components using MessagePorts, * allowing subscription to specific message types and dispatching messages to registered callbacks. * * @typeParam T - The type of data contained in messages received from components * @typeParam O - The type of data contained in messages sent from the host * @typeParam R - The response type carried by completion messages */ export class MessageEventManager> { private listeners: Map>> = new Map(); private port?: MessagePort; constructor() { this.handleMessage = this.handleMessage.bind(this); } /** * Sets up the message port for communication. * * Initializes the MessagePort for receiving messages by setting up the message handler * and starting the port. * * @param port - The MessagePort instance to use for communication */ setPort(port: MessagePort): void { if (!port) return; this.port?.close(); this.port = port; this.port.onmessage = this.handleMessage; this.port.start(); } /** * Registers a callback for a specific message type. * * When messages of the specified type are received, the callback will be executed * with the message data. * * @param messageType - The type of message to listen for * @param callback - The callback function to execute when matching messages are received */ on(messageType: string, callback: MessageCallback): () => void { if (!this.listeners.has(messageType)) { this.listeners.set(messageType, new Set()); } this.listeners.get(messageType)!.add(callback); return () => this.off(messageType, callback); } /** * Removes a callback for a specific message type. * * If the callback is the last one registered for the message type, * the message type entry will be removed entirely. * * @param messageType - The type of message to remove listener from * @param callback - The callback function to remove */ off(messageType: string, callback: MessageCallback): void { const callbacks = this.listeners.get(messageType); if (callbacks) { callbacks.delete(callback); if (callbacks.size === 0) { this.listeners.delete(messageType); } } } /** * Internal message handler for processing received messages. * * Validates incoming messages and dispatches them to registered callbacks * based on the message type. * * @param event - The message event containing the component message */ private handleMessage(event: MessageEvent>): void { const message = event.data; if (typeof message === 'object' && message !== null && 'type' in message) { this.emit(message); } } /** * Dispatches a message to the local listeners registered for its type. * * @param message - The component message to dispatch */ emit(message: ComponentMessage): void { this.listeners .get(message.type as string) ?.forEach((callback) => callback(message)); } /** * Sends a message through the message port * @param message The host message to send */ send(message: HostMessage): void { if (this.port) { this.port.postMessage(message); } } }