{"version":3,"file":"ChannelEmitter.cjs","names":["ChannelNetwork","ChannelNotSetError","ConnectionTimeoutError","InternalEmitterRequestType","createSuccessResponseMessage","validateMessage","isRequestMessage","InternalReceiverRequestType","createErrorResponseMessage","NotReadyError"],"sources":["../../src/channel/ChannelEmitter.ts"],"sourcesContent":["import type { ChannelNetworkOptions, PostRequestOptions } from \"./ChannelNetwork\"\nimport { ChannelNetwork } from \"./ChannelNetwork\"\nimport type { AllChannelReceiverOptions } from \"./ChannelReceiver\"\nimport { ChannelNotSetError, ConnectionTimeoutError, NotReadyError } from \"./errors\"\nimport {\n\tcreateErrorResponseMessage,\n\tcreateSuccessResponseMessage,\n\tisRequestMessage,\n\tvalidateMessage,\n} from \"./messages\"\nimport type {\n\tExtractSuccessResponseMessage,\n\tInternalEmitterTransactions,\n\tSuccessResponseMessage,\n\tTransactionsHandlers,\n\tUnknownRequestMessage,\n\tUnknownResponseMessage,\n\tUnknownTransaction,\n} from \"./types\"\nimport { InternalEmitterRequestType, InternalReceiverRequestType } from \"./types\"\n\nexport type ChannelEmitterOptions = {\n\tconnectTimeout: number\n}\n\nexport const channelEmitterDefaultOptions: ChannelEmitterOptions & Partial<ChannelNetworkOptions> =\n\t{\n\t\tconnectTimeout: 20000,\n\t\trequestIDPrefix: \"emitter-\",\n\t}\n\nexport type AllChannelEmitterOptions = ChannelEmitterOptions & ChannelNetworkOptions\n\nexport abstract class ChannelEmitter<\n\tTReceiverTransactions extends Record<string, UnknownTransaction> = Record<string, never>,\n\tTOptions extends Record<string, unknown> = Record<string, unknown>,\n\tTReceiverOptions extends Record<string, unknown> = Record<string, unknown>,\n> extends ChannelNetwork<TReceiverTransactions, ChannelEmitterOptions & TOptions> {\n\tprivate _target: HTMLIFrameElement\n\tprivate _channel: MessageChannel | null = null\n\tprotected get channel(): MessageChannel {\n\t\tif (!this._channel) {\n\t\t\tthrow new ChannelNotSetError()\n\t\t}\n\n\t\treturn this._channel\n\t}\n\tprotected set channel(channel: MessageChannel | null) {\n\t\tthis._channel = channel\n\n\t\t// Update port automatically\n\t\tif (this._channel) {\n\t\t\tthis.port = this._channel.port1\n\t\t} else {\n\t\t\tthis.port = null\n\t\t}\n\t}\n\tprivate _receiverReady = \"\"\n\tprivate _receiverReadyCallback: (() => Promise<void>) | null = null\n\tprivate _connected = false\n\tpublic get connected(): boolean {\n\t\treturn this._connected\n\t}\n\n\tconstructor(\n\t\ttarget: HTMLIFrameElement,\n\t\trequestHandlers: TransactionsHandlers<TReceiverTransactions>,\n\t\toptions: Partial<AllChannelEmitterOptions> & TOptions,\n\t) {\n\t\tsuper(requestHandlers, { ...channelEmitterDefaultOptions, ...options })\n\n\t\tthis._target = target\n\n\t\twindow.addEventListener(\"message\", (event) => {\n\t\t\tthis._onPublicMessage(event)\n\t\t})\n\t}\n\n\t/**\n\t * Initiates connection to receiver\n\t *\n\t * @param receiverOptions - Options to configure the receiver with\n\t * @param newOrigin - Indicates to the emitter that we're connecting to a new origin\n\t * @returns Success connect message\n\t */\n\tconnect(\n\t\treceiverOptions: InternalEmitterTransactions<\n\t\t\tAllChannelReceiverOptions & TReceiverOptions\n\t\t>[\"connect\"][\"request\"][\"data\"] = {},\n\t\tnewOrigin = false,\n\t): Promise<SuccessResponseMessage> {\n\t\t// Disconnect first\n\t\tthis.disconnect()\n\t\t// If changing origin we'll need to wait for receiver to be ready again\n\t\tif (newOrigin) {\n\t\t\tthis._receiverReady = \"\"\n\t\t}\n\n\t\t// Handshake promise\n\t\treturn new Promise<SuccessResponseMessage>((resolve, reject) => {\n\t\t\t// Wait for target to be loaded\n\t\t\tthis._target.addEventListener(\n\t\t\t\t\"load\",\n\t\t\t\t() => {\n\t\t\t\t\t// Throw if target doesn't allow access to content window\n\t\t\t\t\tif (!this._target.contentWindow) {\n\t\t\t\t\t\treturn reject(new Error(\"Target window is not available\"))\n\t\t\t\t\t}\n\n\t\t\t\t\tconst receiverReadyTimeout = setTimeout(() => {\n\t\t\t\t\t\treject(new ConnectionTimeoutError())\n\t\t\t\t\t}, this.options.connectTimeout)\n\n\t\t\t\t\t// Connect to target once ready\n\t\t\t\t\tconst receiverReadyCallback = async (): Promise<void> => {\n\t\t\t\t\t\t// Clear receiver ready timeout\n\t\t\t\t\t\tclearTimeout(receiverReadyTimeout)\n\n\t\t\t\t\t\t// Create new message channel (set up port automatically)\n\t\t\t\t\t\t// This is done here to prevent transferable objects neutering\n\t\t\t\t\t\t// when calling `connect()` multiple times\n\t\t\t\t\t\tthis.channel = new MessageChannel()\n\n\t\t\t\t\t\t// Conclude handshake by sending message channel port to target\n\t\t\t\t\t\tconst request = this.createRequestMessage(\n\t\t\t\t\t\t\tInternalEmitterRequestType.Connect,\n\t\t\t\t\t\t\treceiverOptions,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tconst response = await this.postRequest<\n\t\t\t\t\t\t\tInternalEmitterTransactions<\n\t\t\t\t\t\t\t\tAllChannelReceiverOptions & TReceiverOptions\n\t\t\t\t\t\t\t>[\"connect\"][\"request\"],\n\t\t\t\t\t\t\tInternalEmitterTransactions<\n\t\t\t\t\t\t\t\tAllChannelReceiverOptions & TReceiverOptions\n\t\t\t\t\t\t\t>[\"connect\"][\"response\"]\n\t\t\t\t\t\t>(request, (request) => {\n\t\t\t\t\t\t\t// Target content window is checked in previous statement\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\t\t\t\tthis._target.contentWindow!.postMessage(request, \"*\", [this.channel.port2])\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\t// Finish by aknowledging ready\n\t\t\t\t\t\tthis.postResponse(\n\t\t\t\t\t\t\tcreateSuccessResponseMessage(this._receiverReady, undefined),\n\t\t\t\t\t\t\t(response) => {\n\t\t\t\t\t\t\t\t// Target content window is checked in previous statement\n\t\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\t\t\t\t\tthis._target.contentWindow!.postMessage(response, \"*\")\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\t// If post request succeed, we're connected\n\t\t\t\t\t\tthis._connected = true\n\n\t\t\t\t\t\tresolve(response)\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this._receiverReady) {\n\t\t\t\t\t\t// If receiver is already ready, send port immediately\n\t\t\t\t\t\treceiverReadyCallback()\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Else wait for receiver to be ready\n\t\t\t\t\t\tthis._receiverReadyCallback = receiverReadyCallback\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{ once: true },\n\t\t\t)\n\t\t})\n\t}\n\n\t/** Destroys current connection to receiver if any */\n\tdisconnect(): void {\n\t\tthis._connected = false\n\t\tthis.channel = null\n\t}\n\n\t/** Handles public messages */\n\tprivate async _onPublicMessage(event: MessageEvent<unknown>): Promise<void> {\n\t\t// Return is event is not from target\n\t\tif (event.source !== this._target.contentWindow) {\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tconst message = validateMessage(event.data)\n\n\t\t\tif (isRequestMessage(message)) {\n\t\t\t\tif (this.options.debug) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.debug(event.data)\n\t\t\t\t}\n\n\t\t\t\tswitch (message.type) {\n\t\t\t\t\tcase InternalReceiverRequestType.Ready:\n\t\t\t\t\t\tthis._receiverReady = message.requestID\n\n\t\t\t\t\t\t// If emitter is waiting for receiver to be ready\n\t\t\t\t\t\tif (this._receiverReadyCallback) {\n\t\t\t\t\t\t\t// We don't await the promise directly as we need to clear the callback first\n\t\t\t\t\t\t\tconst receiverReadyCallbackPromise = this._receiverReadyCallback()\n\n\t\t\t\t\t\t\tthis._receiverReadyCallback = null\n\n\t\t\t\t\t\t\tawait receiverReadyCallbackPromise\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthis.postResponse(\n\t\t\t\t\t\t\tcreateErrorResponseMessage(message.requestID, undefined),\n\t\t\t\t\t\t\t(response) => {\n\t\t\t\t\t\t\t\t;(event.source as WindowProxy).postMessage(response, event.origin)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t)\n\t\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// No response messages are expected on public channel\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof TypeError) {\n\t\t\t\t// Ignore unknown messages\n\t\t\t} else {\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected postFormattedRequest<\n\t\tTRequest extends UnknownRequestMessage,\n\t\tTResponse extends UnknownResponseMessage,\n\t>(\n\t\ttype: TRequest[\"type\"],\n\t\tdata?: TRequest[\"data\"],\n\t\toptions: PostRequestOptions = {},\n\t): Promise<ExtractSuccessResponseMessage<TResponse>> {\n\t\tif (!this._connected) {\n\t\t\tthrow new NotReadyError(\"Emitter is not connected, use `ChannelEmitter.connect()` first\")\n\t\t}\n\n\t\treturn this.postRequest(this.createRequestMessage(type, data), undefined, options)\n\t}\n}\n"],"mappings":";;;;;AAyBA,MAAa,+BACZ;CACC,gBAAgB;CAChB,iBAAiB;CACjB;AAIF,IAAsB,iBAAtB,cAIUA,uBAAAA,eAAwE;CACjF;CACA,WAA0C;CAC1C,IAAc,UAA0B;AACvC,MAAI,CAAC,KAAK,SACT,OAAM,IAAIC,eAAAA,oBAAoB;AAG/B,SAAO,KAAK;;CAEb,IAAc,QAAQ,SAAgC;AACrD,OAAK,WAAW;AAGhB,MAAI,KAAK,SACR,MAAK,OAAO,KAAK,SAAS;MAE1B,MAAK,OAAO;;CAGd,iBAAyB;CACzB,yBAA+D;CAC/D,aAAqB;CACrB,IAAW,YAAqB;AAC/B,SAAO,KAAK;;CAGb,YACC,QACA,iBACA,SACC;AACD,QAAM,iBAAiB;GAAE,GAAG;GAA8B,GAAG;GAAS,CAAC;AAEvE,OAAK,UAAU;AAEf,SAAO,iBAAiB,YAAY,UAAU;AAC7C,QAAK,iBAAiB,MAAM;IAC3B;;;;;;;;;CAUH,QACC,kBAEkC,EAAE,EACpC,YAAY,OACsB;AAElC,OAAK,YAAY;AAEjB,MAAI,UACH,MAAK,iBAAiB;AAIvB,SAAO,IAAI,SAAiC,SAAS,WAAW;AAE/D,QAAK,QAAQ,iBACZ,cACM;AAEL,QAAI,CAAC,KAAK,QAAQ,cACjB,QAAO,uBAAO,IAAI,MAAM,iCAAiC,CAAC;IAG3D,MAAM,uBAAuB,iBAAiB;AAC7C,YAAO,IAAIC,eAAAA,wBAAwB,CAAC;OAClC,KAAK,QAAQ,eAAe;IAG/B,MAAM,wBAAwB,YAA2B;AAExD,kBAAa,qBAAqB;AAKlC,UAAK,UAAU,IAAI,gBAAgB;KAGnC,MAAM,UAAU,KAAK,qBACpBC,cAAAA,2BAA2B,SAC3B,gBACA;KACD,MAAM,WAAW,MAAM,KAAK,YAO1B,UAAU,YAAY;AAGvB,WAAK,QAAQ,cAAe,YAAY,SAAS,KAAK,CAAC,KAAK,QAAQ,MAAM,CAAC;OAC1E;AAGF,UAAK,aACJC,iBAAAA,6BAA6B,KAAK,gBAAgB,KAAA,EAAU,GAC3D,aAAa;AAGb,WAAK,QAAQ,cAAe,YAAY,UAAU,IAAI;OAEvD;AAGD,UAAK,aAAa;AAElB,aAAQ,SAAS;;AAGlB,QAAI,KAAK,eAER,wBAAuB;QAGvB,MAAK,yBAAyB;MAGhC,EAAE,MAAM,MAAM,CACd;IACA;;;CAIH,aAAmB;AAClB,OAAK,aAAa;AAClB,OAAK,UAAU;;;CAIhB,MAAc,iBAAiB,OAA6C;AAE3E,MAAI,MAAM,WAAW,KAAK,QAAQ,cACjC;AAGD,MAAI;GACH,MAAM,UAAUC,iBAAAA,gBAAgB,MAAM,KAAK;AAE3C,OAAIC,iBAAAA,iBAAiB,QAAQ,EAAE;AAC9B,QAAI,KAAK,QAAQ,MAEhB,SAAQ,MAAM,MAAM,KAAK;AAG1B,YAAQ,QAAQ,MAAhB;KACC,KAAKC,cAAAA,4BAA4B;AAChC,WAAK,iBAAiB,QAAQ;AAG9B,UAAI,KAAK,wBAAwB;OAEhC,MAAM,+BAA+B,KAAK,wBAAwB;AAElE,YAAK,yBAAyB;AAE9B,aAAM;;AAEP;KAED;AACC,WAAK,aACJC,iBAAAA,2BAA2B,QAAQ,WAAW,KAAA,EAAU,GACvD,aAAa;AACX,aAAM,OAAuB,YAAY,UAAU,MAAM,OAAO;QAEnE;AACD;;;WAKK,OAAO;AACf,OAAI,iBAAiB,WAAW,OAG/B,OAAM;;;CAKT,qBAIC,MACA,MACA,UAA8B,EAAE,EACoB;AACpD,MAAI,CAAC,KAAK,WACT,OAAM,IAAIC,eAAAA,cAAc,iEAAiE;AAG1F,SAAO,KAAK,YAAY,KAAK,qBAAqB,MAAM,KAAK,EAAE,KAAA,GAAW,QAAQ"}