Skip to content

@stephansama/typed-events

Typed events store using standard schema

Open Table of contents
Terminal window
pnpm install @stephansama/typed-events

create a typed CustomEvent using a standard-schema compatible validator

open example
import { createEvent } from "@stephansama/typed-events";
export const customAnimationEvent = createEvent(
"custom-animation-event",
z.object({
x: z.number(),
y: z.number(),
}),
);

somewhere in your codebase

export function listenForAnimationEvent() {
const item = document.querySelector<HTMLElement>("#item");
if (!item) throw new Error("unable to find item");
const cleanup = customAnimationEvent.listen((event) => {
item.style.x = String(event.data.x);
item.style.y = String(event.data.y);
});
return () => cleanup();
}

somewhere else in your codebase

export function dispatchEvent() {
const button = document.querySelector("#button");
const x = document.querySelector("#x");
const y = document.querySelector("#y");
if (!button) throw new Error("unable to find button");
if (!x) throw new Error("unable to find x");
if (!y) throw new Error("unable to find y");
button.addEventListener("click", () => {
customAnimationEvent.dispatch({
x: +x.textContent,
y: +y.textContent,
});
});
}
open example
import { createEventMap } from "@stephansama/typed-events";
export const eventMap = createEventMap("event-map", {
reset: z.object({}),
update: z.object({ value: z.number() }),
});

somewhere in your codebase

export function listenForEventMap() {
const value = document.querySelector("#value");
if (!value) throw new Error("unable to find value");
const cleanup = eventMap.listen("update", (message) => {
value.textContent = String(message.data.value);
});
return () => cleanup();
}

somewhere else in your codebase

export function dispatchEventMap() {
const button = document.querySelector("#button");
if (!button) throw new Error("unable to find button");
button.addEventListener("click", () => {
eventMap.dispatch("update", {
value: Math.floor(Math.random() * 100),
});
});
}

create a typed BroadcastChannel using a standard-schema compatible validator

open example
import { createBroadcastChannel } from "@stephansama/typed-events";
export const channel = createBroadcastChannel("broadcaster", {
reset: z.object({}),
update: z.object({ value: z.number() }),
});

somewhere in your codebase

export function listenForChannelMessage() {
const value = document.querySelector("#value");
if (!value) throw new Error("unable to find value");
const cleanup = channel.listen("update", (message) => {
value.textContent = String(message.data.value);
});
return () => cleanup();
}

somewhere else in your codebase

export function dispatchChannelMessage() {
const button = document.querySelector("#button");
if (!button) throw new Error("unable to find button");
button.addEventListener("click", () => {
channel.dispatch("update", {
value: Math.floor(Math.random() * 100),
});
});
}

create a typed BroadcastChannel and CustomEvent using a standard-schema compatible validator

open example
import { createBroadcastEvent } from "@stephansama/typed-events";
export const broadcastEvent = createBroadcastEvent("broadcaster", {
reset: z.object({}),
update: z.object({ value: z.number() }),
});

somewhere in your codebase

export function listenForBroadcastEvent() {
const value = document.querySelector("#value");
if (!value) throw new Error("unable to find value");
const cleanup = broadcastEvent.listen("update", (message) => {
value.textContent = String(message.data.value);
});
return () => cleanup();
}

somewhere else in your codebase

export function dispatchBroadcastEvent() {
const button = document.querySelector("#button");
if (!button) throw new Error("unable to find button");
button.addEventListener("click", () => {
broadcastEvent.dispatch("update", {
value: Math.floor(Math.random() * 100),
});
});
}

create a typed MessageEvent using a standard-schema compatible validator

open example
import { createMessage } from "@stephansama/typed-events";
export const message = createMessage("event-map", {
reset: z.object({}),
update: z.object({ value: z.number() }),
});

somewhere in your codebase

export function listenForMessage() {
const value = document.querySelector("#value");
if (!value) throw new Error("unable to find value");
const cleanup = message.listen("update", (message) => {
value.textContent = String(message.data.value);
});
return () => cleanup();
}

somewhere else in your codebase

export function dispatchMessage() {
const button = document.querySelector("#button");
if (!button) throw new Error("unable to find button");
button.addEventListener("click", () => {
message.dispatch("update", {
value: Math.floor(Math.random() * 100),
});
});
}

you can use useListener or useListeners to automatically register and cleanup typed event listeners

open example
import { useListeners } from "../dist/react.mjs";
const map = createBroadcastEvent("react-example", {
first: z.object({}),
second: z.object({ payload: z.number() }),
});
export function ExampleComponent() {
useListeners(map, {
first(_payload) {
},
second(_payload) {},
});
return; // more jsx...
}
Module Description

broadcast

broadcast-event

errors

event

event-map

index

message

react

utils