
Security News
Feross on the 10 Minutes or Less Podcast: Nobody Reads the Code
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.
repond-events
Advanced tools
Serializable event orchestration with fine-grained control
Unlike setTimeout or typical event systems, repond-events lets you:
Built on Repond for reactive state management.
The Problem:
Traditional event systems use setTimeout and callbacks, which are:
The Solution:
Repond Events provides serializable event orchestration:
Date.now())npm install repond-events repond
import { makeEventTypes, initEventTypeGroups, runEvents, E } from "repond-events";
// 1. Define event types
const basicEvents = makeEventTypes(({ event }) => ({
log: event({
run: ({ message }) => console.log(message),
params: { message: "" as string },
}),
wait: event({
run: async ({}) => { /* timing handled by system */ },
params: { duration: 1000 as number },
duration: 1000,
}),
}));
// 2. Register events
initEventTypeGroups({ basic: basicEvents });
// 3. Run a sequence
runEvents([
E("basic", "log", { message: "Starting..." }),
E("basic", "wait", { duration: 1000 }),
E("basic", "log", { message: "After 1 second" }),
E("basic", "wait", { duration: 2000 }),
E("basic", "log", { message: "Done!" }),
]);
Output:
Starting...
[1 second pause]
After 1 second
[2 seconds pause]
Done!
Events run in order by default:
runEvents([
E("game", "fadeIn", { duration: 500 }),
E("game", "showDialog", { text: "Welcome!" }),
E("game", "fadeOut", { duration: 500 }),
]);
Each event completes before the next starts.
Values are evaluated when events run, not when defined:
import { V } from "repond-events";
// Without values - fixed at definition time
runEvent("ui", "showScore", { score: 100 });
// With values - evaluated at runtime
runEvent("ui", "showScore", {
score: V("basic", "getVariable", { name: "currentScore" })
});
Why this matters: The value is fetched when the event runs, so it's always current.
Create your own value types for reusable logic:
import { makeValueTypes, initValueTypeGroups } from "repond-events";
const customValues = makeValueTypes(({ value }) => ({
ifThen: value({
run: ({ condition, thenValue, elseValue }) => {
return condition ? thenValue : elseValue;
},
params: {
condition: false as boolean,
thenValue: undefined as any,
elseValue: undefined as any,
},
}),
}));
initValueTypeGroups({ custom: customValues });
// Use it for conditional logic
runEvent("game", "applyEffect", {
effectType: V("custom", "ifThen", {
condition: V("game", "isPlayerHealthy"),
thenValue: "healing",
elseValue: "damage"
})
});
You can even choose between event chains:
const conditionalValues = makeValueTypes(({ value }) => ({
chooseChain: value({
run: ({ condition, thenChain, elseChain }) => {
return condition ? thenChain : elseChain;
},
params: {
condition: false as boolean,
thenChain: [] as EventBlock[],
elseChain: [] as EventBlock[],
},
}),
}));
runEvent("game", "processAction", {
result: V("basic", "getEventValue", {
events: V("conditional", "chooseChain", {
condition: V("game", "isHealthy"),
thenChain: [
E("game", "heal", { amount: 50 }),
E("basic", "returnValue", { value: "success" })
],
elseChain: [
E("game", "damage", { amount: 10 }),
E("basic", "returnValue", { value: "failure" })
]
})
})
});
Event chains are plain objects - fully JSON-compatible:
{
"events": [
{
"group": "game",
"name": "showDialog",
"params": { "text": "Hello!" },
"options": {}
},
{
"group": "basic",
"name": "wait",
"params": { "duration": 2000 },
"options": {}
}
]
}
This enables:
import { chainDo } from "repond-events";
runEvents([
E("game", "cutscene1", {}),
E("basic", "wait", { duration: 5000 }),
E("game", "cutscene2", {}),
], { chainId: "mainStory" });
// Pause the entire chain
chainDo("pause", "mainStory");
// Resume later
chainDo("unpause", "mainStory");
Perfect for: Pause menus, game saves, interactive tutorials
Events use Repond state for timing, not Date.now():
import { setState } from "repond";
// Initialize with custom time path
initEventTypeGroups(allEvents, {
defaultTimePath: ["global", "time", "elapsed"]
});
// Control time speed
setState("global.time.speed", 0.5); // Slow motion (0.5x)
setState("global.time.speed", 2.0); // Fast forward (2x)
setState("global.time.speed", 0); // Pause
Perfect for: Slow-motion mechanics, pause menus, multiple time streams (game time vs. UI time)
// LLM generates quest JSON
const llmQuest = await fetch("/api/llm/generate-quest").then(r => r.json());
// Run it directly
runEvents(llmQuest.events, { chainId: `quest_${llmQuest.id}` });
Perfect for: Dynamic quests, procedural content, personalized experiences
import { getState, setState } from "repond";
// Save
const chainState = getState("chains", "myChain");
const liveEvents = chainState.liveEventIds.map(id => getState("liveEvents", id));
localStorage.setItem("gameSave", JSON.stringify({ chainState, liveEvents }));
// Load
const saved = JSON.parse(localStorage.getItem("gameSave"));
// Restore state and continue exactly where you left off
Perfect for: Game save systems, offline-first apps, cross-device sync
| Feature | setTimeout | XState | GSAP | Repond Events |
|---|---|---|---|---|
| Serializable | ❌ | ⚠️ Partial | ❌ | ✅ |
| Pause/Resume | ❌ | ⚠️ Limited | ✅ | ✅ |
| Custom Time | ❌ | ❌ | ⚠️ Limited | ✅ |
| Conditional Logic | ✅ Code | ✅ Built-in | ❌ | ✅ Values |
| Event Chaining | ⚠️ Manual | ⚠️ Manual | ✅ | ✅ |
| LLM-Ready | ❌ | ❌ | ❌ | ✅ |
| Save/Load | ❌ | ⚠️ Partial | ❌ | ✅ |
import { makeEventTypes, initEventTypeGroups } from "repond-events";
const myEvents = makeEventTypes(({ event }) => ({
myEvent: event({
run: (params, liveInfo) => {
// Event logic here
console.log(params.message);
},
params: { message: "" as string },
duration: 1000, // Optional default duration
isParallel: false, // Optional: can run alongside other events
}),
}));
initEventTypeGroups({
myGroup: myEvents,
});
import { runEvents, runEvent, E } from "repond-events";
// Single event
runEvent("myGroup", "myEvent", { message: "Hello" });
// Event chain
runEvents([
E("myGroup", "event1", { /* params */ }),
E("myGroup", "event2", { /* params */ }),
], { chainId: "myChain" });
import { eventDo, chainDo } from "repond-events";
// Control individual event
eventDo("pause", liveEventId);
eventDo("skip", liveEventId);
eventDo("cancel", liveEventId);
// Control entire chain
chainDo("pause", chainId);
chainDo("unpause", chainId);
chainDo("cancel", chainId);
import { makeValueTypes, initValueTypeGroups, V } from "repond-events";
const myValues = makeValueTypes(({ value }) => ({
myValue: value({
run: (params, valueRunInfo) => {
// Return computed value
return params.a + params.b;
},
params: { a: 0 as number, b: 0 as number },
}),
}));
initValueTypeGroups({ myGroup: myValues });
// Use it
runEvent("game", "applyDamage", {
amount: V("myGroup", "myValue", { a: 10, b: 5 }) // Returns 15
});
isParallel: true)isFast: true)runPriorityEvent)getEventValueSee Getting Started Examples for details.
Repond Events is part of a larger goal: making game/app logic fully serializable and tweakable at runtime.
Current:
Future:
Contributions welcome! This library is in active development.
Ideas for contribution:
saveChainState(), loadChainState())MIT
Created by Hugo McPhee
Built on Repond for state management.
FAQs
events for repond
We found that repond-events demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.

Research
/Security News
Campaign of 108 extensions harvests identities, steals sessions, and adds backdoors to browsers, all tied to the same C2 infrastructure.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.