intentx-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Delpi.Kye
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,357 @@
1
+ # 🧩 intentx-react
2
+
3
+ [![NPM](https://img.shields.io/npm/v/intentx-react.svg)](https://www.npmjs.com/package/intentx-react) ![Downloads](https://img.shields.io/npm/dt/intentx-react.svg)
4
+
5
+ <a href="https://codesandbox.io/p/sandbox/7tgzxw" target="_blank">LIVE EXAMPLE</a>
6
+
7
+ Official React adapter for **intentx-runtime**.
8
+ Designed for deterministic orchestration across UI, backend, and workers.
9
+
10
+ > Intent-first business logic. React is just a view layer.
11
+
12
+ ---
13
+
14
+ ## 🧠 What is intentx?
15
+
16
+ `intentx-runtime` is a deterministic, intent-driven business logic runtime.
17
+
18
+ `intentx-react` is a thin React binding on top of that runtime.
19
+
20
+ You can use the runtime:
21
+
22
+ - In React
23
+ - In Node.js
24
+ - In workers
25
+ - In tests
26
+ - Without any UI
27
+
28
+ React is optional.
29
+
30
+ ---
31
+
32
+ ## 🏗 Mental Model
33
+
34
+ ```
35
+ UI / HTTP / Queue / Cron
36
+
37
+ emit(intent)
38
+
39
+ middleware / effects
40
+
41
+ intent handlers
42
+
43
+ mutate state
44
+
45
+ computed (derived state)
46
+ ```
47
+
48
+ Core principle:
49
+
50
+ > Intent is the only mutation entry point.
51
+ > The runtime owns behavior. UI only triggers intent.
52
+
53
+ ---
54
+
55
+ ## 📦 Installation
56
+
57
+ ```bash
58
+ npm install intentx-react
59
+ ```
60
+
61
+ ---
62
+
63
+ ## 🚀 Quick Start (Headless Runtime)
64
+
65
+ Even though this is the React package, the runtime is fully usable without React.
66
+
67
+ ```ts
68
+ import { createLogic } from "intentx-react"
69
+
70
+ const counterLogic = createLogic({
71
+ state: { count: 0 },
72
+
73
+ intents: bus => {
74
+ bus.on("inc", ({ setState }) => {
75
+ setState(s => { s.count++ })
76
+ })
77
+
78
+ bus.on<number>("add", ({ payload, setState }) => {
79
+ setState(s => { s.count += payload })
80
+ })
81
+ },
82
+
83
+ computed: {
84
+ double: ({ state }) => state.count * 2
85
+ }
86
+ })
87
+
88
+ async function main() {
89
+ const runtime = counterLogic.create()
90
+
91
+ await runtime.emit("inc")
92
+ await runtime.emit("add", 5)
93
+
94
+ console.log(runtime.state.double)
95
+ }
96
+
97
+ main()
98
+ ```
99
+
100
+ ✔ Deterministic
101
+ ✔ Fully testable
102
+ ✔ No React dependency
103
+
104
+ ---
105
+
106
+ ## ⚛️ React Integration
107
+
108
+ React integration is a thin adapter over the runtime.
109
+
110
+ You have two options:
111
+
112
+ - `withLogic` (recommended)
113
+ - `useLogic`
114
+
115
+ ---
116
+
117
+ #### 🧩 Option 1 — withLogic (Recommended)
118
+
119
+ Keeps your view pure.
120
+
121
+ ```tsx
122
+ import { withLogic } from "intentx-react"
123
+ import { counterLogic } from "./counter.logic"
124
+
125
+ const CounterView = ({ state, emit }) => {
126
+ return (
127
+ <div>
128
+ <h2>{state.count}</h2>
129
+ <button onClick={() => emit("inc")}>+</button>
130
+ </div>
131
+ )
132
+ }
133
+
134
+ export const CounterPage =
135
+ withLogic(counterLogic, CounterView)
136
+ ```
137
+
138
+ Why recommended?
139
+
140
+ - No hooks inside the view
141
+ - View is easily testable
142
+ - Clear separation of concerns
143
+ - Logic reusable outside React
144
+
145
+ ---
146
+
147
+ #### 🪝 Option 2 — useLogic
148
+
149
+ ```tsx
150
+ import { useLogic } from "intentx-react"
151
+ import { counterLogic } from "./counter.logic"
152
+
153
+ export function Counter() {
154
+ const { state, emit } = useLogic(counterLogic)
155
+
156
+ return (
157
+ <div>
158
+ <h2>{state.count}</h2>
159
+ <button onClick={() => emit("inc")}>+</button>
160
+ </div>
161
+ )
162
+ }
163
+ ```
164
+
165
+ ---
166
+
167
+ ## 🧠 Computed State
168
+
169
+ ```ts
170
+ computed: {
171
+ double: ({ state }) => state.count * 2,
172
+ triple: ({ state }) => state.count * 3,
173
+ }
174
+ ```
175
+
176
+ Computed values:
177
+
178
+ - Are cached
179
+ - Track dependencies automatically
180
+ - Recompute only when needed
181
+
182
+ ---
183
+
184
+ ## 🌊 Async Logic
185
+
186
+ Async is just another intent.
187
+
188
+ ```ts
189
+ bus.on("fetchUser", async ({ setState }) => {
190
+ setState(s => { s.loading = true })
191
+
192
+ const user = await api.getUser()
193
+
194
+ setState(s => {
195
+ s.user = user
196
+ s.loading = false
197
+ })
198
+ })
199
+ ```
200
+
201
+ No special async API required.
202
+
203
+ ---
204
+
205
+ ## 🧪 Unit Testing
206
+
207
+ ```ts
208
+ const logic = createLogic({
209
+ state: { value: 0 },
210
+
211
+ intents: bus => {
212
+ bus.on<number>("set", ({ payload, setState }) => {
213
+ setState(s => { s.value = payload })
214
+ })
215
+ },
216
+
217
+ computed: {
218
+ squared: ({ state }) => state.value * state.value
219
+ }
220
+ })
221
+
222
+ const runtime = logic.create()
223
+
224
+ await runtime.emit("set", 4)
225
+
226
+ expect(runtime.state.squared).toBe(16)
227
+ ```
228
+
229
+ ---
230
+
231
+ ## 🔄 Multiple Logic Communication
232
+
233
+ Multiple runtime instances can communicate through a shared intent bus.
234
+
235
+ By default, each `useLogic` call is fully isolated.
236
+ To enable communication, you can share a bus.
237
+
238
+ ---
239
+
240
+ #### 1️⃣ Scoped Global Bus
241
+
242
+ When `sharedBus = true`, runtimes share a bus **within the same scope**.
243
+
244
+ ```ts
245
+ import { useLogic } from "intentx-react"
246
+
247
+ // ✅ Same scope → shared bus
248
+ useLogic(counterLogic, "app", true)
249
+ useLogic(userLogic, "app", true)
250
+
251
+ // ❌ Different scope → isolated
252
+ useLogic(counterLogic, "counter", true)
253
+ useLogic(userLogic, "user", true)
254
+ ```
255
+
256
+ <b>Behavior</b>
257
+
258
+ - Same scope → runtimes can emit/listen to each other
259
+
260
+ - Different scope → fully isolated
261
+
262
+ - No global leakage
263
+
264
+ This is the recommended way for modular communication.
265
+
266
+ ---
267
+
268
+ #### 2️⃣ Custom Bus (Advanced)
269
+
270
+ ```ts
271
+ import { createIntentBus, useLogic } from "intentx-react"
272
+
273
+ const bus = createIntentBus()
274
+
275
+ useLogic(counterLogic, "counter", bus)
276
+ useLogic(userLogic, "user", bus)
277
+ ```
278
+
279
+ <b>Behavior</b>
280
+
281
+ - Cross-scope communication
282
+ - Explicit orchestration control
283
+ - Useful for complex workflows or app-wide coordination
284
+
285
+ ---
286
+
287
+ #### 🧠 Communication Modes
288
+
289
+ | Mode | Isolation | Cross Logic | Control |
290
+ | ------------------------------- | --------- | ----------- | ------- |
291
+ | Default | ✅ Full | ❌ No | Low |
292
+ | `sharedBus = true` (same scope) | ⚖ Scoped | ✅ Yes | Medium |
293
+ | Custom bus instance | ❌ Manual | ✅ Yes | High |
294
+
295
+
296
+ <br />
297
+
298
+ <b>⚠️ Important Notes</b>
299
+
300
+ - sharedBus = true shares by scope, not globally.
301
+ - Passing a bus instance overrides scope behavior.
302
+ - Scope and bus are immutable after mount.
303
+
304
+ ---
305
+
306
+
307
+ ## 🧭 Comparison (Conceptual)
308
+
309
+ | Criteria | intentx-runtime | Redux Toolkit | Zustand | MobX | Recoil |
310
+ |--------------------------------|----------------|-----------------|---------|---------------|--------|
311
+ | **Primary abstraction** | 🟢 Intent | 🟢 Reducer | 🟢 Store | 🟢 Observable | 🟢 Atom |
312
+ | **Single mutation entry** | ✅ | ✅ | ❌ | ❌ | ❌ |
313
+ | **Built-in orchestration** | ✅ | ⚠️ (middleware) | ❌ | ⚠️ | ❌ |
314
+ | **Async as first-class** | ✅ | ⚠️ (thunk) | ❌ | ⚠️ | ⚠️ |
315
+ | **Derived state built-in** | ✅ | ❌ | ⚠️ | ✅ | ✅ |
316
+ | **Deterministic execution** | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ |
317
+ | **Headless runtime** | ✅ | ⚠️ | ⚠️ | ⚠️ | ❌ |
318
+ | **Backend / worker ready** | ✅ | ⚠️ | ⚠️ | ❌ | ❌ |
319
+ | **Behavior-centric design** | ✅ | ❌ | ❌ | ❌ | ❌ |
320
+
321
+ <br/>
322
+
323
+ 🟢 = Primary design focus
324
+ ✅ = Built-in / first-class
325
+ ⚠️ = Possible but not first-class or requires extra tooling
326
+ ❌ = Not built-in
327
+
328
+ Most libraries answer:
329
+
330
+ > "How is state stored and updated?"
331
+
332
+ intentx answers:
333
+
334
+ > "What behavior happens when this event occurs?"
335
+
336
+ ---
337
+
338
+ ## 🎯 When To Use
339
+
340
+ Use this if:
341
+
342
+ - You have complex async flows
343
+ - You want deterministic replayable behavior
344
+ - You share logic between frontend and backend
345
+ - You want strict mutation boundaries
346
+
347
+ Do not use this if:
348
+
349
+ - Your app only manages simple UI state
350
+ - Your async flows are trivial
351
+ - You don't need orchestration
352
+
353
+ ---
354
+
355
+ ## 🪪 License
356
+
357
+ MIT
@@ -0,0 +1 @@
1
+ "use strict";var e=require("intentx-runtime"),t=require("react"),n=require("react/jsx-runtime");function r(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}}),t.default=e,Object.freeze(t)}var c=r(t);const i=new Map;function u(t,n,r){const u=c.useRef(null),o=!0===r?function(t){return t?"string"!=typeof t?e.createIntentBus():(i.has(t)||i.set(t,e.createIntentBus()),i.get(t)):e.createIntentBus()}(n):r||void 0;u.current||(u.current=t.create("string"==typeof n?e.createScope(n):n,o));const s=u.current,a=c.useSyncExternalStore(s.subscribe,s.getSnapshot,s.getSnapshot),f=c.useCallback((e,t)=>s.emit(e,t),[s]);return{runtime:s,state:a,actions:s.actions,emit:f}}Object.defineProperty(exports,"createIntentBus",{enumerable:!0,get:function(){return e.createIntentBus}}),Object.defineProperty(exports,"createLogic",{enumerable:!0,get:function(){return e.createLogic}}),Object.defineProperty(exports,"effect",{enumerable:!0,get:function(){return e.effect}}),exports.setGlobalBus=function(e){},exports.useLogic=function(e,t,n){const{state:r,actions:i,emit:o}=u(e,t,n);return c.useMemo(()=>({state:r,actions:i,emit:o}),[r,i,o])},exports.withLogic=function(e,t,r,c){var i,o;const s=i=>{const{state:o,actions:s,emit:a}=u(e,r,c);return n.jsx(t,{...i,state:o,actions:s,emit:a})};return s.displayName=`withLogic(${null!==(o=null!==(i=t.displayName)&&void 0!==i?i:t.name)&&void 0!==o?o:"View"})`,s};
@@ -0,0 +1,8 @@
1
+ export { createLogic } from "intentx-runtime";
2
+ export { createIntentBus } from "intentx-runtime";
3
+ export { effect } from "intentx-runtime";
4
+ export type { LogicFactory, LogicActions, IntentMiddleware, IntentNext, } from "intentx-runtime";
5
+ export { setGlobalBus } from "./react/useLogicRuntime";
6
+ export { withLogic } from "./react/withLogic";
7
+ export type { LogicViewProps } from "./react/withLogic";
8
+ export { useLogic } from "./react/useLogic";
@@ -0,0 +1 @@
1
+ import{createScope as t,createIntentBus as e}from"intentx-runtime";export{createIntentBus,createLogic,effect}from"intentx-runtime";import*as n from"react";import{jsx as r}from"react/jsx-runtime";const i=new Map;function o(t){}function s(r,o,s){const c=n.useRef(null),a=!0===s?function(t){return t?"string"!=typeof t?e():(i.has(t)||i.set(t,e()),i.get(t)):e()}(o):s||void 0;c.current||(c.current=r.create("string"==typeof o?t(o):o,a));const u=c.current,m=n.useSyncExternalStore(u.subscribe,u.getSnapshot,u.getSnapshot),f=n.useCallback((t,e)=>u.emit(t,e),[u]);return{runtime:u,state:m,actions:u.actions,emit:f}}function c(t,e,n,i){var o,c;const a=o=>{const{state:c,actions:a,emit:u}=s(t,n,i);return r(e,{...o,state:c,actions:a,emit:u})};return a.displayName=`withLogic(${null!==(c=null!==(o=e.displayName)&&void 0!==o?o:e.name)&&void 0!==c?c:"View"})`,a}function a(t,e,r){const{state:i,actions:o,emit:c}=s(t,e,r);return n.useMemo(()=>({state:i,actions:o,emit:c}),[i,o,c])}export{o as setGlobalBus,a as useLogic,c as withLogic};
@@ -0,0 +1,7 @@
1
+ import type { LogicActions, LogicFactory } from "intentx-runtime";
2
+ import { ComputedDef } from "./withLogic";
3
+ export declare function useLogic<S extends object, C extends ComputedDef<S>, A extends LogicActions>(logic: LogicFactory<S, C, A>, scope?: string, sharedBus?: any): {
4
+ state: Readonly<S & import("intentx-runtime/build/core/runtime").InferComputed<C>>;
5
+ actions: A;
6
+ emit: (intent: string, payload?: any) => Promise<void>;
7
+ };
@@ -0,0 +1,12 @@
1
+ import { Scope, LogicRuntime, LogicActions, LogicFactory, createIntentBus } from "intentx-runtime";
2
+ import { ComputedDef } from "./withLogic";
3
+ type IntentBus = ReturnType<typeof createIntentBus>;
4
+ export declare function getGlobalBus(): IntentBus;
5
+ export declare function setGlobalBus(bus: IntentBus): void;
6
+ export declare function useLogicRuntime<S extends object, C extends ComputedDef<S>, A extends LogicActions>(logic: LogicFactory<S, C, A>, scope?: Scope | string, sharedBus?: boolean | IntentBus): {
7
+ runtime: LogicRuntime<S, C, A>;
8
+ state: Readonly<S & import("intentx-runtime/build/core/runtime").InferComputed<C>>;
9
+ actions: A;
10
+ emit: (intent: string, payload?: any) => Promise<void>;
11
+ };
12
+ export {};
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ import type { ExtractLogicTypes, LogicActions, LogicFactory } from "intentx-runtime";
3
+ export type ComputedDef<S> = Record<string, (context: {
4
+ state: Readonly<S>;
5
+ }) => any>;
6
+ export type InferComputed<C> = {
7
+ [K in keyof C]: C[K] extends (...args: any[]) => infer R ? R : never;
8
+ };
9
+ type InjectedProps<S extends object, C extends ComputedDef<S>, A extends LogicActions> = {
10
+ state: Readonly<S & InferComputed<C>>;
11
+ actions: A;
12
+ emit: (intent: string, payload?: any) => Promise<void>;
13
+ };
14
+ export type LogicViewProps<T extends LogicFactory<any, any, any>> = ExtractLogicTypes<T>;
15
+ export declare function withLogic<S extends object, C extends ComputedDef<S>, A extends LogicActions, P extends object>(logic: LogicFactory<S, C, A>, View: React.ComponentType<P & InjectedProps<S, C, A>>, scope?: string, sharedBus?: any): React.FC<Omit<P, keyof InjectedProps<S, C, A>>>;
16
+ export {};
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "intentx-react",
3
+ "version": "0.1.0",
4
+ "description": "React adapter for intentx-runtime. Intent-first, deterministic business logic orchestration with a headless runtime.",
5
+ "license": "MIT",
6
+ "author": "Delpi.Kye",
7
+ "sideEffects": false,
8
+ "type": "module",
9
+
10
+ "main": "build/index.cjs.js",
11
+ "module": "build/index.esm.js",
12
+ "types": "build/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./build/index.d.ts",
16
+ "import": "./build/index.esm.js",
17
+ "require": "./build/index.cjs.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "build"
22
+ ],
23
+
24
+ "scripts": {
25
+ "clean": "rimraf build",
26
+ "build": "rollup -c",
27
+ "cb": "npm run clean && npm run build",
28
+ "prepublishOnly": "npm run cb"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/delpikye-v/intentx-react.git"
33
+ },
34
+ "homepage": "https://github.com/delpikye-v/intentx-react#readme",
35
+ "bugs": {
36
+ "url": "https://github.com/delpikye-v/intentx-react/issues"
37
+ },
38
+
39
+ "funding": {
40
+ "type": "github",
41
+ "url": "https://github.com/sponsors/delpikye-v"
42
+ },
43
+
44
+ "keywords": [
45
+ "intent",
46
+ "intent-first",
47
+ "business-logic",
48
+ "logic-runtime",
49
+ "deterministic",
50
+ "orchestration",
51
+ "headless",
52
+ "framework-agnostic",
53
+ "react",
54
+ "react-state",
55
+ "react-architecture",
56
+ "external-store",
57
+ "computed-state",
58
+ "derived-state",
59
+ "async-orchestration",
60
+ "event-driven",
61
+ "state-management"
62
+ ],
63
+
64
+ "peerDependencies": {
65
+ "react": ">=18.0.0",
66
+ "react-dom": ">=18.0.0"
67
+ },
68
+ "devDependencies": {
69
+ "@rollup/plugin-commonjs": "^25.0.0",
70
+ "@rollup/plugin-node-resolve": "^15.2.3",
71
+ "@rollup/plugin-terser": "^0.4.4",
72
+ "@types/react": "^18.2.43",
73
+ "@types/react-dom": "^18.2.17",
74
+ "react": "^18.2.0",
75
+ "react-dom": "^18.2.0",
76
+ "rimraf": "^5.0.5",
77
+ "rollup": "^4.9.6",
78
+ "rollup-plugin-peer-deps-external": "^2.2.4",
79
+ "rollup-plugin-typescript2": "^0.36.0",
80
+ "tslib": "^2.6.2",
81
+ "typescript": "^5.3.3"
82
+ },
83
+ "dependencies": {
84
+ "intentx-runtime": "^0.1.2"
85
+ }
86
+ }