export declare type PostMessageTarget = Window | MessagePort | Worker | ServiceWorker | Messagable; interface Messagable { postMessage: typeof Worker.prototype.postMessage; } export interface messageReceiver { addEventListener: typeof Window.prototype.addEventListener; removeEventListener: typeof Window.prototype.removeEventListener; } export interface PostdocConfig { origin: string; onMessage: (message: MessageEvent) => unknown; inferTarget: boolean; messageTarget?: PostMessageTarget; messageReceiver?: messageReceiver; } /** * PostDoc is a utility class for sending and receiving messages between * MessageEventSources (e.g. a Window or anything with a standard postMessage * format * `postMessage(message: any, options?: StructuredSerializeOptions | undefined)` * ). * * It is designed to be instantiated at both ends of a message source and * automatically set up the necessary event listeners and create a * MessageChannel. What differentiates this class from other libraries is that * it will readily accept new handshakes from the same target, thus allowing * the case of a reloading iframe to "reconnect" to the external frame via * handshake. * * @example * ```ts * /// In Window * import {PostDoc} from 'postdoc'; * * // instantiate * const onMessage = (message: MessageEvent) => * console.log('Parent received:', message.data); * const postdoc = new PostDoc({ * messageReciever: window, * // note: messageTarget is optional if `inferTarget` is `true`. If * // `inferTarget` is `true` an `messageTarget` is not included PostDoc will * // automatically set it to the first MessageEventSource that fires a * // handshake message to the given messageReceiver. * messageTarget: iframe.contentWindow, * onMessage, * // Origin is also optional and defaults to '*' if posting to a Window. * origin: 'https://my-postmessage-origin.com' * }); * * // await handshake and postMessage is safe to call * await postdoc.handshake; * postdoc.postMessage('Hello from window'); * * /// In Iframe that may reload constantly * import {PostDoc} from 'postdoc'; * * // Instantiate PostDoc in iframe * const onMessage = (message: MessageEvent) => * console.log('Child received:', message.data); * const postdoc = new PostDoc({ * messageReciever: window, * messageTarget: window.top!, * onMessage * }); * * // await handshake and postMessage is safe to call * await postdoc.handshake; * postdoc.postMessage('Hello from iframe'); * ``` */ export declare class PostDoc { private _messageTarget; private _messageReceiver; private _origin; private _resolveHandshake; private _messageChannel; private _messagePort; private _onMessage; private _handshake; private _handshakeComplete; private _messagePortDirty; private _inferTarget; /** * Promise that resolves when the handshake is complete. */ get handshake(): Promise; /** * Function to be called when a message is received through the PostDoc * message channel. */ get onMessage(): (message: MessageEvent) => unknown; set onMessage(newOnMessage: (message: MessageEvent) => unknown); set messageReceiver(newReceiver: messageReceiver | null); /** * MessageEventSource that should be listended to for handshake messages. */ get messageReceiver(): messageReceiver | null; set messageTarget(newTarget: PostMessageTarget | null); /** * Target for handshake messages. If `inferTarget` is `true` and * `messageTarget` is omitted, `messageTarget` will be set to the first * `MessageEventSource` that fires a handshake message to the given * `messageReceiver`. * * Note, if handshake is fired to receiver before PostDoc is instantiated, the * handshake will not resolve. This can be prevented in most cases by setting * messageTarget in both message sources. Additionally, `messageReceiver` * should be set before `messageTarget` or set in the constructor. */ get messageTarget(): PostMessageTarget | null; /** * @param config Optional configuration object. All items other than `origin` * can be set as properties after instantiation, but `messageTarget` should * not be set before `messageReceiver` is set or else postdoc may miss the * handskake acknowledgement message. `origin` defaults to `'*'` and is * only used if `messageTarget` is a `Window`. */ constructor(config?: Partial); /** * Resets the handshake if already completed and sets the handshake promise to * a new, unresolved promise. * * @param force If true, will reset the handshake even if it not complete. * This is useful for initialization. */ private _resetHandshake; /** * Called when the messageReceiver receives a `message` event and executes * handshake logic if redceived. * * @param event The MessageEvent that was received. */ private _onMessageReceiverMessage; /** * Posts a message to the messageTarget. If it is a Window, it will post the * message with the origin of `this.origin`. Necessary to normalize the * signatures of `Window.postMessage` and * `(MessagePort|ServiceWorker).postMessage`. * * @param message The message to be posted to messageTarget. * @param transfer Transferables to be transferred to messageTarget. */ private _postMessageToTarget; /** * Triggered whenever a handshake message request is received. * * Creates a new message channel, cleans up previous message channel listeners * from previous handshakes if necessary, sets messageTarget if necessary, and * posts a handshake acknowledgement message back with one of the message * channel ports. * * @param event The handshake MessageEvent. */ private _onHandshake; /** * Triggered whenever a handshake acknowledgement message is received. That * would be a response when a handshake message was sent or whether the * receiving end has acknowledged an acknowledgement. * * If this message event source posted a handshake, this will receive the * transferrred message port, listen to it, fire an acknowledgement of the * acknowledgement, and resolve the handshake. * * If this message even source did not post a handshake but rather posted an * acknowledgement to a previous handshake, then this will resolve the * handhsake. * * @param event The handshake acknowledgement MessageEvent. */ private _onHandshakeAck; private _destroyMessagePort; /** * Posts a message to the paired postdoc. It is expected to await the * handshake promise before calling this method. If the handshake is not * resolved, then this will throw an error. * * @param message The message to be sent to the paired postdoc. */ postMessage(message: T, options?: StructuredSerializeOptions): void; } export {};