intentx-svelte
v0.2.0
Published
Intent-first headless state management for Svelte powered by intentx-runtime.
Downloads
905
Maintainers
Readme
🔶⚡ intentx-svelte
LIVE EXAMPLE
intentx-svelte is an architectural layer for Svelte.
It enforces a strict separation between:
- Business Logic (deterministic runtime)
- UI Rendering (Svelte reactivity)
It is not a store library.
It is a bridge between deterministic logic and Svelte’s reactive UI.
✨ Why intentx-svelte?
Use it when your UI starts to feel like business logic.
✅ Complex async workflows
✅ Intent-based architecture
✅ Microfrontend communication
✅ Testable business logic
✅ Cross-runtime reuse (Node / SSR / workers)
Avoid it when:
❌ You only need $: reactive statements
❌ Your state is purely local UI
❌ You want reducer-style state
🤔 Why not just Svelte stores?
Svelte stores are excellent for reactivity.
But they do not provide:
- Intent routing
- Cross-logic orchestration
- Deterministic runtime snapshotting
- Cross-framework runtime reuse
- Explicit business boundaries
Stores solve reactivity.
intentx solves orchestration.
🧠 Mental Model
UI / HTTP / Queue / Cron
↓
intentx-runtime
↓
Fine-grained reactivity updates UICore principle:
Intent is the only mutation entry point. The runtime owns behavior. UI only triggers intent.
📦 Installation
npm install intentx-svelte🧩 Core Logic (Framework-Agnostic)
Define logic outside components:
import { createLogic } from "intentx-svelte"
export const counterLogic = createLogic({
name: "counter",
state: {
count: 0
},
computed: {
double: ({ state }) => state.count * 2
},
actions: ({ setState }) => ({
inc() {
setState(d => {
d.count++
})
}
})
})Logic is:
- Serializable
- Deterministic
- Testable without UI
- Reusable across runtimes
🔌 Svelte Adapter
export { useLogic }useLogic creates a runtime instance bound to Svelte's store system.
🚀 Usage
// counter.ts
import { useLogic } from "intentx-svelte"
import { counterLogic } from "./counter.logic"
export const counter = useLogic(counterLogic)<script>
import { counter } from "./counter"
</script>
<button on:click={counter.actions.inc}>
{$counter.store.count}
</button>
<p>Double: {$counter.store.double}</p>No wrapper components.
No magic subscriptions.
UI only consumes state.
📦 What useLogic Returns
const counter = useLogic(counterLogic){
runtime, // ⚙ LogicRuntime instance
store, // 🗂 merged state + computed (Svelte store)
state, // 🪞 alias of store
actions, // 🎯 logic actions
emit // 📡 emit intent
}🔥 Important
storeis readonly- Mutations must go through actions
- Svelte reactivity remains untouched
📡 Multiple Logic Communication (Bus)
Each logic instance is isolated by default.
To enable communication between runtimes, you can share an Intent Bus.
1️⃣ Scoped Shared Bus (Recommended)
import { useLogic } from "intentx-react"
// ✅ Same scope → shared bus
useLogic(logic, {
scope: "dashboard",
sharedBus: true
})
// ❌ Different scope → different bus
useLogic(logic, {
scope: "settings",
sharedBus: true
})How it works
When sharedBus: true is enabled:
A singleton bus is created per scope
Same scope → same bus instance
Different scope → different bus
No global leakage
If no scope is provided:
useLogic(logic, {
sharedBus: true
})→ uses a default global scope bus.
2️⃣ Custom Bus (Advanced / Cross-Scope)
import { createIntentBus } from "intentx-svelte"
const bus = createIntentBus()
useLogic(logicA, { bus })
useLogic(logicB, { bus })
Behavior
- Full cross-scope communication
- Manual orchestration control
- Suitable for:
- Microfrontend
- App-wide coordination
- Complex workflow systems
🔍 Behavior Comparison
| Mode | Isolation | Scope-aware | Cross-scope | Recommended |
| -------------------- | ----------- | ----------- | ------------ | --------------------- |
| Default (no options) | ✅ Full | ❌ | ❌ | Small/local logic |
| sharedBus: true | ❌ Per scope | ✅ | ❌ | Modular apps |
| Custom bus | ❌ Manual | ❌ | ✅ | Advanced architecture |
🎯 Recommendation
✅ Use sharedBus for modular communication.
✅ Use custom bus for orchestration layer.
🚫 Avoid global single bus without scope in large apps.
🧩 Context API
Provide logic via Svelte context:
import { setLogicContext, useLogicContext } from "intentx-svelte"
import { counterLogic } from "./counter.logic"Provider:
<script>
setLogicContext("counter", counterLogic)
</script>
<slot />Consume:
const counter = useLogicContext("counter")🌍 SSR
- Runtime created during SSR
- Deterministic snapshot
- Hydration-safe
- No fake empty stores
- Server snapshot serializable
- Client rehydrates from deterministic state
Works with:
- SvelteKit
- Node SSR
- Edge runtimes
🧪 Testability Upgrade
No Svelte required:
const runtime = counterLogic.create()
runtime.actions.inc()
expect(runtime.state.count).toBe(1)No DOM.
No component mounting.
Pure deterministic logic.
That is the real split.
🔍 Comparison
This is not about “better”. It’s about solving different layers of the architecture.
| Problem / Responsibility | Svelte Only | intentx-svelte |
|----------------------------------|-------------|----------------|
| Local UI state | ✅ Perfect | ❌ Overkill |
| Simple derived values | ✅ $: | ❌ Unnecessary |
| Complex async workflows | ⚠️ Manual | ✅ Structured |
| Cross-component orchestration | ⚠️ Implicit | ✅ Explicit |
| Cross-runtime reuse (Node/SSR) | ❌ | ✅ |
| Deterministic snapshotting | ❌ | ✅ |
| Intent-based communication | ❌ | ✅ |
| Microfrontend signaling | ❌ | ✅ |
Svelte handles reactivity.
intentx handles orchestration.
🎯 Philosophy
Rendering is reactive.
Business logic should be deterministic.
intentx-svelte ensures they never mix.
📜 License
MIT
