Describe what matters. It watches, filters, and explains — persistently.
You're buried under emails, alerts, feeds, and messages. You can't process it all. GraphReFly lets you describe automations in plain language, review them visually, run them persistently, and trace every decision back to its source.
Docs | Spec | Python | API Reference
Email triage — "Watch my inbox. Urgent emails from my team go to a priority list. Newsletters get summarized weekly. Everything else, count by sender." It watches, classifies, and alerts — and when you ask "why was this flagged?", it walks you through the reasoning.
Spending alerts — Connect bank transactions to budget categories. Get a push notification when monthly dining exceeds your target. No polling, no manual checks — changes propagate the moment data arrives.
Knowledge management — Notes, bookmarks, highlights flow in. Contradictions surface automatically. Related ideas link themselves. Your second brain stays current without you maintaining it.
npm install @graphrefly/graphrefly
import { state, derived, effect } from "@graphrefly/graphrefly";
const count = state(0);
const doubled = derived([count], ([c]) => c * 2);
effect([doubled], ([d]) => console.log("doubled:", d));
// → doubled: 0
count.set(3);
// → doubled: 6
You describe what you need — an LLM composes a reactive graph (like SQL for data flows). The graph runs persistently, checkpoints its state, and traces every decision through a causal chain. Ask "why?" at any point and get a human-readable explanation from source to conclusion.
| Zustand / Jotai | RxJS | XState | LangGraph | TC39 Signals | GraphReFly | |
|---|---|---|---|---|---|---|
| Simple store API | yes | no | no | no | yes | yes |
| Streaming operators | no | yes | no | no | no | yes |
| Diamond resolution | no | n/a | n/a | n/a | partial | glitch-free |
| Graph introspection | no | no | visual | checkpoints | no | describe / observe / diagram |
| Causal tracing | no | no | no | no | no | explain every decision |
| Durable checkpoints | no | no | persistence | yes | no | file / SQLite / IndexedDB |
| LLM orchestration | no | no | no | yes | no | agentLoop / chatStream / toolRegistry |
| NL → graph composition | no | no | no | no | no | graphFromSpec / llmCompose |
| Framework adapters | React | Angular | React / Vue | n/a | varies | React / Vue / Svelte / Solid / NestJS |
| Dependencies | 0 | 0 | 0 | many | n/a | 0 |
Everything is a node. Sugar constructors give you the right shape:
import { state, derived, producer, effect, pipe } from "@graphrefly/graphrefly";
// Writable state
const name = state("world");
// Computed (re-runs when deps change)
const greeting = derived([name], ([n]) => `Hello, ${n}!`);
// Push source (timers, events, async streams)
const clock = producer((emit) => {
const id = setInterval(() => emit([[DATA, Date.now()]]), 1000);
return () => clearInterval(id);
});
// Side effect
effect([greeting], ([g]) => document.title = g);
// Operator pipeline
const delayed = pipe(clock, delay(500), map(([, ts]) => new Date(ts)));
70+ operators — transform, combine, buffer, window, rate-limit, retry, circuit-break:
import { pipe, merge, switchMap, debounceTime, retry } from "@graphrefly/graphrefly";
const search = pipe(
input,
debounceTime(300),
switchMap((query) => fromPromise(fetch(`/api?q=${query}`))),
retry({ strategy: "exponential", maxAttempts: 3 }),
);
Register nodes in a Graph for introspection, snapshot, and persistence:
import { Graph, state, derived } from "@graphrefly/graphrefly";
const g = new Graph("pricing");
const price = g.register("price", state(100));
const tax = g.register("tax", derived([price], ([p]) => p * 0.1));
const total = g.register("total", derived([price, tax], ([p, t]) => p + t));
g.describe(); // → full graph topology as JSON
g.diagram(); // → Mermaid diagram string
g.observe((e) => console.log(e)); // → live change stream
First-class patterns for LLM streaming, agent loops, and human-in-the-loop workflows:
import { chatStream, agentLoop, toolRegistry } from "@graphrefly/graphrefly";
// Streaming chat with tool use
const chat = chatStream("assistant", {
model: "claude-sonnet-4-20250514",
tools: toolRegistry("tools", { search, calculate }),
});
// Full agent loop: observe → think → act → memory
const agent = agentLoop("researcher", {
llm: chat,
memory: agentMemory({ decay: "openviking" }),
});
Drop-in bindings — your framework, your way:
// React
import { useNode } from "@graphrefly/graphrefly/compat/react";
const [value, setValue] = useNode(count);
// Vue
import { useNode } from "@graphrefly/graphrefly/compat/vue";
const value = useNode(count); // → Ref<number>
// Svelte
import { toStore } from "@graphrefly/graphrefly/compat/svelte";
const value = toStore(count); // → Svelte store
// Solid
import { useNode } from "@graphrefly/graphrefly/compat/solid";
const value = useNode(count); // → Signal<number>
// NestJS
import { GraphReflyModule } from "@graphrefly/graphrefly/compat/nestjs";
@Module({ imports: [GraphReflyModule.forRoot()] })
Prefer subpath imports for minimal bundle:
import { node, batch, DATA } from "@graphrefly/graphrefly/core";
import { map, switchMap } from "@graphrefly/graphrefly/extra";
import { Graph } from "@graphrefly/graphrefly/graph";
The root entry re-exports everything:
import { node, map, Graph } from "@graphrefly/graphrefly";
Built-in retry, circuit breakers, rate limiters, and persistent checkpoints:
import { retry, circuitBreaker, saveGraphCheckpoint, FileCheckpointAdapter } from "@graphrefly/graphrefly";
// Retry with exponential backoff
const resilient = pipe(source, retry({ strategy: "exponential" }));
// Circuit breaker
const breaker = circuitBreaker({ threshold: 5, resetTimeout: 30_000 });
// Checkpoint to file system
const adapter = new FileCheckpointAdapter("./checkpoints");
await saveGraphCheckpoint(graph, adapter);
| Path | Contents |
|---|---|
src/core/ |
Message protocol, node primitive, batch, sugar constructors |
src/extra/ |
Operators, sources, data structures, resilience, checkpoints |
src/graph/ |
Graph container, describe/observe, snapshot, persistence |
src/patterns/ |
Orchestration, messaging, memory, AI, CQRS, reactive layout |
src/compat/ |
Framework adapters (React, Vue, Svelte, Solid, NestJS) |
docs/ |
Roadmap, guidance, benchmarks |
website/ |
Astro + Starlight docs site (graphrefly.dev) |
pnpm test # vitest run
pnpm run lint # biome check
pnpm run build # tsup (ESM + CJS + .d.ts)
pnpm bench # vitest bench
GraphReFly builds on ideas from many projects and papers:
Protocol & predecessor:
Reactive design patterns:
.get()/.set() contract and the push toward language-level reactivity that clarified where signals end and graphs begin.combineLatest, mergeMap, catchError) and the DevTools observability philosophy that inspired the Inspector pattern.AI & memory:
sigmoid(log1p(count)) * exp_decay(age, 7d)) and L0/L1/L2 progressive loading strategy used in agentMemory().knowledgeGraph() design.agentMemory() design.Layout & other:
state -> derived graph.allow()/deny() policy builder DX that inspired policy(), though CASL itself was rejected as a dependency..get()/.set()/.subscribe() mapping that validated the store ergonomics.