UNPKG

async-post-message

Version:

Asynchronous postMessage protocol for typed, promise-based windows and iFrame communication

1 lines 9.13 kB
{"version":3,"sources":["../src/ParentHandler.ts","../src/utils/index.ts","../src/RequestManager/RequestManager.ts","../src/WebViewRequester.ts"],"sourcesContent":["import {\n AsyncPostMessageRequest,\n AsyncPostMessageResponse,\n Promises,\n} from \"./RequestManager/types\";\n\ntype Unsubscribe = () => void;\n\nexport const handleWebViewRequest = <PromisesInterface extends Promises>(\n target: { postMessage: typeof window.postMessage },\n handler: (\n request: AsyncPostMessageRequest<PromisesInterface>\n ) => Promise<AsyncPostMessageResponse<PromisesInterface>>\n): Unsubscribe => {\n const handleMessage = async (\n event: MessageEvent<AsyncPostMessageRequest<PromisesInterface>>\n ) => {\n // Execute the business logic.\n const response = await handler(event.data);\n\n // Send the response back to the iFrame so it can resolve.\n target.postMessage(response, \"*\");\n };\n\n window.addEventListener(\"message\", handleMessage);\n\n return () => {\n window.removeEventListener(\"message\", handleMessage);\n };\n};\n","/**\n * Generates an alphanumeric unique identifier.\n *\n * @returns {string} The alphanumeric unique identifier.\n */\nexport const generateAlphaNumericUniqueId = (): string => {\n // Generate a random number between 0 and 35.\n const randomNumber = Math.floor(Math.random() * 36);\n\n // Get the current timestamp.\n const timestamp = Date.now();\n\n // Combine the random number and timestamp to create a unique identifier.\n const uniqueId = `${randomNumber}-${timestamp}`;\n\n // Convert the unique identifier to a base-36 string.\n const alphaNumericUniqueId = uniqueId.replace(/[\\W_]/g, \"\");\n\n // Return the alpha numeric unique identifier.\n return alphaNumericUniqueId;\n};\n","import { generateAlphaNumericUniqueId } from \"../utils\";\nimport {\n AsyncPostMessageRequest,\n AsyncPostMessageResponse,\n CallbackType,\n Promises,\n UidType,\n} from \"./types\";\n\n/**\n * A class for managing async postMessage requests and their promise wrappers. Each instance\n * stores a map of callbacks for each unique ID. It does not wrap the `postMessage` API. Instead,\n * it allows the client to set the listener and postMessage methods on their own.\n */\nexport class RequestManager<PromisesInterface extends Promises> {\n /**\n * Map of callbacks for each promise's unique ID.\n */\n private callbacks: Map<UidType, CallbackType>;\n\n constructor() {\n this.callbacks = new Map();\n }\n\n /**\n * Send a strongly typed async postMessage request.\n *\n * @param functionName The name of the function to call\n * @param args The arguments to pass to the function\n * @param options Options for the request. Defaults to a timeout of 10 seconds. If `0` is passed\n * as the timeout, the request will not timeout.\n * @returns A promise that resolves with the return value of the function\n */\n public send = <FnNameType extends keyof PromisesInterface & string>(\n functionName: FnNameType,\n args: Parameters<PromisesInterface[FnNameType]>,\n options?: { timeoutMs: number }\n ): Promise<ReturnType<PromisesInterface[FnNameType]>> => {\n // Generate a unique ID for this request.\n const uid = generateAlphaNumericUniqueId();\n\n const promise = new Promise<ReturnType<PromisesInterface[FnNameType]>>(\n (resolve, reject) => {\n this.callbacks.set(uid, { resolve, reject });\n const message: AsyncPostMessageRequest<PromisesInterface> = {\n uid,\n functionName,\n args,\n };\n this.postMessage(message);\n\n // Set a timeout for the request (defaults to 10 seconds).\n const timeout = options?.timeoutMs ?? 10000;\n if (timeout > 0) {\n setTimeout(() => {\n reject(new Error(`${functionName} timed out (id: ${uid})`));\n this.callbacks.delete(uid);\n }, timeout);\n }\n }\n );\n\n return promise;\n };\n\n /**\n * Handler for postMessages to the client.\n *\n * @param message Message payload received by the webview.\n */\n public onResponse(\n message: AsyncPostMessageResponse<PromisesInterface>\n ): void {\n const callback = this.callbacks.get(message.uid);\n\n // If the callback is not defined, do nothing as it means the request timed out.\n if (!callback) {\n return;\n }\n\n this.callbacks.delete(message.uid);\n if (message.error) {\n callback.reject(new Error(message.error));\n } else {\n callback.resolve(message.response);\n }\n }\n\n /**\n * Send a strongly typed async postMessage request to the target. This should be set by the\n * client.\n *\n * @param _message Message payload to send to the target.\n */\n public postMessage(\n _message: AsyncPostMessageRequest<PromisesInterface>\n ): void {\n console.warn(\"[AsyncPostMessage] postMessage unimplemented\");\n }\n}\n","import { RequestManager } from \"./RequestManager/RequestManager\";\nimport { AsyncPostMessageResponse, Promises } from \"./RequestManager/types\";\n\n/**\n * The Singleton class defines the `getInstance` method that lets clients access\n * the unique singleton instance.\n */\nexport class WebViewRequester<PromisesInterface extends Promises = any> {\n private static instance: WebViewRequester;\n\n private requestManager: RequestManager<PromisesInterface>;\n\n /**\n * The Singleton's constructor should always be private to prevent direct\n * construction calls with the `new` operator.\n */\n private constructor() {\n // Setup instance of RequestManager that will be used by all calls.\n this.requestManager = new RequestManager();\n\n // Listen for messages from the parent.\n window.addEventListener(\n \"message\",\n (event: MessageEvent<AsyncPostMessageResponse<PromisesInterface>>) => {\n if (!event.data) {\n return;\n }\n this.requestManager.onResponse(event.data);\n }\n );\n\n // Setup data pipeline to send messages to the parent.\n this.requestManager.postMessage = (message) => {\n window.parent.postMessage(message, \"*\");\n };\n }\n\n /**\n * The static method that controls the access to the global AsyncPostMessage instance.\n *\n * We use a singleton so that the client does not need to worry about creating multiple\n * instances of the `AsyncPostMessage` class.\n */\n public static getInstance = <\n PromisesInterface extends Promises\n >(): WebViewRequester<PromisesInterface> => {\n if (!WebViewRequester.instance) {\n WebViewRequester.instance = new WebViewRequester();\n }\n\n return WebViewRequester.instance;\n };\n\n /**\n * Execute a strongly typed promise that is executed in the parent process.\n */\n public execute = <FnNameType extends keyof PromisesInterface & string>(\n functionName: FnNameType,\n args: Parameters<PromisesInterface[FnNameType]>,\n options?: { timeoutMs: number }\n ): Promise<ReturnType<PromisesInterface[FnNameType]>> => {\n return this.requestManager.send(functionName, args, options);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAQO,IAAM,uBAAuB,CAClC,QACA,YAGgB;AAChB,QAAM,gBAAgB,CACpB,UACG;AAEH,UAAM,WAAW,MAAM,QAAQ,MAAM,IAAI;AAGzC,WAAO,YAAY,UAAU,GAAG;AAAA,EAClC;AAEA,SAAO,iBAAiB,WAAW,aAAa;AAEhD,SAAO,MAAM;AACX,WAAO,oBAAoB,WAAW,aAAa;AAAA,EACrD;AACF;;;ACxBO,IAAM,+BAA+B,MAAc;AAExD,QAAM,eAAe,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE;AAGlD,QAAM,YAAY,KAAK,IAAI;AAG3B,QAAM,WAAW,GAAG,YAAY,IAAI,SAAS;AAG7C,QAAM,uBAAuB,SAAS,QAAQ,UAAU,EAAE;AAG1D,SAAO;AACT;;;ACNO,IAAM,iBAAN,MAAyD;AAAA,EAM9D,cAAc;AAad;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAO,OAAO,CACZ,cACA,MACA,YACuD;AAEvD,YAAM,MAAM,6BAA6B;AAEzC,YAAM,UAAU,IAAI;AAAA,QAClB,CAAC,SAAS,WAAW;AA1C3B;AA2CQ,eAAK,UAAU,IAAI,KAAK,EAAE,SAAS,OAAO,CAAC;AAC3C,gBAAM,UAAsD;AAAA,YAC1D;AAAA,YACA;AAAA,YACA;AAAA,UACF;AACA,eAAK,YAAY,OAAO;AAGxB,gBAAM,WAAU,wCAAS,cAAT,YAAsB;AACtC,cAAI,UAAU,GAAG;AACf,uBAAW,MAAM;AACf,qBAAO,IAAI,MAAM,GAAG,YAAY,mBAAmB,GAAG,GAAG,CAAC;AAC1D,mBAAK,UAAU,OAAO,GAAG;AAAA,YAC3B,GAAG,OAAO;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AA1CE,SAAK,YAAY,oBAAI,IAAI;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgDO,WACL,SACM;AACN,UAAM,WAAW,KAAK,UAAU,IAAI,QAAQ,GAAG;AAG/C,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AAEA,SAAK,UAAU,OAAO,QAAQ,GAAG;AACjC,QAAI,QAAQ,OAAO;AACjB,eAAS,OAAO,IAAI,MAAM,QAAQ,KAAK,CAAC;AAAA,IAC1C,OAAO;AACL,eAAS,QAAQ,QAAQ,QAAQ;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,YACL,UACM;AACN,YAAQ,KAAK,8CAA8C;AAAA,EAC7D;AACF;;;AC5FO,IAAM,oBAAN,MAAM,kBAA2D;AAAA;AAAA;AAAA;AAAA;AAAA,EAS9D,cAAc;AAwCtB;AAAA;AAAA;AAAA,SAAO,UAAU,CACf,cACA,MACA,YACuD;AACvD,aAAO,KAAK,eAAe,KAAK,cAAc,MAAM,OAAO;AAAA,IAC7D;AA5CE,SAAK,iBAAiB,IAAI,eAAe;AAGzC,WAAO;AAAA,MACL;AAAA,MACA,CAAC,UAAqE;AACpE,YAAI,CAAC,MAAM,MAAM;AACf;AAAA,QACF;AACA,aAAK,eAAe,WAAW,MAAM,IAAI;AAAA,MAC3C;AAAA,IACF;AAGA,SAAK,eAAe,cAAc,CAAC,YAAY;AAC7C,aAAO,OAAO,YAAY,SAAS,GAAG;AAAA,IACxC;AAAA,EACF;AA4BF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAxDa,kBAoCG,cAAc,MAEgB;AAC1C,MAAI,CAAC,kBAAiB,UAAU;AAC9B,sBAAiB,WAAW,IAAI,kBAAiB;AAAA,EACnD;AAEA,SAAO,kBAAiB;AAC1B;AA5CK,IAAM,mBAAN;","names":[]}