{"version":3,"file":"createBrowserMessageBus.cjs","names":["_messageGuards","require","isBrowserMessageEnvelope","envelope","isRecord","channelName","protocolVersion","getTopMessageWindow","ownerWindow","window","topWindow","top","origin","exports","createBrowserMessageBus","messageChannel","isMessage","messageWindow","publish","message","postMessage","subscribe","subscriber","handleMessageEvent","messageEvent","data","structuredClone","unsubscribe","removeEventListener","handlePageHide","pageTransitionEvent","persisted","addEventListener"],"sources":["../../../src/message-bus/createBrowserMessageBus.ts"],"sourcesContent":["/*!\n * Copyright (c) 2026-present, Okta, Inc. and/or its affiliates. All rights reserved.\n * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the \"License.\")\n *\n * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport {\n  type MessageBus,\n  type MessageSubscription,\n  type PublishMessage,\n} from \"../tools/createMessageBus.js\";\nimport { type MessageChannel } from \"./createMessageChannel.js\";\nimport { isRecord } from \"./messageGuards.js\";\n\n/**\n * What actually crosses `window.postMessage`. The message is wrapped so a subscriber can tell which channel and which protocol version it belongs to before trusting its shape.\n */\nexport type BrowserMessageEnvelope<Message> = {\n  channelName: string;\n  message: Message;\n  protocolVersion: number;\n};\n\nexport type CreateBrowserMessageBusArgs<Message> = {\n  /**\n   * The channel to publish and subscribe on, from `createMessageChannel`.\n   */\n  messageChannel: MessageChannel<Message>;\n  /**\n   * The window to publish on and listen to.\n   * @default the top window from `getTopMessageWindow`\n   */\n  messageWindow?: Window;\n  /**\n   * The window this code runs in. When it unloads, its listeners on `messageWindow` are removed, so an iframe that reloads never leaves a listener behind on the top window.\n   * @default window\n   */\n  ownerWindow?: Window;\n};\n\nconst isBrowserMessageEnvelope = (\n  envelope: unknown,\n): envelope is BrowserMessageEnvelope<unknown> =>\n  isRecord(envelope) &&\n  typeof envelope.channelName === \"string\" &&\n  typeof envelope.protocolVersion === \"number\" &&\n  \"message\" in envelope;\n\n/**\n * The window every bundle on the page publishes and listens on: the top window when it's on the same origin, otherwise this window.\n *\n * An app rendered in a same-origin iframe then reaches Unified UI Shell in the page around it. A cross-origin top window can't be listened on, so an app in a cross-origin iframe stays on its own window.\n */\nexport const getTopMessageWindow = ({\n  ownerWindow = window,\n}: {\n  ownerWindow?: Window;\n} = {}): Window => {\n  const topWindow = ownerWindow.top;\n\n  if (!topWindow || topWindow === ownerWindow) {\n    return ownerWindow;\n  }\n\n  // Reading `origin` on a cross-origin window throws, which is the browser's only way of saying it can't be reached. `window.origin` rather than `location.origin`, because an `about:blank` iframe inherits its parent's origin while its URL's origin reads `\"null\"`.\n  try {\n    return topWindow.origin === ownerWindow.origin ? topWindow : ownerWindow;\n  } catch {\n    return ownerWindow;\n  }\n};\n\n/**\n * Create a message bus that sends messages through the browser's `window.postMessage`.\n *\n * Use it instead of `createMessageBus` when the publisher and subscriber may come from separate bundles, such as an app and Unified UI Shell each loading their own copy of Odyssey. A bus from `createMessageBus` only reaches subscribers that share the same JavaScript object, but every bundle on the page shares the same top window.\n *\n * Messages arrive asynchronously, and a publisher receives its own messages. Only messages from the page's own origin, on the same channel, at the same protocol version, and passing the channel's check are delivered.\n *\n * `postMessage` copies each message with the structured clone algorithm, so a message can't contain functions, symbols, or DOM elements. Each subscriber receives a copy made in its own window.\n */\nexport const createBrowserMessageBus = <Message>({\n  messageChannel: { channelName, isMessage, protocolVersion },\n  ownerWindow = window,\n  messageWindow = getTopMessageWindow({ ownerWindow }),\n}: CreateBrowserMessageBusArgs<Message>): MessageBus<Message> => {\n  const publish: PublishMessage<Message> = (message) => {\n    messageWindow.postMessage(\n      {\n        channelName,\n        message,\n        protocolVersion,\n      } satisfies BrowserMessageEnvelope<Message>,\n      messageWindow.origin,\n    );\n  };\n\n  const subscribe: MessageSubscription<Message> = (subscriber) => {\n    const handleMessageEvent = (messageEvent: MessageEvent<unknown>) => {\n      if (\n        messageEvent.origin === messageWindow.origin &&\n        isBrowserMessageEnvelope(messageEvent.data) &&\n        messageEvent.data.channelName === channelName &&\n        messageEvent.data.protocolVersion === protocolVersion &&\n        isMessage(messageEvent.data.message)\n      ) {\n        // The top window builds the message, so its objects and arrays belong to that window. Copying it with this window's `structuredClone` hands subscribers values that pass `Array.isArray`, `instanceof`, and strict equality here.\n        subscriber(ownerWindow.structuredClone(messageEvent.data.message));\n      }\n    };\n\n    const unsubscribe = () => {\n      messageWindow.removeEventListener(\"message\", handleMessageEvent);\n      ownerWindow.removeEventListener(\"pagehide\", handlePageHide);\n    };\n\n    // A page going into the back/forward cache takes the window around it along, and comes back with no code running again to subscribe. Only a page that is really unloading leaves a listener behind.\n    const handlePageHide = (pageTransitionEvent: PageTransitionEvent) => {\n      if (!pageTransitionEvent.persisted) {\n        unsubscribe();\n      }\n    };\n\n    messageWindow.addEventListener(\"message\", handleMessageEvent);\n\n    if (messageWindow !== ownerWindow) {\n      ownerWindow.addEventListener(\"pagehide\", handlePageHide);\n    }\n\n    return unsubscribe;\n  };\n\n  return {\n    publish,\n    subscribe,\n  };\n};\n"],"mappings":";;;;;;AAkBA,IAAAA,cAAA,GAAAC,OAAA;AAlBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAoCA,MAAMC,wBAAwB,GAC5BC,QAAiB,IAEjB,IAAAC,uBAAQ,EAACD,QAAQ,CAAC,IAClB,OAAOA,QAAQ,CAACE,WAAW,KAAK,QAAQ,IACxC,OAAOF,QAAQ,CAACG,eAAe,KAAK,QAAQ,IAC5C,SAAS,IAAIH,QAAQ;AAOhB,MAAMI,mBAAmB,GAAGA,CAAC;EAClCC,WAAW,GAAGC;AAGhB,CAAC,GAAG,CAAC,CAAC,KAAa;EACjB,MAAMC,SAAS,GAAGF,WAAW,CAACG,GAAG;EAEjC,IAAI,CAACD,SAAS,IAAIA,SAAS,KAAKF,WAAW,EAAE;IAC3C,OAAOA,WAAW;EACpB;EAGA,IAAI;IACF,OAAOE,SAAS,CAACE,MAAM,KAAKJ,WAAW,CAACI,MAAM,GAAGF,SAAS,GAAGF,WAAW;EAC1E,CAAC,CAAC,MAAM;IACN,OAAOA,WAAW;EACpB;AACF,CAAC;AAACK,OAAA,CAAAN,mBAAA,GAAAA,mBAAA;AAWK,MAAMO,uBAAuB,GAAGA,CAAU;EAC/CC,cAAc,EAAE;IAAEV,WAAW;IAAEW,SAAS;IAAEV;EAAgB,CAAC;EAC3DE,WAAW,GAAGC,MAAM;EACpBQ,aAAa,GAAGV,mBAAmB,CAAC;IAAEC;EAAY,CAAC;AACf,CAAC,KAA0B;EAC/D,MAAMU,OAAgC,GAAIC,OAAO,IAAK;IACpDF,aAAa,CAACG,WAAW,CACvB;MACEf,WAAW;MACXc,OAAO;MACPb;IACF,CAAC,EACDW,aAAa,CAACL,MAChB,CAAC;EACH,CAAC;EAED,MAAMS,SAAuC,GAAIC,UAAU,IAAK;IAC9D,MAAMC,kBAAkB,GAAIC,YAAmC,IAAK;MAClE,IACEA,YAAY,CAACZ,MAAM,KAAKK,aAAa,CAACL,MAAM,IAC5CV,wBAAwB,CAACsB,YAAY,CAACC,IAAI,CAAC,IAC3CD,YAAY,CAACC,IAAI,CAACpB,WAAW,KAAKA,WAAW,IAC7CmB,YAAY,CAACC,IAAI,CAACnB,eAAe,KAAKA,eAAe,IACrDU,SAAS,CAACQ,YAAY,CAACC,IAAI,CAACN,OAAO,CAAC,EACpC;QAEAG,UAAU,CAACd,WAAW,CAACkB,eAAe,CAACF,YAAY,CAACC,IAAI,CAACN,OAAO,CAAC,CAAC;MACpE;IACF,CAAC;IAED,MAAMQ,WAAW,GAAGA,CAAA,KAAM;MACxBV,aAAa,CAACW,mBAAmB,CAAC,SAAS,EAAEL,kBAAkB,CAAC;MAChEf,WAAW,CAACoB,mBAAmB,CAAC,UAAU,EAAEC,cAAc,CAAC;IAC7D,CAAC;IAGD,MAAMA,cAAc,GAAIC,mBAAwC,IAAK;MACnE,IAAI,CAACA,mBAAmB,CAACC,SAAS,EAAE;QAClCJ,WAAW,CAAC,CAAC;MACf;IACF,CAAC;IAEDV,aAAa,CAACe,gBAAgB,CAAC,SAAS,EAAET,kBAAkB,CAAC;IAE7D,IAAIN,aAAa,KAAKT,WAAW,EAAE;MACjCA,WAAW,CAACwB,gBAAgB,CAAC,UAAU,EAAEH,cAAc,CAAC;IAC1D;IAEA,OAAOF,WAAW;EACpB,CAAC;EAED,OAAO;IACLT,OAAO;IACPG;EACF,CAAC;AACH,CAAC;AAACR,OAAA,CAAAC,uBAAA,GAAAA,uBAAA","ignoreList":[]}