pulse-state-z
v0.1.28
Published
Event-driven reactive state runtime with deterministic propagation and dependency graphs.
Maintainers
Readme
⚡ pulse-state-z
Minimal event-driven reactive state runtime built on top of eventbus-z.
pulse-state-z turns event streams into a deterministic reactive state graph
with batching, derived state, and automatic dependency tracking.
eventbus-zemits → reactive graph propagates → derived state updates
Why pulse-state-z?
- ✅ Event-driven state updates (powered by eventbus-z)
- ✅ Fine-grained reactive graph
- ✅ Derived & computed state
- ✅ Automatic dependency tracking
- ✅ Batched updates
- ✅ Async state actions
- ✅ Selector subscriptions
- ✅ Combine multiple stores
- ✅ Cycle detection
- ✅ Glitch-free propagation
- ✅ Optional persistence
Mental Model
Every store emits an event when its value changes.
signal.set()
↓
eventbus-z emit
↓
reactive graph propagation
↓
computed / derived recompute
↓
subscribers notifiedThe dependency graph guarantees deterministic and glitch-free updates.
Installation
npm install pulse-state-zQuick Example
import { signal, map, computed } from "pulse-state-z"
const count = signal(0)
const double = map(count, v => v * 2)
const total = computed(() => count() * 10)
count.set(5)
console.log(count()) // 5
console.log(double()) // 10
console.log(total()) // 50Creating a Store
import { signal } from "pulse-state-z"
const counter = signal(0)
counter.set(1)
console.log(counter()) // 1Updating State
counter.update(v => v + 1)Async updates:
await counter.asyncUpdate(async v => {
const next = await fetchValue()
return v + next
})Subscribe to Changes
counter.subscribe(() => {
console.log("value:", counter())
})Or listen directly to value changes:
counter.onChange(v => {
console.log("value:", v)
})Batch Updates
Batch multiple updates into a single propagation cycle.
// transaction
import { batch } from "pulse-state-z"
batch(() => {
counter.set(1)
counter.set(2)
counter.set(3)
})Subscribers run only once after the batch finishes.
Derived State with map
Create derived values from one or more stores.
import { map } from "pulse-state-z"
const double = map(counter, v => v * 2)
console.log(double())Multiple sources:
const a = signal(2)
const b = signal(3)
const sum = map([a, b], (x, y) => x + y)
console.log(sum()) // 5Computed
Dependencies are tracked automatically.
import { computed } from "pulse-state-z"
const count = signal(1)
const expensive = computed(() => {
console.log("compute")
return count() * 100
})
count.set(2)
count.set(3)
count.set(4)
console.log(expensive())Computed values are lazily evaluated.
They only recompute when accessed, not when dependencies change. This avoids unnecessary work in large reactive graphs.
Selector
Create lightweight derived values without creating a full reactive node.
import { selector } from "pulse-state-z"
const double = selector(counter, v => v * 2)
console.log(double())Selectors recompute only when subscribed.
Effect
Run a reactive side-effect that automatically tracks dependencies.
import { signal, effect } from "pulse-state-z"
const price = signal(100)
const qty = signal(2)
effect(() => {
console.log("total:", price() * qty())
})- Any signal accessed inside the function becomes a dependency.
- The effect re-runs automatically when those signals change.
Watch
Watch a specific signal and react to its changes.
import { signal, watch } from "pulse-state-z"
const count = signal(0)
watch(count, v => {
console.log("count:", v)
})
count.set(1)
count.set(2)watch is useful when you want to observe one signal directly
without creating a full reactive computation.
Watch Multiple Signals
const price = signal(10)
const qty = signal(2)
watch([price, qty], ([p, q]) => {
console.log("total:", p * q)
})Untrack
Read a signal without creating a dependency.
Useful for logging, debugging, or reading values that should not trigger reactive updates.
import { signal, effect, untrack } from "pulse-state-z"
const count = signal(1)
effect(() => {
console.log(
"count:",
count(),
"time:",
untrack(() => Date.now())
)
})Only count is tracked as a dependency.
Difference
| API | Purpose |
|----------- |--------------------------- |
| effect() | auto-track dependencies |
| watch() | observe specific signal(s) |
Combine Multiple Stores
import { signal, combine } from "pulse-state-z"
const user = signal({ name: "A" })
const count = signal(0)
const state = combine({
user,
count
})
console.log(state())Combined state updates when any dependency changes.
Persistence
Persist state in localStorage.
const counter = signal(0, {
persistKey: "counter"
})State is restored automatically on load.
Comparison
| Criteria | pulse-state-z | Zustand | Redux | Recoil | | ----------------------- | ------------------------ | --------------- | --------------------- | ------------- | | Core model | Event + reactive graph | Mutable store | Reducer state machine | Atom graph | | React required | ❌ No | ⚠️ Mostly | ⚠️ Mostly | ✅ Yes | | Outside React | ✅ Native | ⚠️ Limited | ✅ Yes | ❌ No | | Derived state | ✅ computed / autoComputed| ⚠️ selector | ⚠️ memo selector | ✅ selector | | Signal tracking | ✅ autoComputed | ❌ | ❌ | ⚠️ partial | | Batch update | ✅ built-in | ⚠️ React batch | ⚠️ middleware | ⚠️ React | | Glitch-free propagation | ✅ graph propagation | ❌ | ❌ | ⚠️ sometimes | | Event driven | ✅ | ❌ | ⚠️ action dispatch | ❌ | | Persistence | ✅ built-in | ⚠️ middleware | ⚠️ middleware | ❌ | | Cycle detection | ✅ | ❌ | ❌ | ❌ | | Bundle size | small | ~1kb | 6-10kb | 15kb+ |
Architecture
signal()
│
▼
eventbus-z emission
│
▼
Reactive graph propagation
│
▼
map / computed recompute
│
▼
Subscribers notifiedThe runtime guarantees:
- deterministic propagation
- cycle detection
- glitch-free updates
Ecosystem
pulse-state-z is designed to work naturally with eventbus-z.
eventbus-z provides the event transport layer,
while pulse-state-z builds the reactive dependency graph runtime on top.
pulse-state-z is part of a growing eventbus-driven ecosystem and
works naturally alongside runtimes like intentx-core-z.
Minimal API Surface
signal • map • computed • effect • watch • combine • selector • batch
A small set of primitives for building event-driven reactive state graphs.
License
MIT
