intentx-runtime
v0.2.6
Published
Intent-first business logic runtime. Deterministic, headless, and framework-agnostic.
Maintainers
Readme

LIVE EXAMPLE
A framework-agnostic domain logic runtime
Build isolated modules. Orchestrate through intents. Scale without chaos.
✨ Why intentx-runtime?
- 🧠 Modular business logic
- 📦 Independent state per logic
- 🔄 Event-driven communication
- 🧩 Shared intent bus
- ⚡ Computed with shallow caching
- 🛠 Works in Node, React, Vue, or anywhere
This is not a UI state library.
This is a logic runtime engine.
🧠 Mental Model
UI / HTTP / Queue / Cron
↓
emit(intent)
↓
effects / middleware
↓
intent handlers
↓
mutate state
↓
computed (derived state) / subscribers📦 Installation
npm install intentx-runtime✨ Two Ways to Define Logic
intentx-runtime supports 2 styles:
- Object configuration (classic)
- Builder chain API (fluent architecture style)
Both produce the same runtime behavior.
🧩 1️⃣ Object Style (Classic)
import { createLogic, effect } from "intentx-runtime"
export const counterLogic = createLogic({
name: "counter",
state: {
count: 1,
loading: false,
},
computed: {
double: ({ state }) => state.count * 2,
triple: ({ state }) => state.count * 3,
},
intents: bus => {
bus.on("inc", ({ setState }) => {
setState(s => { s.count++ })
})
bus.on<number>("add", ({ payload, setState }) => {
setState(s => { s.count += payload })
})
bus.on<number>("inc-async", async ({ payload, setState }) => {
setState(s => { s.loading = true })
await new Promise(r => setTimeout(r, 1000))
setState(s => {
s.loading = false
})
})
bus.effect(
"inc-async",
effect(async ({ payload }) => {
console.log("effect run:", payload)
}).takeLatest().blocking()
)
},
actions: {
inc: ({ emit }) => () => emit("inc"),
add: ({ emit }) => (n: number) => emit("add", n),
incAsync: ({ emit }) => (n: number) => emit("inc-async", n),
},
})🏗 2️⃣ Builder Style (Fluent API)
Builder API allows more architectural clarity and composition.
import { createLogic, effect } from "intentx-runtime"
export const counterLogic =
createLogic({
name: "counter",
state: {
count: 1,
loading: false,
},
})
.computed({
double: ({ state }) => state.count * 2,
triple: ({ state }) => state.count * 3,
})
.intent("inc", ({ setState }) => {
setState(s => { s.count++ })
})
.intent<number>("add", ({ payload, setState }) => {
setState(s => { s.count += payload })
})
.intent("inc-async", async ({ payload, setState }) => {
setState(s => { s.loading = true })
await new Promise(r => setTimeout(r, 1000))
setState(s => {
s.count += payload
s.loading = false
})
})
.effect(
"inc-async",
effect(async ({ payload }: { payload: number }) => {
console.log("effect run:", payload)
}).takeLatest().blocking()
)
.action({
inc: ({ emit }) => () => emit("inc"),
add: ({ emit }) => (n: number) => emit("add", n),
incAsync: ({ emit }) => (n: number) => emit("inc-async", n),
})▶ Run
const runtime = counterLogic.create()
console.log("Initial:", runtime.state)
runtime.actions.incAsync(4).then(() => {
console.log("After +4:", runtime.state)
})🧠 When to Use Which?
| Style | Use When | |---------|---------------------------------------------------------------| | Object | Simple modules, compact definition | | Builder | Large domain modules, composition, plugin-style architecture |
Both styles:
- Isolate domain state
- Use shared intent bus
- Support async intent handlers
- Support effect orchestration
- Provide cached computed
- Work in Node, React, Vue, or anywhere
🧩 Multi-Logic (Shared Bus)
import { createLogic } from "intentx-runtime"
export const cartLogic = createLogic({
name: "cart",
state: {
items: [] as string[],
},
computed: {
total: ({ state }) => state.items.length,
},
intents: bus => {
bus.on<string>("add-item", ({ payload, setState }) => {
setState(s => {
s.items.push(payload)
})
})
bus.on("clear-cart", ({ setState }) => {
setState(s => {
s.items = []
})
})
},
actions: {
addItem({ emit }) {
return (item: string) => emit("add-item", item)
},
clear({ emit }) {
return () => emit("clear-cart")
},
},
})import { createLogic } from "intentx-runtime";
type AuthState = {
user: { name: string } | null
}
export const userLogic = createLogic({
state: {
// generic
user: null as null | AuthState,
},
actions: {
login: ({ emit }) => async (name: string) => {
await emit("user/login", { name })
},
},
intents: ({ on }) => {
// generic
on<{ name: string }>("user/login", ({ payload, setState }) => {
setState(draft => {
draft.user = { user: { name: payload.name } }
})
})
},
})🚀 Create App
import { createApp } from "intentx-runtime"
import { userLogic } from "./userLogic"
import { cartLogic } from "./cartLogic"
const app = createApp({
logics: {
user: userLogic,
cart: cartLogic,
},
})▶ Run
const run = async () => {
await app.logics.user.actions.login("An")
await app.logics.cart.actions.addItem("iPhone")
await app.logics.cart.actions.addItem("Macbook")
console.log(app.logics.cart.state.total)
// 👉 2
}
run()⚡ Computed Engine
Computed values:
- Recalculate only when state changes (shallow)
- Cached between reads
- Lazy evaluation
Example:
computed: {
total: ({ state }) => state.items.length
}It only recalculates when items reference changes.
🧩 Logic Isolation
Each logic:
- Has its own state
- Has its own computed
- Has its own actions
- Communicates only via intent bus
No direct cross-logic mutation.
🏗 Architecture
createApp()
│
├── shared IntentBus
│
├── userLogic runtime
│ ├ state
│ ├ computed
│ └ actions
│
└── cartLogic runtime
├ state
├ computed
└ actions🛠 Works Anywhere
Because this runtime:
- Has no framework dependency
- No React hooks
- No Vue reactivity
- Pure TypeScript
You can:
- Use in Node backend
- Wrap into React hook
- Wrap into Vue composable
- Run in service workers
🔍 Comparison
This is not about “better” — it's about architectural intent.
| Criteria | 🚀 intentx-runtime | 🧰 Redux Toolkit | 🐻 Zustand | ⚡ MobX | 🎛️ XState | | ----------------------- | ----------------------- | ---------------- | --------------- | ---------------- | ---------------- | | Event-driven Core | ✅ Native | ⚠️ Action-based | ❌ | ❌ | ✅ FSM | | Modular Isolation | ✅ True domain isolation | ⚠️ Slice-based | ⚠️ Store-based | ❌ Shared graph | ⚠️ Machine-based | | Shared Event Bus | ✅ Built-in | ❌ | ❌ | ❌ | ⚠️ Actor model | | Built-in Computed | ✅ First-class | ❌ | ❌ | ✅ | ❌ | | Framework Agnostic | ✅ 100% | ⚠️ React-first | ⚠️ React-first | ⚠️ Mostly React | ✅ 100% | | Backend Friendly | ✅ Designed for it | ❌ | ❌ | ❌ | ⚠️ Possible | | Domain-first Design | ✅ Core philosophy | ❌ | ❌ | ❌ | ⚠️ Machine-first |
Legend
Event-driven Core: Has a first-class event system for orchestration.Modular Isolation: Encourages splitting logic into isolated modules.Shared Event Bus: Supports communication between modules via central bus.Built-in Computed: Provides derived state with caching.Framework Agnostic: Not tightly coupled to React or any UI framework.Backend Friendly: Can run naturally in Node or server environments.Domain-first Design: Designed primarily for business logic, not UI state.
🧠 Positioning Summary
- Redux Toolkit → predictable UI state for React apps
- Zustand → tiny and ergonomic React store
- MobX → automatic reactivity system
- XState → explicit state machine modeling
- intentx-runtime → modular event-driven domain logic runtime
🎯 What Makes intentx-runtime Different?
- Redux for domain
- XState without machine rigidity
- Event bus with isolated state containers
- Backend-style architecture usable on frontend
🔥 Philosophy
intentx-runtime is built for:
- Complex domain logic
- Backend-like state flow
- Deterministic event system
- Scalable modular architecture
It is not meant to replace Redux or Zustand.
It is meant to replace messy business logic.
📜 License
MIT
