{"id":15011444,"url":"https://github.com/khou22/async-post-message","last_synced_at":"2025-04-12T04:06:55.293Z","repository":{"id":196169680,"uuid":"694839246","full_name":"khou22/async-post-message","owner":"khou22","description":"A system for making postMessage asynchronous","archived":false,"fork":false,"pushed_at":"2023-10-02T18:00:40.000Z","size":12550,"stargazers_count":8,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-12T04:06:39.259Z","etag":null,"topics":["bunjs","codeium","iframe-api","postmessage","postmessage-async-await","postmessage-promise","promises","react","shadcn","tailwind","typescript"],"latest_commit_sha":null,"homepage":"https://async-post-message.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/khou22.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-09-21T19:56:39.000Z","updated_at":"2024-10-25T12:49:05.000Z","dependencies_parsed_at":"2023-09-25T23:10:49.133Z","dependency_job_id":null,"html_url":"https://github.com/khou22/async-post-message","commit_stats":null,"previous_names":["khou22/async-post-message"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khou22%2Fasync-post-message","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khou22%2Fasync-post-message/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khou22%2Fasync-post-message/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khou22%2Fasync-post-message/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/khou22","download_url":"https://codeload.github.com/khou22/async-post-message/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248514228,"owners_count":21116903,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bunjs","codeium","iframe-api","postmessage","postmessage-async-await","postmessage-promise","promises","react","shadcn","tailwind","typescript"],"created_at":"2024-09-24T19:41:06.548Z","updated_at":"2025-04-12T04:06:55.264Z","avatar_url":"https://github.com/khou22.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Async Post Message\n\n\u003c/div\u003e\n\n[![built with Codeium](https://codeium.com/badges/main)](https://codeium.com?repo_name=khou22%2Fasync-post-message) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/esta/issues) ![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/kevinhou22)\n\n[![NPM](https://nodei.co/npm/async-post-message.png)](https://nodei.co/npm/async-post-message/)\n\nThe `async` / `await` for window-based communication. Fully typed and works with Typescript, React, and NextJS.\n\n``` typescript\nconst resp = await window.postMessage(req)\n```\n\n## Demo\n\n[![Demo](/docs/demo.gif)](/docs/demo.gif)\n\n## Motivation\n\nI have been using the Javascript `postMessage` [[docs](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)] to communicate between frames, but have been frustrated that communication is not strongly guaranteed. You can send a message reliably, but there is no notion of an async call --\u003e response. Rather, you can send a message to the other context and hope you get a response. You need to instrument listening to the correct response and yet this is still quite complex if you want to run an `async` request.\n\nIn this demo, I create a promise wrapper around the `postMessage` Javascript API and handle sending messages between contexts so that you can simply run `await asyncPromise.send('functionName', [...args])` in your client code.\n\n## Installation\n\nInstallation with popular package managemers:\n\n```shell\nnpm install async-post-message\n```\n\n```shell\nyarn add async-post-message\n```\n\n```shell\nbun install async-post-message\n```\n\n## Usage\n\n### Getting Started\n\nDefine the promises types that you would like to execute across the frame contexts:\n\n```typescript\nexport type MyPromises = {\n    getText: () =\u003e string;\n    multiplyByFour: (n: number) =\u003e number;\n};\n```\n\nThe parent process needs to be set up to handle the promise requests:\n\n```typescript\nvar iframe: HTMLIFrameElement = ...;\n\nconst unsubscribe = handleWebViewRequest\u003cMyPromises\u003e(\n    iframe.contentWindow,\n    async (request) =\u003e {\n        const { uid, functionName, args } = event.data;\n        switch (functionName) {\n            case \"multiplyByFour\": {\n                iframeRef.current.contentWindow.postMessage({\n                    uid,\n                    functionName: \"multiplyByFour\",\n                    response: 4 * args[0],\n                });\n                break;\n            }\n        }\n    }\n);\n```\n\nOn the iFrame page (or other web view that can `postMessage`), create a new `AsyncPostMessage` singleton instance with the promise interface as the generic argument. You can then call `execute` with the function name and signature.\n\n```typescript\nconst asyncPostMessage = WebViewRequester.getInstance\u003cMyPromises\u003e();\n\n// Execute the asynchronous request to the parent.\nconst response = await asyncPostMessage.execute(\"multiplyByFour\", 4);\nconsole.log(response); // 16\n```\n\n### Usage in React\n\nYou may want to use this system in React. The big change here is that you'll want to wrap things in `ref`'s and `useEffect`s.\n\nDefine the promises types that you would like to execute across the frame contexts:\n\n```typescript\nexport type MyPromises = {\n    getText: () =\u003e string;\n    multiplyByFour: (n: number) =\u003e number;\n    induceError: () =\u003e boolean;\n};\n```\n\n#### Parent Window\n\nThe parent process needs to be set up to handle the promise requests:\n\n```tsx\nconst iframeRef = useRef\u003cHTMLIFrameElement\u003e(null);\n\nuseEffect(() =\u003e {\n    if (!iframeRef.current?.contentWindow) return;\n\n    const unsubscribe = handleWebViewRequest\u003cMyPromises\u003e(\n        iframeRef.current.contentWindow,\n        async (request) =\u003e {\n        const { uid, functionName, args } = request;\n        switch (functionName) {\n            case \"multiplyByFour\": {\n                const argsTyped = args as Parameters\u003cMyPromises[\"multiplyByFour\"]\u003e;\n                const response = 4 * argsTyped[0];\n                return {\n                    uid,\n                    functionName: \"multiplyByFour\",\n                    response,\n                };\n            }\n        }\n    );\n\n    return () =\u003e {\n        unsubscribe();\n    };\n}, []);\n```\n\n#### iFrame Web View\n\nOn the iFrame page, create a new `AsyncPostMessage` instance with the promise interface as the generic argument.\n\n```tsx\nconst asyncPostMessage = useRef(new AsyncPostMessage\u003cMyPromises\u003e());\n\n// Check to ensure it can run in an iFrame.\nuseEffect(() =\u003e {\n    if (!window) {\n        setError(new Error(\"Not an iFrame\"));\n    }\n    asyncPostMessage.current = WebViewRequester.getInstance\u003cMyPromises\u003e();\n}, []);\n```\n\nNow to call a promise you can simply call the `send()`:\n\n```ts\nconst response = await asyncPostMessage.current.send(\"multiplyByFour\", 4);\nconsole.log(response); // 16\n```\n\n## Development\n\n### Building\n\n1. `yarn install`\n2. `yarn build` which will generate the `dist/` folder\n3. To deploy to NPM, run `npm publish`\n\n### Running Demo Locally\n\nFirst, navigate to `/apps/www`.\n\n1. Install dependencies: `bun install`\n2. Run dev server: `bun dev`\n3. Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhou22%2Fasync-post-message","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkhou22%2Fasync-post-message","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhou22%2Fasync-post-message/lists"}