Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/khou22/async-post-message
A system for making postMessage asynchronous
https://github.com/khou22/async-post-message
bunjs codeium iframe-api postmessage postmessage-async-await postmessage-promise promises react shadcn tailwind typescript
Last synced: 3 months ago
JSON representation
A system for making postMessage asynchronous
- Host: GitHub
- URL: https://github.com/khou22/async-post-message
- Owner: khou22
- License: mit
- Created: 2023-09-21T19:56:39.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2023-10-02T18:00:40.000Z (over 1 year ago)
- Last Synced: 2024-10-31T09:41:54.721Z (3 months ago)
- Topics: bunjs, codeium, iframe-api, postmessage, postmessage-async-await, postmessage-promise, promises, react, shadcn, tailwind, typescript
- Language: TypeScript
- Homepage: https://async-post-message.vercel.app
- Size: 12 MB
- Stars: 8
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Async Post Message
[![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)
[![NPM](https://nodei.co/npm/async-post-message.png)](https://nodei.co/npm/async-post-message/)
The `async` / `await` for window-based communication. Fully typed and works with Typescript, React, and NextJS.
``` typescript
const resp = await window.postMessage(req)
```## Demo
[![Demo](/docs/demo.gif)](/docs/demo.gif)
## Motivation
I 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 --> 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.
In 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.
## Installation
Installation with popular package managemers:
```shell
npm install async-post-message
``````shell
yarn add async-post-message
``````shell
bun install async-post-message
```## Usage
### Getting Started
Define the promises types that you would like to execute across the frame contexts:
```typescript
export type MyPromises = {
getText: () => string;
multiplyByFour: (n: number) => number;
};
```The parent process needs to be set up to handle the promise requests:
```typescript
var iframe: HTMLIFrameElement = ...;const unsubscribe = handleWebViewRequest(
iframe.contentWindow,
async (request) => {
const { uid, functionName, args } = event.data;
switch (functionName) {
case "multiplyByFour": {
iframeRef.current.contentWindow.postMessage({
uid,
functionName: "multiplyByFour",
response: 4 * args[0],
});
break;
}
}
}
);
```On 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.
```typescript
const asyncPostMessage = WebViewRequester.getInstance();// Execute the asynchronous request to the parent.
const response = await asyncPostMessage.execute("multiplyByFour", 4);
console.log(response); // 16
```### Usage in React
You 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.
Define the promises types that you would like to execute across the frame contexts:
```typescript
export type MyPromises = {
getText: () => string;
multiplyByFour: (n: number) => number;
induceError: () => boolean;
};
```#### Parent Window
The parent process needs to be set up to handle the promise requests:
```tsx
const iframeRef = useRef(null);useEffect(() => {
if (!iframeRef.current?.contentWindow) return;const unsubscribe = handleWebViewRequest(
iframeRef.current.contentWindow,
async (request) => {
const { uid, functionName, args } = request;
switch (functionName) {
case "multiplyByFour": {
const argsTyped = args as Parameters;
const response = 4 * argsTyped[0];
return {
uid,
functionName: "multiplyByFour",
response,
};
}
}
);return () => {
unsubscribe();
};
}, []);
```#### iFrame Web View
On the iFrame page, create a new `AsyncPostMessage` instance with the promise interface as the generic argument.
```tsx
const asyncPostMessage = useRef(new AsyncPostMessage());// Check to ensure it can run in an iFrame.
useEffect(() => {
if (!window) {
setError(new Error("Not an iFrame"));
}
asyncPostMessage.current = WebViewRequester.getInstance();
}, []);
```Now to call a promise you can simply call the `send()`:
```ts
const response = await asyncPostMessage.current.send("multiplyByFour", 4);
console.log(response); // 16
```## Development
### Building
1. `yarn install`
2. `yarn build` which will generate the `dist/` folder
3. To deploy to NPM, run `npm publish`### Running Demo Locally
First, navigate to `/apps/www`.
1. Install dependencies: `bun install`
2. Run dev server: `bun dev`
3. Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.