npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

repond-events

v0.3.3

Published

events for repond

Readme

Repond Events

Serializable event orchestration with fine-grained control

Unlike setTimeout or typical event systems, repond-events lets you:

  • Save and restore entire event chains (perfect for game save systems)
  • Pause, resume, and control time (slow-motion, fast-forward, independent time streams)
  • Generate events dynamically from AI/LLMs or load from servers
  • Chain events with late-binding values for complex, composable sequences
  • Conditional logic through custom values (yes, if/then/else is possible!)

Built on Repond for reactive state management.


Why Repond Events?

The Problem:

Traditional event systems use setTimeout and callbacks, which are:

  • ❌ Not serializable (can't save mid-execution)
  • ❌ Can't be paused or resumed
  • ❌ Tied to wall-clock time (no slow-motion/fast-forward)
  • ❌ Hard to chain with dynamic parameters

The Solution:

Repond Events provides serializable event orchestration:

  • ✅ Event chains are plain data (JSON-compatible)
  • ✅ Full lifecycle control (pause, resume, skip, cancel)
  • ✅ Custom time sources (your time, not Date.now())
  • ✅ Late-binding values for dynamic composition
  • ✅ Works anywhere - games, UI workflows, offline-first apps, LLM-generated content

Quick Start

Installation

npm install repond-events repond

Basic Example

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!

Core Concepts

1. Events Execute Sequentially

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.

2. Values Enable Late-Binding

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.

3. Custom Values Unlock Conditional Logic

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" })
      ]
    })
  })
});

4. Everything is Serializable

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:

  • Save/load game state (save mid-cutscene!)
  • LLM-generated events (ChatGPT creates quests)
  • Server-driven content (update without app updates)
  • Works on iOS (no runtime code compilation)

Key Features

Pause/Resume Events

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

Custom Time Speed

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-Generated Events

// 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

Save/Load Event Chains

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


Use Cases

Games

  • Cutscenes and dialogue systems
  • Quest sequences
  • Combat choreography
  • Tutorial flows
  • Pause menus (that actually pause events!)
  • Slow-motion mechanics

UI Workflows

  • Multi-step wizards
  • Onboarding flows
  • Animated transitions with timing control
  • Form validation sequences

Offline-First Apps

  • Queue API calls as events
  • Serialize and sync across devices
  • Resume operations when back online

LLM/AI-Generated Content

  • ChatGPT generates quest chains
  • Server-driven event sequences
  • Dynamic dialogue trees
  • Personalized user experiences

Comparison

| 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 | ❌ | ✅ |


Documentation


API Overview

Defining Events

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,
});

Running Events

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" });

Controlling Events

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);

Creating Values

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
});

Advanced Features

  • Parallel events - Run multiple events simultaneously (isParallel: true)
  • Fast mode - Skip lifecycle overhead for performance (isFast: true)
  • Priority events - Jump the queue (runPriorityEvent)
  • Scoped variables - Chain-specific state
  • Event result chaining - Downstream events use upstream results via getEventValue
  • Multiple time paths - Different event chains can use different time sources

See Getting Started Examples for details.


Future Vision

Repond Events is part of a larger goal: making game/app logic fully serializable and tweakable at runtime.

Current:

  • ✅ State management (Repond) - serializable reactive state
  • ✅ Event orchestration (Repond Events) - serializable event chains

Future:

  • 🔮 Serializable effects - reactive state subscriptions as data
  • 🔮 Complete runtime editability - tweak logic without touching code

Contributing

Contributions welcome! This library is in active development.

Ideas for contribution:

  • Built-in conditional value library (if/then, and/or, comparisons)
  • JSON validation utilities
  • Serialization helpers (saveChainState(), loadChainState())
  • TypeScript improvements
  • More examples and use cases

License

MIT


Credits

Created by Hugo McPhee

Built on Repond for state management.


Quick Links