Reactive dependency graph runtime for Node.js backends. Tracks dependencies between state properties and propagates updates automatically.
Tracks which properties each effect reads and re-runs only when those properties change. Zero dependencies, TypeScript-first.
npm install @nerdalytics/beacon --save-exact
# or
yarn add @nerdalytics/beacon
# or
pnpm add @nerdalytics/beacon
# or
bun add @nerdalytics/beacon
# or
deno install npm:@nerdalytics/beacon
# or
npx jsr add @nerdalytics/beacon
# or
vlt install @nerdalytics/beaconimport { state, derive, effect } from '@nerdalytics/beacon';
const counter = state({ count: 0 });
const doubled = derive(() => counter.count * 2);
const dispose = effect(() => {
console.log(`Count: ${counter.count}, Doubled: ${doubled.value}`);
});
// => "Count: 0, Doubled: 0"
counter.count = 5;
// => "Count: 5, Doubled: 10"
dispose();
doubled.reactive = false;derive accepts async compute functions. While a computation is pending the value keeps its last settled result and pending is true; pending is itself a trackable dependency. Superseded computations are discarded, and rejections route to the onError hook.
const user = derive(async () => (await db.users.find(route.id)) ?? null);
effect(() => {
if (user.pending) return;
console.log(user.value);
});until(predicate, { timeout?, signal? }) resolves with the predicate's value once a settled evaluation is truthy — safe to await inside request handlers. It throws when called inside a reactive scope and disposes itself on settle, timeout, or abort.
await until(() => cache.warm, { timeout: 5000 });onIdle(callback) invokes the callback each time the graph goes idle after processing work (a flush or an async settlement): no effect is queued and no microtask flush is armed. A pending async computation does not keep the graph busy, so the callback fires after the flush that started the computation and again once it settles. Returns an unsubscribe function.
effect(fn, name, hooks, { schedule: 'microtask' }) defers re-runs to a microtask, coalescing writes across await boundaries into one run. The default 'sync' preserves v2000 timing exactly.
Migration note: synchronous derive and default-schedule effects behave exactly as in v2000; the only consumer-visible changes are the new pending field on derived values and async compute support.
Full documentation, API reference, and examples available at: nerdalytics.github.io/beacon
The handbook has plain-text endpoints for LLMs:
llms.txtlists available versions<version>/llms.txtlists pages for a version<version>/llms-full.txtconcatenates every page into one file
You can also append .md to any handbook page URL for its Markdown source (e.g. <version>/introduction.md).
MIT - See LICENSE for details.