Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/drenther/izod
type safe, validated, namespaced iframe communication made simple!
https://github.com/drenther/izod
iframe iframe-communication react typescript zod
Last synced: 22 days ago
JSON representation
type safe, validated, namespaced iframe communication made simple!
- Host: GitHub
- URL: https://github.com/drenther/izod
- Owner: drenther
- License: mit
- Created: 2023-08-14T15:14:34.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2023-11-01T06:58:18.000Z (about 1 year ago)
- Last Synced: 2024-10-04T08:06:51.198Z (about 2 months ago)
- Topics: iframe, iframe-communication, react, typescript, zod
- Language: TypeScript
- Homepage:
- Size: 76.2 KB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# izod
> NOTE: This is very early stage, documentation is not complete and breaking API changes likely ahead. Please use at your own risk. Lock your version in case you use. (Even though I will adhere to semver for updates)
![Bundle Size](https://img.shields.io/bundlephobia/minzip/@izod/core) ![npm version](https://badgen.net/npm/v/@izod/core) ![types](https://badgen.net/npm/types/@izod/core)
`izod` leverages [zod](https://github.com/colinhacks/zod) to provide a type safe Promise oriented API to manage iframe communication.
## Installation
### core (only)
```sh
npm i zod @izod/core
```### react
```sh
npm i zod @izod/core @izod/react
```## Usage
### @izod/core
```ts
// common.tsimport { z } from 'zod';
import type { EventMap } from '@izod/core';export const parentOriginEvents = {
askQuestion: z.object({
question: z.string(),
}),
shout: z.object({
message: z.string(),
}),
} as const satisfies EventMap;export const childOriginEvents = {
answerQuestion: z.object({
answer: z.string(),
}),
whisper: z.object({
message: z.string(),
}),
} as const satisfies EventMap;
``````ts
// parent.htmlimport { createChild } from '@izod/core';
// create the child instance (not mounted until handshake is executed)
const child = createChild({
container: document.body, // required
url: 'http://127.0.0.1:3010', // required
inboundEvents: childOriginEvents, // optional
outboundEvents: parentOriginEvents, // optional
handshakeOptions: {
// optional
maxHandshakeRequests: 10, // default 5
handshakeRetryInterval: 100, // default 1000
},
});// perfect time to setup event listeners so that they are ready once the handshake is over
// type safe event listeners for events coming from the child
child.on('whisper', (data) => {
console.log(`Child whispered: ${data.message}`);
});const childApi = await child.executeHandshake();
// type safe event emitters
childApi.emit('shout', { message: 'Hello' });
``````ts
// child.htmlimport { connectToParent } from '@izod/core';
// sets the boilerplate
const parent = connectToParent({
inboundEvents: parentOriginEvents, // optional
outboundEvents: childOriginEvents, // optional
});// type safe event listeners for events coming from the parent
parent.on('shout', (data) => {
console.log(`Parent shouted: ${data.message}`);
});const parentApi = await parent.executeHandshake();
// type safe event emitters
parentApi.emit('whisper', { message: 'Hi' });
```### @izod/react
```tsx
// parent.tsximport { child } from '@izod/react';
function Parent() {
// accepts all the parameters that `createChild` from @izod/core does
// `api` is the same that is returned from `connectToParent.executeHandshake` from @izod/core
// `on` can be used to attach event listeners
const { on, api, executeHandshake } = child.useCreate({
container: document.body, // required
url: 'http://127.0.0.1:3010', // required
inboundEvents: parentOriginEvents, // optional
outboundEvents: childOriginEvents, // optional
handshakeOptions: {
// optional
maxHandshakeRequests: 10, // default 5
handshakeRetryInterval: 100, // default 1000
},
onHandshakeComplete(api) {
// callback called when handshake is successful
},
onHandshakeError(error) {
// callback called when handshake fails
},
// remove the iframe on component unmount
destroyOnUnmount: false, // default false - optional
});// `child.useEventListener` takes care of this boilerplate for you but is not fully type safe as of now
// to add event listeners
// prefer this over `onHandshakeComplete` for attaching event listeners
useEffect(() => {
if (api) {
// function is returned from `.on` that can be called to unsubscribe
const off = on('askQuestion', (data) => {
console.log('Question: ', data.question);
});// return that from the useEffect for cleanup
return off;
}
}, [api]);const ranOnce = useRef(false);
useEffect(() => {
if (ranOnce.current) {
return;
}executeHandshake();
ranOnce.current = true;
}, []);const shout = () => {
api.emit('shout', { message: 'Hello' });
};
}
``````tsx
// child.tsximport { parent } from '@izod/react';
function Child() {
// `api` is the same that is returned from `connectToParent.executeHandshake` from @izod/core
const { on, api, executeHandshake } = parent.useConnect({
inboundEvents: parentOriginEvents,
outboundEvents: childOriginEvents,
onHandshakeComplete(api) {
// callback called when handshake is successful
},
onHandshakeError(error) {
// callback called when handshake fails
},
});// `parent.useEventListener` takes care of this boilerplate for you but is not fully type safe as of now
// to add event listeners
useEffect(() => {
// function is returned from `.on` that can be called to unsubscribe
if (api) {
const off = api.on('shout', (data) => {
console.log(`Parent shouted: ${data.message}`);
});// return that from the useEffect for cleanup
return off;
}
}, [api]);const ranOnce = useRef(false);
useEffect(() => {
if (ranOnce.current) {
return;
}executeHandshake();
ranOnce.current = true;
}, []);const whisper = () => {
api.emit('whisper', { message: 'Hi' });
};
}
```## Prior Art (packages I ~~copied~~ adapted code from)
- [Postmate](https://github.com/dollarshaveclub/postmate)