UNPKG

@blockprotocol/core

Version:

Implementation of the Block Protocol Core specification for blocks and embedding applications

170 lines 6.44 kB
import { CoreBlockHandler } from "./core-block-handler.js"; import { CoreEmbedderHandler } from "./core-embedder-handler.js"; /** * The base class for creating module handlers from. * - registers the module with the CoreHandler * - provides methods for registering callbacks and sending messages */ export class ModuleHandler { constructor({ element, callbacks, moduleName, sourceType, }) { /** the CoreHandler this module is registered with, for passing messages via */ this.coreHandler = null; /** the element messages are sent via */ this.element = null; /** * the core handler is not available until element is set, so we need a queue */ this.coreQueue = []; /** * If we register callbacks prior to creating the core handler, we want to * register those on the core handler once it is available, but before we * call initialize on it, to ensure callbacks which would catch messages sent * during initialize are registered. To enable that, we have a separate queue. */ this.preCoreInitializeQueue = []; this.moduleName = moduleName; this.sourceType = sourceType; if (callbacks) { this.registerCallbacks(callbacks); } if (element) { this.initialize(element); } } /** * You only need to use this if you are constructing a module directly. * You do not need to use it if you're using a React hook or block template. * * This initializes a module with the element it will listen for messages on, * and must be called if the module was constructed without an element. */ initialize(element) { if (!this.element) { this.registerModule(element); } else if (element !== this.element) { throw new Error("Could not initialize – already initialized with another element"); } const coreHandler = this.coreHandler; if (!coreHandler) { throw new Error("Could not initialize – missing core handler"); } this.processCoreCallbackQueue(this.preCoreInitializeQueue); coreHandler.initialize(); this.processCoreQueue(); } registerModule(element) { this.checkIfDestroyed(); if (this.element) { throw new Error("Already registered"); } this.element = element; if (this.sourceType === "block") { this.coreHandler = CoreBlockHandler.registerModule({ element, module: this, }); } else if (this.sourceType === "embedder") { this.coreHandler = CoreEmbedderHandler.registerModule({ element, module: this, }); } else { throw new Error(`Provided sourceType '${this.sourceType}' must be one of 'block' or 'embedder'.`); } } /** * Unregister and clean up the module. */ destroy() { this.coreHandler?.unregisterModule({ module: this }); this.destroyed = true; } checkIfDestroyed() { if (this.destroyed) { throw new Error("Module has been destroyed. Please construct a new instance."); } } /** Register callbacks with the CoreHandler to handle incoming messages of specific types */ registerCallbacks(callbacks) { for (const [messageName, callback] of Object.entries(callbacks)) { this.registerCallback({ messageName, callback }); } } /** Remove callbacks with the CoreHandler for incoming messages of specific types */ removeCallbacks(callbacks) { for (const [messageName, callback] of Object.entries(callbacks)) { this.removeCallback({ messageName, callback }); } } /** Register a callback with the CoreHandler to handle an incoming messages of a specific type */ registerCallback({ messageName, callback, }) { this.checkIfDestroyed(); this.getRelevantQueueForCallbacks().push((coreHandler) => coreHandler.registerCallback({ callback, messageName, moduleName: this.moduleName, })); this.processCoreQueue(); } /** * When adding/removing callbacks before calling the core handler is * available, we want to queue these in a queue which will be processed before * calling initializing once we create the core handler, to ensure callbacks * are properly set up before calling initialize */ getRelevantQueueForCallbacks() { return this.coreHandler ? this.coreQueue : this.preCoreInitializeQueue; } /** Remove a callback from the CoreHandler for an incoming messages of a specific type */ removeCallback({ messageName, callback, }) { this.checkIfDestroyed(); this.getRelevantQueueForCallbacks().push((coreHandler) => coreHandler.removeCallback({ callback, messageName, moduleName: this.moduleName, })); this.processCoreQueue(); } processCoreQueue() { this.processCoreCallbackQueue(this.coreQueue); } processCoreCallbackQueue(queue) { const coreHandler = this.coreHandler; if (coreHandler) { while (queue.length) { const callback = queue.shift(); if (callback) { callback(coreHandler); } } } } /** Send a message via the CoreHandler */ sendMessage(args) { this.checkIfDestroyed(); const { message } = args; if ("respondedToBy" in args) { return new Promise((resolve, reject) => { this.coreQueue.push((coreHandler) => { coreHandler .sendMessage({ partialMessage: message, respondedToBy: args.respondedToBy, sender: this, }) .then(resolve, reject); }); this.processCoreQueue(); }); } this.coreQueue.push((coreHandler) => coreHandler.sendMessage({ partialMessage: message, sender: this, })); this.processCoreQueue(); } } //# sourceMappingURL=module-handler.js.map