{"version":3,"file":"message-channel.cjs","sourceRoot":"","sources":["../../src/browser/message-channel.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * This module establishes a simple protocol for creating a MessageChannel between two\n * realms, as follows:\n * 1. The sending realm asserts that the receiving realm is ready to receive messages,\n * either by creating the realm itself (for example, by appending an iframe to the DOM),\n * or via some other means.\n * 2. The receiving realm calls `receiveMessagePort()` on startup in one of its scripts.\n * The script element in question should not have the `async` attribute.\n * 3. The sending realm calls `initializeMessageChannel()` which sends a message port to\n * the receiving realm. When the returned promise resolves, the sending realm and the\n * receiving realm have established a message channel.\n *\n * @module MessageChannel utilities\n */\n\nimport { makePromiseKit } from '@endo/promise-kit';\nimport { stringify } from '@metamask/kernel-utils';\nimport { isObject } from '@metamask/utils';\n\nexport const MessageType = {\n  Initialize: 'INIT_MESSAGE_CHANNEL',\n  Acknowledge: 'ACK_MESSAGE_CHANNEL',\n} as const;\n\ntype InitializeMessage = { type: typeof MessageType.Initialize };\ntype AcknowledgeMessage = { type: typeof MessageType.Acknowledge };\n\nconst isInitMessage = (\n  event: MessageEvent,\n): event is MessageEvent<InitializeMessage> =>\n  isObject(event.data) &&\n  event.data.type === MessageType.Initialize &&\n  Array.isArray(event.ports) &&\n  event.ports.length === 1 &&\n  event.ports[0] instanceof MessagePort;\n\nconst isAckMessage = (value: unknown): value is AcknowledgeMessage =>\n  isObject(value) && value.type === MessageType.Acknowledge;\n\n/**\n * Creates a message channel and sends one of the ports to the receiving realm. The\n * realm must be loaded, and it must have called {@link receiveMessagePort} to\n * receive the remote message port. Rejects if the first message received over the\n * channel is not an {@link AcknowledgeMessage}.\n *\n * A `portHandler` function can be specified to synchronously perform any work with\n * the local message port before the promise resolves.\n *\n * @param postMessage - A bound method for posting a message to the receiving realm.\n * Must be able to transfer a message port.\n * @param portHandler - A function that receives the local message port and returns a\n * value. Returns the local message port by default.\n * @returns A promise that resolves with the value returned by `portHandler`.\n */\nexport async function initializeMessageChannel<Result = MessagePort>(\n  postMessage: (message: unknown, transfer: Transferable[]) => void,\n  portHandler: (port: MessagePort) => Result = (port) => port as Result,\n): Promise<Result> {\n  const { port1, port2 } = new MessageChannel();\n\n  const { promise, resolve, reject } = makePromiseKit<Result>();\n  const listener = (message: MessageEvent): void => {\n    if (!isAckMessage(message.data)) {\n      reject(\n        new Error(\n          `Received unexpected message via message port:\\n${stringify(\n            message.data,\n          )}`,\n        ),\n      );\n      return;\n    }\n\n    port1.removeEventListener('message', listener);\n    resolve(portHandler(port1));\n  };\n\n  port1.addEventListener('message', listener);\n  port1.start();\n\n  const initMessage: InitializeMessage = {\n    type: MessageType.Initialize,\n  };\n  postMessage(initMessage, [port2]);\n\n  return promise.catch((error) => {\n    port1.close();\n    port1.removeEventListener('message', listener);\n    throw error;\n  });\n}\n\ntype Listener = (message: MessageEvent) => void;\n\n/**\n * Receives a message port from the sending realm, and sends an {@link AcknowledgeMessage}\n * over the port. Should be called in a script _without_ the `async` attribute on startup.\n * The sending realm must call {@link initializeMessageChannel} to send the message port\n * after this realm has loaded. Ignores any message events dispatched on the local\n * realm that are not an {@link InitializeMessage}.\n *\n * A `portHandler` function can be specified to synchronously perform any work with the\n * received port before the promise resolves.\n *\n * @param addListener - A bound method to add a message event listener to the sending\n * realm.\n * @param removeListener - A bound method to remove a message event listener from the\n * sending realm.\n * @param portHandler - A function that receives the message port and returns a value.\n * Returns the message port by default.\n * @returns A promise that resolves with the value returned by `portHandler`.\n */\nexport async function receiveMessagePort<Result = MessagePort>(\n  addListener: (listener: Listener) => void,\n  removeListener: (listener: Listener) => void,\n  portHandler: (port: MessagePort) => Result = (port) => port as Result,\n): Promise<Result> {\n  const { promise, resolve } = makePromiseKit<Result>();\n\n  const listener = (message: MessageEvent): void => {\n    if (!isInitMessage(message)) {\n      return;\n    }\n    removeListener(listener);\n\n    const port = message.ports[0] as MessagePort;\n    const ackMessage: AcknowledgeMessage = { type: MessageType.Acknowledge };\n    port.postMessage(ackMessage);\n    resolve(portHandler(port));\n  };\n\n  addListener(listener);\n  return promise;\n}\n"]}