runtime-intent-z
v2.2.1
Published
Intent-first orchestration engine with computed graph and effect pipeline
Maintainers
Readme
🔀 runtime-intent-z
LIVE EXAMPLE
runtime-intent-z is a lightweight intent-first orchestration engine for frontend & headless applications.
Think of it as:
“Business behavior runtime, independent from framework & rendering.”
✨ Why / When to Use
Use runtime-intent-z when:
- Business logic should not live inside UI components
- UI should only emit intent, not orchestrate logic
- Async flows are complex (login → fetch → redirect → notify)
- Side effects must be predictable & testable
- You want headless tests without rendering React
- Multiple independent domains / engines exist
- You follow DDD, hexagonal, or layered architecture
📦 Installation
npm install runtime-intent-z🚀 Basic Usage (Headless / No Framework)
import { createEngine } from "runtime-intent-z"
// 1️⃣ Create engine
const engine = createEngine({ name: "cart" })
// 2️⃣ Define intent
engine.intent("cart.addItem", {
reducer(state, item: { price: number; name: string }) {
return {
...state,
cart: {
items: [...(state.cart?.items ?? []), item]
}
}
}
})
// 3️⃣ Define computed properties
engine.computed("cart.total", {
deps: ["cart"],
onIntent: ["cart.addItem"],
compute({ values }) {
const items = values.cart?.items ?? []
return items.reduce((sum, item) => sum + item.price, 0)
},
effect({ value, intent }) {
console.log("[computed]", intent, "→ total =", value)
}
})
engine.computed("cart.canCheckout", {
deps: ["cart.total"],
compute({ values }) {
return values["cart.total"] > 0
}
})
// 4️⃣ Dispatch intents
engine.dispatch("cart.addItem", { name: "Apple", price: 100 })
engine.dispatch("cart.addItem", { name: "Banana", price: 50 })
console.log(engine.getComputed("cart.total")) // 150
console.log(engine.getComputed("cart.canCheckout")) // true⚛️ Using with React
1️⃣ Without Provider (module singleton)
// cart.engine.ts
import { createEngine } from "runtime-intent-z"
// 1️⃣ Create a module singleton engine
export const cartEngine = createEngine({ name: "cart" })
cartEngine.intent("cart.addItem", {
reducer(state, item: { price: number; name: string }) {
return { items: [...(state.items ?? []), item] }
}
})
cartEngine.computed("cart.total", {
deps: ["items"],
compute({ values }) {
const items = values.items ?? []
return items.reduce((sum: number, item: any) => sum + item.price, 0)
}
})
// Cart.tsx
import { useEffect, useState } from "react"
import { cartEngine } from "./cart.engine"
export function Cart() {
const [, forceUpdate] = useState(0)
// 2️⃣ Subscribe to engine updates
useEffect(() => {
return cartEngine.subscribe(() => forceUpdate(x => x + 1))
}, [])
// 3️⃣ Access state & computed
const total = cartEngine.getComputed<number>("cart.total")
const items = cartEngine.getState().items ?? []
return (
<div>
<h2>Cart</h2>
<ul>
{items.map((item: any, i: number) => (
<li key={i}>{item.name} - ${item.price}</li>
))}
</ul>
<p>Total: {total}</p>
<button onClick={() => cartEngine.dispatch("cart.addItem", { name: "Apple", price: 10 })}>
Add Apple
</button>
</div>
)
}✅ Engine is headless, framework-agnostic.
✅ No Provider, No Context.
2️⃣ With Provider (React context)
// CartEngineContext.tsx
import { createContext, useContext } from "react"
import { cartEngine, Engine } from "runtime-intent-z"
export const CartEngineContext = createContext<Engine<any, any>>(cartEngine)
export function useCartEngine() {
return useContext(CartEngineContext)
}
// App.tsx
import { CartEngineContext } from "./CartEngineContext"
import { cartEngine } from "./cart.engine"
import { Cart } from "./Cart"
export function App() {
return (
<CartEngineContext.Provider value={cartEngine}>
<Cart />
</CartEngineContext.Provider>
)
}
// Cart.tsx
import { useEffect, useState } from "react"
import { useCartEngine } from "./CartEngineContext"
export function Cart() {
const engine = useCartEngine()
const [, forceUpdate] = useState(0)
useEffect(() => engine.subscribe(() => forceUpdate(x => x + 1)), [])
const total = engine.getComputed<number>("cart.total")
return <div>Total: {total}</div>
}✅ You can swap engines dynamically per subtree.
✅ Multiple engine instances coexist.
✅ Works well with dependency injection / multi-tenant UI.
⚡ React + runtime-intent-z (Vue3 Composition API Style)
// -------------------- cart-example.tsx --------------------
import React, { useEffect, useRef, useState } from "react"
import { defineEngine, defineComputed, Engine, ComputedDef, IntentDef } from "runtime-intent-z"
// -------------------- Runtime React Hooks --------------------
export function useComputed<TState extends object, TComputed extends Record<string, any>, K extends keyof TComputed>(
engine: Engine<TState, TComputed>,
key: K
): TComputed[K] {
const [, forceUpdate] = useState(0)
const valueRef = useRef<TComputed[K]>(engine.getComputed(key))
useEffect(() => {
const unsubscribe = engine.watch(key, ({ next }) => {
valueRef.current = next
forceUpdate(x => x + 1)
})
return unsubscribe
}, [engine, key])
return valueRef.current
}
export function useEngineState<TState extends object, TComputed extends Record<string, any>>(
engine: Engine<TState, TComputed>
): TState {
const [, forceUpdate] = useState(0)
const stateRef = useRef<TState>(engine.getState())
useEffect(() => {
const unsubscribe = engine.subscribe(({ state }) => {
stateRef.current = state
forceUpdate(x => x + 1)
})
return unsubscribe
}, [engine])
return stateRef.current
}
// -------------------- Engine Definition --------------------
type CartItem = { price: number }
export const cartEngine = defineEngine({
name: "cart",
state: () => ({
cart: { items: [] as CartItem[] }
}),
computeds: {
total: defineComputed({
deps: ["cart"],
compute({ values }) {
return (values.cart?.items ?? []).reduce((sum: number, item: CartItem) => sum + item.price, 0)
}
}),
canCheckout: defineComputed({
deps: ["total"],
compute({ values }) {
return values.total > 0
}
})
}
})
// Define the intent to add items
cartEngine.intent("cart.addItem", {
reducer(state, payload: CartItem) {
return {
...state,
cart: { items: [...(state.cart.items ?? []), payload] }
}
}
})
// -------------------- React Component --------------------
export function Cart() {
const total = useComputed(cartEngine, "total")
const canCheckout = useComputed(cartEngine, "canCheckout")
const state = useEngineState(cartEngine)
return (
<div>
<h2>Cart Example</h2>
<ul>
{(state.cart.items ?? []).map((item, i) => (
<li key={i}>${item.price}</li>
))}
</ul>
<div>Total: {total}</div>
<div>Can Checkout: {canCheckout ? "Yes" : "No"}</div>
<button onClick={() => cartEngine.dispatch("cart.addItem", { price: 100 })}>
Add 100
</button>
<button onClick={() => cartEngine.dispatch("cart.addItem", { price: 50 })}>
Add 50
</button>
</div>
)
}🔹 Works like Vue3 Composition API (reactive + computed) but fully in React.
🔹 Computed values automatically updated when dependencies change.
🔹 Engine is decoupled from UI; React just observes it.
⚡ Async Orchestration Example
engine.intent("user.login", {
effect(_, { engine }) {
engine.dispatchAsync(
"user.login.start",
null,
async () => {
const user = await fetch("/api/login").then(r => r.json())
engine.dispatch("user.login.success", user)
}
)
}
})- No async logic in UI.
- Flow is deterministic.
- Easy to test.
🧩 Multiple Engines
const authEngine = createEngine({ name: "auth" })
const cartEngine = createEngine({ name: "cart" })🧪 Testing Example (Headless)
import { createEngine } from "runtime-intent-z"
test("cart.addItem updates total", () => {
const engine = createEngine({ name: "test" })
engine.intent("add", {
reducer(state, n: number) {
return { total: (state.total ?? 0) + n }
}
})
engine.computed("total", {
deps: ["total"],
compute({ values }) {
return values.total
}
})
engine.dispatch("add", 5)
engine.dispatch("add", 10)
expect(engine.getComputed<number>("total")).toBe(15)
})
🔍 Comparison
| Feature | runtime-intent-z | Redux | Event Bus | | -------------- | ---------------- | ----------- | --------- | | Intent-based | ✅ | ❌ | ❌ | | Async built-in | ✅ | ❌ | ❌ | | Side effects | First-class | Middleware | Ad-hoc | | Computed graph | ✅ | ❌ | ❌ | | Headless test | ✅ | ⚠️ | ❌ |
🧠 Mental Model
UI / Adapter Layer
└─ emits intent
↓
Intent Engine (runtime-intent-z)
├─ intent handlers (reducers + effects)
├─ async orchestration
├─ computed graph
└─ side effects- UI never orchestrates logic.
- It only says: “this intent happened”.
📜 License
MIT
