logic-runtime-react-z 3.1.1 → 3.1.3

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/README.md CHANGED
@@ -4,13 +4,12 @@
4
4
 
5
5
  <a href="https://codesandbox.io/p/sandbox/jnd992" target="_blank">LIVE EXAMPLE</a>
6
6
 
7
- **Intent-first business logic runtime**: React is a **view** logic lives **elsewhere**.
7
+ **Intent-First Business Logic Runtime.**. React is a view layer. Business logic lives elsewhere.
8
8
 
9
- A headless, deterministic, intent-driven runtime for frontend & backend logic.
10
- React components stay pure. Business logic is fully testable, replayable, and framework-agnostic.
9
+ A headless, deterministic, intent-driven runtime for frontend and backend systems.
11
10
 
12
- > **Intent is the only entry point.**
13
- > **React is optional. createLogic is the product. Everything else is an adapter.**
11
+ > **Intent is the only entry point. Logic is deterministic.**
12
+ > **React is optional** — `createLogic` is the product; everything else is an adapter.
14
13
 
15
14
  ---
16
15
 
@@ -59,7 +58,7 @@ npm install logic-runtime-react-z
59
58
  import { createLogic } from "logic-runtime-react-z"
60
59
 
61
60
  const counterLogic = createLogic({
62
- state: { count: 0 },
61
+ state: { count: 0, loading: false },
63
62
 
64
63
  intents: bus => {
65
64
  bus.on("inc", ({ setState }) => {
@@ -73,6 +72,23 @@ const counterLogic = createLogic({
73
72
  s.count += payload
74
73
  })
75
74
  })
75
+
76
+ bus.on("dec", ({ setState }) => {
77
+ setState(s => {
78
+ s.count--
79
+ })
80
+ })
81
+
82
+ bus.on("asyncInc", async ({ setState }) => {
83
+ setState(s => { s.loading = true })
84
+
85
+ await new Promise(r => setTimeout(r, 1000))
86
+
87
+ setState(s => {
88
+ s.count++
89
+ s.loading = false
90
+ })
91
+ })
76
92
  },
77
93
  })
78
94
 
@@ -111,124 +127,111 @@ computed: {
111
127
  - Reading `state.count` automatically tracks dependencies.
112
128
  - Computed values are cached and only re-evaluated when tracked dependencies change.
113
129
 
130
+
114
131
  ---
115
132
 
116
- ## ⚛️ React Integration (No Hooks Required)
133
+ ## ⚛️ React Integration
117
134
 
118
- ### Define Logic (Framework-Agnostic)
135
+ React is a thin adapter.
119
136
 
120
- ```ts
121
- // counter.logic.ts
122
- import { createLogic, effect } from "logic-runtime-react-z"
137
+ You have **2 integration styles**:
123
138
 
124
- export const counterLogic = createLogic({
125
- name: "counter",
139
+ - `withLogic` Recommended
140
+ - `useLogic` → Direct hook usage
126
141
 
127
- state: {
128
- count: 1,
129
- loading: false,
130
- },
142
+ #### 🧩 Option 1 — withLogic (Recommended)
131
143
 
132
- computed: {
133
- double: ({ state }) => state.count * 2,
134
- triple: ({ state }) => state.count * 3,
135
- },
136
-
137
- intents: bus => {
138
- bus.on("inc", ({ setState }) => {
139
- setState(s => {
140
- s.count++
141
- })
142
- })
143
-
144
- bus.on<number>("add", ({ payload, setState }) => {
145
- setState(s => {
146
- s.count += payload
147
- })
148
- })
149
-
150
- bus.on<number>("inc-async", async ({ payload, setState }) => {
151
- setState(s => {
152
- s.loading = true
153
- })
154
-
155
- await new Promise(r => setTimeout(r, 1000))
144
+ Keeps view pure and declarative.
156
145
 
157
- setState(s => {
158
- s.count += payload
159
- s.loading = false
160
- })
161
- })
146
+ ```tsx
147
+ import { withLogic, LogicViewProps } from "logic-runtime-react-z"
148
+ import { counterLogic } from "./counter.logic"
162
149
 
163
- // effects = side-effects only (no state mutation)
164
- bus.effect(
165
- "inc-async",
166
- effect(async ({ payload }) => {
167
- console.log("effect run:", payload)
168
- }).takeLatest()
169
- )
170
- },
150
+ type CounterInjected =
151
+ LogicViewProps<typeof counterLogic>
171
152
 
172
- actions: {
173
- inc({ emit }) {
174
- return () => emit("inc")
175
- },
153
+ const CounterView = ({ state, computed, intent }: LogicViewProps) => {
154
+ return (
155
+ <div>
156
+ <h2>Count: {state.count}</h2>
157
+ <p>Double: {computed.double}</p>
158
+ <p>Triple: {computed.triple}</p>
159
+
160
+ <button onClick={() => intent("inc")}>+</button>
161
+ <button onClick={() => intent("dec")}>-</button>
162
+ <button onClick={() => intent("asyncInc")}>
163
+ {state.loading ? "Loading..." : "Async +"}
164
+ </button>
165
+ </div>
166
+ )
167
+ }
176
168
 
177
- add({ emit }) {
178
- return (n: number) => emit("add", n)
179
- },
169
+ // export default withLogic(counterLogic)(CounterView)
170
+ export const CounterPage = withLogic(counterLogic, CounterView)
171
+ ```
180
172
 
181
- incAsync({ emit }) {
182
- return (n: number) => emit("inc-async", n)
183
- },
184
- },
185
- })
173
+ <b> Why this is recommended?</b>
186
174
 
187
- ```
175
+ - View is fully testable
176
+ - No hooks inside view
177
+ - Logic can be reused outside React
178
+ - Clear separation of concerns
188
179
 
189
180
  ---
190
181
 
191
- ### Pure React View (Dumb View)
182
+ #### 🪝 Option 2 useLogic
183
+
184
+ Use directly inside a component.
192
185
 
193
186
  ```tsx
194
- import React from "react"
195
- import { withLogic } from "logic-runtime-react-z"
187
+ import { useLogic } from "logic-runtime-react-z"
196
188
  import { counterLogic } from "./counter.logic"
197
189
 
198
- function CounterView(props) {
199
- const { state, actions, emit } = props
190
+ export function Counter() {
191
+ const { state, computed, intent } = useLogic(counterLogic)
200
192
 
201
193
  return (
202
- <div style={{ padding: 12 }}>
203
- <div>Triple Count: {state.triple}</div>
204
-
205
- <button onClick={actions.inc}>+1</button>
206
- <button onClick={() => actions.add(10)}>+10</button>
207
-
208
- <button
209
- disabled={state.loading}
210
- onClick={() => actions.incAsync(5)}
211
- >
212
- Async +5
213
- </button>
214
-
215
- <hr />
216
-
217
- <button onClick={() => emit("inc")}>
218
- emit("inc")
194
+ <div>
195
+ <h2>Count: {state.count}</h2>
196
+ <p>Double: {computed.double}</p>
197
+ <p>Triple: {computed.triple}</p>
198
+
199
+ <button onClick={() => intent("inc")}>+</button>
200
+ <button onClick={() => intent("dec")}>-</button>
201
+ <button onClick={() => intent("asyncInc")}>
202
+ {state.loading ? "Loading..." : "Async +"}
219
203
  </button>
220
204
  </div>
221
205
  )
222
206
  }
207
+ ```
223
208
 
224
- export const CounterPage = withLogic(counterLogic, CounterView)
209
+ Props are inferred when using useLogic, no manual generics required.
210
+
211
+ ---
212
+
213
+ ## 🌊 Async Support
214
+
215
+ Async logic is just another intent.
225
216
 
217
+ ```ts
218
+ bus.on("fetchUser", async ({ setState }) => {
219
+ setState(s => { s.loading = true })
220
+
221
+ const data = await api.getUser()
222
+
223
+ setState(s => {
224
+ s.user = data
225
+ s.loading = false
226
+ })
227
+ })
226
228
  ```
227
229
 
228
- Props are inferred when using withLogic, no manual generics required.
230
+ No special async API needed.
229
231
 
230
232
  ---
231
233
 
234
+
232
235
  ## 🧪 Backend Usage (Same Runtime)
233
236
 
234
237
  ```ts
@@ -274,197 +277,164 @@ async function run() {
274
277
  run()
275
278
  ```
276
279
 
277
- ✔ Same runtime, same behavior, no React involved.
278
- ✔ No React
279
- ✔ Replayable
280
+ ✔ Same runtime, same behavior.
281
+ ✔ No React dependency
282
+ ✔ Replayable execution
280
283
 
281
284
  ---
282
285
 
283
- ## 🪝 Hooks Examples (Optional, Thin Adapters)
284
286
 
285
- Hooks are optional convenience layers on top of the same logic runtime.
286
- They do not own state, they only subscribe to it.
287
+ ## 🧪 Unit Test Example
287
288
 
288
- #### useRuntime – full snapshot
289
289
  ```ts
290
- import { useRuntime } from "logic-runtime-react-z"
291
-
292
- function Debug() {
293
- const snapshot = useRuntime(counterLogic)
294
- return <pre>{JSON.stringify(snapshot, null, 2)}</pre>
295
- }
296
- ```
290
+ const logic = createLogic({
291
+ state: { value: 0 },
297
292
 
298
- Subscribes to full snapshot
299
- Includes state + computed
300
- ✔ Read-only
293
+ computed: {
294
+ squared: ({ state }) => state.value * state.value,
295
+ },
301
296
 
302
- #### useActions actions only (no re-render)
303
- ```ts
304
- import { useActions } from "logic-runtime-react-z"
297
+ intents: bus => {
298
+ bus.on("set", ({ payload, setState }) => {
299
+ setState(s => {
300
+ s.value = payload
301
+ })
302
+ })
303
+ },
304
+ })
305
305
 
306
- function Buttons() {
307
- const actions = useActions(counterLogic)
306
+ const runtime = logic.create()
308
307
 
309
- return (
310
- <>
311
- <button onClick={actions.inc}>+1</button>
312
- <button onClick={() => actions.add(5)}>+5</button>
313
- </>
314
- )
315
- }
308
+ await runtime.emit("set", 4)
309
+ expect(runtime.computed.squared).toBe(16)
316
310
  ```
317
311
 
312
+ ---
318
313
 
319
- No re-render on state change
320
- ✔ Fully inferred action types
321
- ✔ Ideal for buttons / handlers
322
-
323
- #### useComputed – Subscribe to computed values
324
- ```ts
325
- import { useComputed } from "logic-runtime-react-z"
314
+ ## 🔍 Comparison
326
315
 
327
- function Stats() {
328
- const { double, triple } = useComputed(counterLogic)
316
+ This is not about “better” — it's about architectural intent.
329
317
 
330
- return (
331
- <>
332
- <div>Double: {double}</div>
333
- <div>Triple: {triple}</div>
334
- </>
335
- )
336
- }
318
+ | Criteria | logic-runtime-react-z | Redux Toolkit | Zustand | Recoil | MobX |
319
+ |---------------------------------|------------------------------|----------------------|----------------|---------------|----------------|
320
+ | **Primary abstraction** | Intent runtime | Reducer store | Store | Atom graph | Observable |
321
+ | **Mental model** | Intent → Behavior → State | Action → Reducer | Mutate store | Atom graph | Reactive graph |
322
+ | **Single mutation entry** | ✅ | ✅ | ❌ | ❌ | ❌ |
323
+ | **Business logic isolation** | ✅ | ✅ | ⚠️ | ⚠️ | ⚠️ |
324
+ | **Built-in async orchestration**| ✅ | ⚠️ | ❌ | ❌ | ❌ |
325
+ | **Deterministic execution** | ✅ | ✅ | ⚠️ | ⚠️ | ⚠️ |
326
+ | **Derived state built-in** | ✅ | ❌ | ⚠️ | ✅ | ✅ |
327
+ | **Headless runtime** | ✅ | ⚠️ | ⚠️ | ❌ | ⚠️ |
328
+ | **Backend / worker ready** | ✅ | ⚠️ | ⚠️ | ❌ | ❌ |
329
+ | **Side-effect centralization** | ✅ | ⚠️ | ❌ | ❌ | ⚠️ |
330
+ | **Devtools maturity** | ⚠️ | ✅ | ⚠️ | ⚠️ | ⚠️ |
337
331
 
338
- function DoubleOnly() {
339
- const double = useComputed(counterLogic, c => c.double)
340
- return <div>{double}</div>
341
- }
342
- ```
343
332
 
344
- Only derived data
345
- ✔ Cached & reactive
346
- ✔ No state mutation possible
333
+ <br />
347
334
 
348
- #### useComputed with selector (recommended)
349
- ```ts
350
- function DoubleOnly() {
351
- const double = useComputed(
352
- counterLogic,
353
- c => c.double
354
- )
335
+ Built-in / first-class
336
+ ⚠️ Possible / usage-dependent
337
+ Not built-in
355
338
 
356
- return <div>Double: {double}</div>
357
- }
358
- ```
339
+ ---
359
340
 
360
- Component re-renders only when double changes
361
- ✔ No extra dependencies
362
- ✔ Type-safe selector
341
+ ### 🧠 Architectural Difference
363
342
 
364
- #### useLogicSelector State selector (Redux-like)
365
- ```ts
366
- import { useLogicSelector } from "logic-runtime-react-z"
343
+ Most state libraries focus on:
367
344
 
368
- function CountLabel() {
369
- const count = useLogicSelector(
370
- counterLogic.create(),
371
- state => state.count
372
- )
345
+ > **How state is stored and updated**
373
346
 
374
- return <span>{count}</span>
375
- }
376
- ```
377
-
378
- ✔ Memoized selector
379
- ✔ Fine-grained subscriptions
380
- ✔ Familiar mental model
381
-
382
- ---
347
+ `logic-runtime-react-z` focuses on:
383
348
 
384
- ## 🧱 Composing Multiple Logic Modules
349
+ > **How behavior is orchestrated through intents**
385
350
 
386
- ```ts
387
- import { composeLogic } from "logic-runtime-react-z"
388
- import { userLogic } from "./user.logic"
389
- import { cartLogic } from "./cart.logic"
351
+ Redux/Zustand answer:
352
+ > "Where is my state and how do I change it?"
390
353
 
391
- const app = composeLogic({
392
- user: userLogic,
393
- cart: cartLogic,
394
- })
354
+ This runtime answers:
355
+ > "What behavior is triggered by this event, and how should it execute?"
395
356
 
396
- await app.emit("login")
357
+ ---
397
358
 
398
- const state = app.getState()
399
- state.user
400
- state.cart
359
+ ### 🧭 Positioning Summary
401
360
 
402
- ```
361
+ - Redux → Structured state container
362
+ - Zustand → Lightweight mutable store
363
+ - Recoil → Declarative dependency graph
364
+ - MobX → Reactive observable system
365
+ - **logic-runtime-react-z → Intent-first behavior runtime**
403
366
 
404
367
  ---
405
368
 
406
- ## 🧪 Unit Test Example
369
+ ### 🎯 When This Makes Sense
407
370
 
408
- ```ts
409
- const logic = createLogic({
410
- state: { value: 0 },
371
+ Choose this if you need:
411
372
 
412
- computed: {
413
- squared: ({ state }) => state.value * state.value,
414
- },
373
+ - Complex async flows
374
+ - Deterministic replayable behavior
375
+ - Logic shared between frontend & backend
376
+ - Strong separation between UI and domain behavior
377
+ - An explicit event-driven boundary
415
378
 
416
- intents: bus => {
417
- bus.on("set", ({ payload, setState }) => {
418
- setState(s => {
419
- s.value = payload
420
- })
421
- })
422
- },
423
- })
379
+ Choose simpler state tools if:
424
380
 
425
- const runtime = counterLogic.create()
381
+ - You mostly manage UI state
382
+ - You don’t need orchestration
383
+ - Your async flows are trivial
384
+ - Your team prefers mutable patterns
426
385
 
427
- await runtime.emit("set", 4)
428
- expect(runtime.computed.squared).toBe(16)
429
- ```
386
+ ---
430
387
 
431
- Computed values are tested like plain data
388
+ ## 🧠 One-liner Takeaway
432
389
 
433
- ---
390
+ - Redux & Zustand manage **state**
391
+ - logic-runtime-react-z orchestrates **logic**
434
392
 
435
- ## 🔍 Comparison: Redux vs Zustand
393
+ ---
436
394
 
437
- | Capability / Library | logic-runtime-react-z | Redux | Zustand |
438
- |--------------------------|:---------------------:|:-----:|:-------:|
439
- | Intent-first model | ✅ | ❌ | ❌ |
440
- | State-first model | ❌ | ✅ | ✅ |
441
- | First-class effects | ✅ | ❌ | ❌ |
442
- | Computed graph | ✅ | ❌ | ⚠️ |
443
- | Deterministic execution | ✅ | ❌ | ❌ |
444
- | Logic outside React | ✅ | ❌ | ❌ |
445
- | Backend-safe | ✅ | ❌ | ❌ |
395
+ ## 🧬 Deterministic Execution Model
446
396
 
397
+ - Intents are processed sequentially
398
+ - State mutations are isolated
399
+ - Async effects can follow declared execution strategies.
400
+ - Execution order is predictable
447
401
 
448
- ##### ⚠️ via selectors, not a true dependency graph
402
+ Given the same intent sequence, the resulting state is reproducible.
449
403
 
450
404
  ---
451
405
 
452
- ## 🧠 One-liner Takeaway
406
+ ## 📐 Architecture Diagram (High-level)
453
407
 
454
- - Redux & Zustand manage **state**
455
- - logic-runtime-react-z orchestrates **logic**
408
+ ```bash
409
+ ┌─────────────┐
410
+ │ React UI │
411
+ └──────┬──────┘
412
+ │ adapter
413
+ ┌──────▼──────┐
414
+ │ Runtime │
415
+ │ (create) │
416
+ ├─────────────┤
417
+ │ Intent Bus │
418
+ │ Effects │
419
+ │ Handlers │
420
+ │ State │
421
+ │ Computed │
422
+ └─────────────┘
423
+ ```
424
+
425
+ <b>Runtime is the product. React is an adapter.</b>
456
426
 
457
427
  ---
458
428
 
459
- ## 🧬 Determinism Guarantee
429
+ ## ⚠️ When Not to Use
460
430
 
461
- - Intents are processed sequentially
462
- - State mutations are isolated
463
- - Async flows are predictable
464
- - Same inputs → same outputs
431
+ - Simple local component state
432
+ - Small apps without async complexity
433
+ - Teams unfamiliar with event-driven models
465
434
 
466
435
  ---
467
436
 
437
+
468
438
  ## License
469
439
 
470
440
  MIT
@@ -2,6 +2,7 @@ export type EffectStrategy = "default" | "takeLatest" | "debounce";
2
2
  export type EffectHandler<S = any> = (context: S) => void | Promise<void>;
3
3
  export type EffectDef<S = any> = {
4
4
  _kind: "effect";
5
+ id: symbol;
5
6
  handler: EffectHandler<S>;
6
7
  strategy: EffectStrategy;
7
8
  wait: number;
@@ -11,6 +12,7 @@ export declare function effect<S = any>(fn: EffectHandler<S>): {
11
12
  takeLatest(): any;
12
13
  debounce(ms: number): any;
13
14
  _kind: "effect";
15
+ id: symbol;
14
16
  handler: EffectHandler<S>;
15
17
  strategy: EffectStrategy;
16
18
  wait: number;
@@ -19,11 +21,13 @@ export declare function effect<S = any>(fn: EffectHandler<S>): {
19
21
  takeLatest(): any;
20
22
  debounce(ms: number): any;
21
23
  _kind: "effect";
24
+ id: symbol;
22
25
  handler: EffectHandler<S>;
23
26
  strategy: EffectStrategy;
24
27
  wait: number;
25
28
  };
26
29
  _kind: "effect";
30
+ id: symbol;
27
31
  handler: EffectHandler<S>;
28
32
  strategy: EffectStrategy;
29
33
  wait: number;
@@ -1 +1 @@
1
- "use strict";var t=require("chrono-state-z"),e=require("intentx-core-z"),s=require("react/jsx-runtime"),n=require("react");function o(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(s){if("default"!==s){var n=Object.getOwnPropertyDescriptor(t,s);Object.defineProperty(e,s,n.get?n:{enumerable:!0,get:function(){return t[s]}})}}),e.default=t,Object.freeze(e)}var r=o(n);class i{constructor(){this.handlers={},this.effects={},this.middlewares=[]}use(t){this.middlewares.push(t)}effect(t,e){var s,n;(null!==(s=(n=this.effects)[t])&&void 0!==s?s:n[t]=[]).push(e)}on(t,e){var s,n;const o=this.handlers[t];o&&o.length>0?console.warn(`[IntentBus] Duplicate intent handler "${t}" detected. Only the first handler will be used.`):(null!==(s=(n=this.handlers)[t])&&void 0!==s?s:n[t]=[]).push(e)}async emit(t,e){var s,n;const o=null!==(s=this.handlers[t])&&void 0!==s?s:[],r=null!==(n=this.effects[t])&&void 0!==n?n:[],i=(c=this.middlewares,a=async t=>{t.effects=r;for(const e of o)await e(t)},c.reduceRight((t,e)=>e(t),a));var c,a;await i(e)}}class c{constructor(t,s=e.createScope("logic")){this.computedAtoms={},this.subs=new Set,this.bus=new i,this.dirty=!0,this.isComputing=!1,this.computedKeys=new Set,this.markDirty=()=>{this.dirty=!0,this.subs.forEach(t=>t())},this.getSnapshot=()=>(this.dirty&&(this.snapshotCache=this.buildSnapshot(),this.dirty=!1),this.snapshotCache),this.subscribe=t=>(this.subs.add(t),()=>this.subs.delete(t)),this.onIntent=(t,e)=>{this.bus.on(t,e)},this.emit=async(t,e)=>{const s=new AbortController;await this.bus.emit(t,{payload:e,scope:this.scope,signal:s.signal,state:this.getSnapshot,setState:t=>this.setStateInternal(t),emit:this.emit})},this.scope=s,this.stateAtoms=this.createStateAtoms(t);for(const t in this.stateAtoms)this.stateAtoms[t].subscribe(this.markDirty);this.bus.use(function(){const t=new Map,e=new Map;return s=>async n=>{var o;const r=n.effects;if(!(null==r?void 0:r.length))return s(n);for(const s of r){const r=s.handler.toString();if("takeLatest"===s.strategy){null===(o=t.get(r))||void 0===o||o.abort();const e=new AbortController;t.set(r,e),await s.handler({...n,signal:e.signal})}else"debounce"===s.strategy?(clearTimeout(e.get(r)),e.set(r,setTimeout(()=>s.handler(n),s.wait))):await s.handler(n)}await s(n)}}())}createStateAtoms(e){const s={};for(const n in e)s[n]=t.atom(e[n]);return s}buildSnapshot(){const t={};for(const e in this.stateAtoms)t[e]=this.stateAtoms[e]();for(const e in this.computedAtoms)t[e]=this.computedAtoms[e]();return t}createReactiveState(){return new Proxy({},{get:(t,e)=>{const s=this.stateAtoms[e];return s?s():void 0}})}get state(){return this.getSnapshot()}get computed(){const t={};return this.computedKeys.forEach(e=>{t[e]=this.computedAtoms[e]()}),t}getComputedKey(t){return this.computedAtoms[t]()}getComputed(t){const e={};return this.computedKeys.forEach(s=>{e[s]=t[s]}),e}setStateInternal(t){this.isComputing&&console.warn("[logic-runtime] setState() called inside computed()");const e={};for(const t in this.stateAtoms)e[t]=this.stateAtoms[t]();t(e);for(const t in this.stateAtoms)e[t]!==this.stateAtoms[t]()&&this.stateAtoms[t].set(e[t])}useEffect(t,e){this.bus.effect(t,e)}attachComputed(e,s){const n=t.atom(void 0),o=this.createReactiveState();this.computedAtoms[e]=n,this.computedKeys.add(e),t.effect(()=>{this.isComputing=!0,n.set(s({state:o})),this.isComputing=!1}),n.subscribe(this.markDirty)}}function a(t){const e=function(){let t=0,e=[];return{get records(){return e.slice()},record:function(s){e.push({...s,id:++t,state:structuredClone(s.state)})},replay:async function(t,s){const{from:n=0,to:o=1/0,scope:r}=null!=s?s:{},i=e.filter(t=>"emit"===t.type&&t.id>=n&&t.id<=o&&(!r||t.scope===r));for(const e of i){const s=t(e.intent,e.payload);s instanceof Promise&&await s}},clear:function(){e=[],t=0}}}();function s(){return"string"==typeof t.scope?t.scope:t.scope.name}return{timeline:e,wrap:function(){const n=t.emit.bind(t);t.emit=async(o,r)=>{e.record({type:"emit:start",intent:o,payload:r,scope:s(),state:t.getSnapshot(),timestamp:Date.now()});try{const t=n(o,r);t instanceof Promise&&await t}finally{e.record({type:"emit:end",intent:o,payload:r,scope:s(),state:t.getSnapshot(),timestamp:Date.now()})}}}}}function u(t){var s;const o=n.useRef(null),r=t&&"function"==typeof t.subscribe&&"function"==typeof t.getSnapshot?t:null!==(s=o.current)&&void 0!==s?s:o.current=t.create(e.createScope("react"));return n.useSyncExternalStore(r.subscribe,r.getSnapshot,r.getSnapshot)}Object.defineProperty(exports,"createSelector",{enumerable:!0,get:function(){return t.createSelector}}),exports.LogicRuntime=c,exports.attachDevtools=a,exports.composeLogic=function(t){const s=e.createScope("logic"),n={};for(const e in t)n[e]=t[e].create(s);const o={};for(const t in n)o[t]=n[t].actions;return{scope:s,runtimes:n,emit:async(t,e)=>{await Promise.all(Object.values(n).map(s=>s.emit(t,e)))},actions:o,getState:()=>{const t={};for(const e in n)t[e]=n[e].state;return t}}},exports.createBackendRuntime=function(t){var s,n;let o=structuredClone(t);const r=()=>o,i=t=>{o={...o,...t}},c=e.createScope("backend");async function u(t,e){await l.emit(t,e,c)}const l=e.createIntentBus(t=>{const e=new AbortController;return{scope:c,payload:t,signal:e.signal,get state(){return r()},setState(){throw new Error("setState is not allowed in backend runtime")},emit:u}}),h={state:r,reset:()=>{o=structuredClone(t)},emit:u,registerIntents:function(t){for(const e in t){const s=t[e];l.on(e,async(t,e)=>{var n;const o={get state(){return r()},signal:null!==(n=t.signal)&&void 0!==n?n:(new AbortController).signal,set:i,emit:u};await s(o)},c)}},onIntent:l.on,effect:l.effect};if("production"!==(null===(n=null===(s=null===globalThis||void 0===globalThis?void 0:globalThis.process)||void 0===s?void 0:s.env)||void 0===n?void 0:n.NODE_ENV)){const t=a(h);t.wrap(h),h.devtools=t}return h},exports.createLogic=function(t){return{name:t.name,create(e){var s;const n=new c(structuredClone(t.state),e);if(t.computed)for(const e in t.computed)n.attachComputed(e,t.computed[e]);null===(s=t.intents)||void 0===s||s.call(t,{on:n.onIntent,effect:n.useEffect.bind(n)});const o={};if(t.actions)for(const e in t.actions)o[e]=t.actions[e]({emit:n.emit,getState:n.getSnapshot});return n.actions=o,n}}},exports.effect=function(t){const e={_kind:"effect",handler:t,strategy:"default",wait:0};return{...e,takeLatest(){return e.strategy="takeLatest",this},debounce(t){return e.strategy="debounce",e.wait=t,this}}},exports.useActions=function(t){return(t instanceof c?t:u(t).__runtime).actions},exports.useComputed=function(e,s){const o=e instanceof c?e:u(e).__runtime;if(!s)return n.useSyncExternalStore(o.subscribe,()=>o.getComputed(o.getSnapshot()),()=>o.getComputed(o.getSnapshot()));const r=n.useMemo(()=>t.createSelector(s),[s]);return n.useSyncExternalStore(o.subscribe,()=>r(o.getComputed(o.getSnapshot())),()=>r(o.getComputed(o.getSnapshot())))},exports.useLogic=function(t){const e=n.useSyncExternalStore(t.subscribe,t.getSnapshot,t.getSnapshot);return{state:e,computed:t.getComputed(e),actions:t.actions}},exports.useLogicSelector=function(e,s){const o=n.useMemo(()=>t.createSelector(s),[s]);return n.useSyncExternalStore(e.subscribe,()=>o(e.getSnapshot()),()=>o(e.getSnapshot()))},exports.useRuntime=u,exports.withLogic=function(t,n,o){var i,c;const a=i=>{const c=r.useRef(null);c.current||(c.current=t.create("string"==typeof o?e.createScope(o):o));const a=c.current,u=r.useSyncExternalStore(a.subscribe,a.getSnapshot,a.getSnapshot),l=r.useCallback((t,e)=>a.emit(t,e),[a]),h=r.useMemo(()=>({state:u,actions:a.actions,emit:l}),[u,l,a]);return s.jsx(n,{...i,...h})};return a.displayName=`withLogic(${null!==(c=null!==(i=n.displayName)&&void 0!==i?i:n.name)&&void 0!==c?c:"View"})`,a};
1
+ "use strict";var t=require("chrono-state-z"),e=require("intentx-core-z"),s=require("react/jsx-runtime");function n(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(s){if("default"!==s){var n=Object.getOwnPropertyDescriptor(t,s);Object.defineProperty(e,s,n.get?n:{enumerable:!0,get:function(){return t[s]}})}}),e.default=t,Object.freeze(e)}var o=n(require("react"));class i{constructor(){this.handlers={},this.effects={},this.middlewares=[]}use(t){this.middlewares.push(t)}effect(t,e){var s,n;(null!==(s=(n=this.effects)[t])&&void 0!==s?s:n[t]=[]).push(e)}on(t,e){var s,n;const o=this.handlers[t];o&&o.length>0?console.warn(`[IntentBus] Duplicate intent handler "${t}" detected. Only the first handler will be used.`):(null!==(s=(n=this.handlers)[t])&&void 0!==s?s:n[t]=[]).push(e)}async emit(t,e){var s,n;const o=null!==(s=this.handlers[t])&&void 0!==s?s:[],i=null!==(n=this.effects[t])&&void 0!==n?n:[],r=(a=this.middlewares,c=async t=>{t.effects=i;for(const e of o)await e(t)},a.reduceRight((t,e)=>e(t),c));var a,c;await r(e)}}class r{constructor(t,s=e.createScope("logic")){this.computedAtoms={},this.subs=new Set,this.bus=new i,this.dirty=!0,this.isComputing=!1,this.computedKeys=new Set,this.markDirty=()=>{this.dirty=!0,this.subs.forEach(t=>t())},this.getSnapshot=()=>(this.dirty&&(this.snapshotCache=this.buildSnapshot(),this.dirty=!1),this.snapshotCache),this.subscribe=t=>(this.subs.add(t),()=>this.subs.delete(t)),this.onIntent=(t,e)=>{this.bus.on(t,e)},this.emit=async(t,e)=>{const s=new AbortController;await this.bus.emit(t,{payload:e,scope:this.scope,signal:s.signal,state:this.getSnapshot,setState:t=>this.setStateInternal(t),emit:this.emit})},this.scope=s,this.stateAtoms=this.createStateAtoms(t);for(const t in this.stateAtoms)this.stateAtoms[t].subscribe(this.markDirty);this.bus.use(function(){const t=new Map,e=new Map;return s=>async n=>{var o;const i=n.effects;if(!(null==i?void 0:i.length))return s(n);for(const s of i){const i=s.id;if("takeLatest"===s.strategy){null===(o=t.get(i))||void 0===o||o.abort();const e=new AbortController;t.set(i,e),await s.handler({...n,signal:e.signal})}else"debounce"===s.strategy?(clearTimeout(e.get(i)),e.set(i,setTimeout(()=>s.handler(n),s.wait))):await s.handler(n)}await s(n)}}())}createStateAtoms(e){const s={};for(const n in e)s[n]=t.atom(e[n]);return s}buildSnapshot(){const t={};for(const e in this.stateAtoms)t[e]=this.stateAtoms[e]();for(const e in this.computedAtoms)t[e]=this.computedAtoms[e]();return t}createReactiveState(){return new Proxy({},{get:(t,e)=>{const s=this.stateAtoms[e];return s?s():void 0}})}get state(){return this.getSnapshot()}get computed(){const t={};return this.computedKeys.forEach(e=>{t[e]=this.computedAtoms[e]()}),t}getComputedKey(t){return this.computedAtoms[t]()}getComputed(t){const e={};return this.computedKeys.forEach(s=>{e[s]=t[s]}),e}setStateInternal(t){this.isComputing&&console.warn("[logic-runtime] setState() called inside computed()");const e={};for(const t in this.stateAtoms)e[t]=this.stateAtoms[t]();t(e);for(const t in this.stateAtoms)e[t]!==this.stateAtoms[t]()&&this.stateAtoms[t].set(e[t])}useEffect(t,e){this.bus.effect(t,e)}attachComputed(e,s){const n=t.atom(void 0),o=this.createReactiveState();this.computedAtoms[e]=n,this.computedKeys.add(e);const i=()=>{this.isComputing=!0,n.set(s({state:o})),this.isComputing=!1};t.effect(i),i(),n.subscribe(this.markDirty)}}function a(t){const e=function(){let t=0,e=[];return{get records(){return e.slice()},record:function(s){e.push({...s,id:++t,state:structuredClone(s.state)})},replay:async function(t,s){const{from:n=0,to:o=1/0,scope:i}=null!=s?s:{},r=e.filter(t=>"emit"===t.type&&t.id>=n&&t.id<=o&&(!i||t.scope===i));for(const e of r){const s=t(e.intent,e.payload);s instanceof Promise&&await s}},clear:function(){e=[],t=0}}}();function s(){return"string"==typeof t.scope?t.scope:t.scope.name}return{timeline:e,wrap:function(){const n=t.emit.bind(t);t.emit=async(o,i)=>{e.record({type:"emit:start",intent:o,payload:i,scope:s(),state:t.getSnapshot(),timestamp:Date.now()});try{const t=n(o,i);t instanceof Promise&&await t}finally{e.record({type:"emit:end",intent:o,payload:i,scope:s(),state:t.getSnapshot(),timestamp:Date.now()})}}}}}Object.defineProperty(exports,"createSelector",{enumerable:!0,get:function(){return t.createSelector}}),exports.LogicRuntime=r,exports.attachDevtools=a,exports.createBackendRuntime=function(t){var s,n;let o=structuredClone(t);const i=()=>o,r=t=>{o={...o,...t}},c=e.createScope("backend");async function u(t,e){await l.emit(t,e,c)}const l=e.createIntentBus(t=>{const e=new AbortController;return{scope:c,payload:t,signal:e.signal,get state(){return i()},setState(){throw new Error("setState is not allowed in backend runtime")},emit:u}}),h={state:i,reset:()=>{o=structuredClone(t)},emit:u,registerIntents:function(t){for(const e in t){const s=t[e];l.on(e,async(t,e)=>{var n;const o={get state(){return i()},signal:null!==(n=t.signal)&&void 0!==n?n:(new AbortController).signal,set:r,emit:u};await s(o)},c)}},onIntent:l.on,effect:l.effect};if("production"!==(null===(n=null===(s=null===globalThis||void 0===globalThis?void 0:globalThis.process)||void 0===s?void 0:s.env)||void 0===n?void 0:n.NODE_ENV)){const t=a(h);t.wrap(h),h.devtools=t}return h},exports.createLogic=function(t){return{name:t.name,create(e){var s;const n=new r(structuredClone(t.state),e);if(t.computed)for(const e in t.computed)n.attachComputed(e,t.computed[e]);null===(s=t.intents)||void 0===s||s.call(t,{on:n.onIntent,effect:n.useEffect.bind(n)});const o={};if(t.actions){const e=Object.keys(t.actions);for(const s of e){const e=t.actions[s];o[s]=e({emit:n.emit,getState:n.getSnapshot})}}return n.actions=o,n}}},exports.effect=function(t){const e={_kind:"effect",id:Symbol("effect"),handler:t,strategy:"default",wait:0};return{...e,takeLatest(){return e.strategy="takeLatest",this},debounce(t){return e.strategy="debounce",e.wait=t,this}}},exports.useLogic=function(t,s){const n=o.useRef(null);n.current||(n.current=t.create("string"==typeof s?e.createScope(s):s));const i=n.current,r=o.useSyncExternalStore(i.subscribe,i.getSnapshot,i.getSnapshot),a=o.useCallback((t,e)=>i.emit(t,e),[i]),c=i.actions;return o.useMemo(()=>({state:r,actions:c,emit:a}),[r,c,a])},exports.withLogic=function(t,n,i){var r,a;const c=r=>{const a=o.useRef(null);a.current||(a.current=t.create("string"==typeof i?e.createScope(i):i));const c=a.current,u=o.useSyncExternalStore(c.subscribe,c.getSnapshot,c.getSnapshot),l=o.useCallback((t,e)=>c.emit(t,e),[c]),h=o.useMemo(()=>({state:u,actions:c.actions,emit:l}),[u,l,c]);return s.jsx(n,{...r,...h})};return c.displayName=`withLogic(${null!==(a=null!==(r=n.displayName)&&void 0!==r?r:n.name)&&void 0!==a?a:"View"})`,c};
package/build/index.d.ts CHANGED
@@ -5,12 +5,8 @@ export { effect } from "./core/effect";
5
5
  export type { IntentMiddleware, IntentNext, } from "./core/middleware";
6
6
  export { createLogic } from "./logic/createLogic";
7
7
  export type { LogicFactory, LogicActions, } from "./logic/createLogic";
8
- export { composeLogic } from "./logic/composeLogic";
9
8
  export { createBackendRuntime } from "./logic/createBackendRuntime";
10
9
  export { withLogic } from "./react/withLogic";
11
- export { useActions } from "./react/useActions";
12
- export { useComputed } from "./react/useComputed";
10
+ export type { LogicViewProps } from "./react/withLogic";
13
11
  export { useLogic } from "./react/useLogic";
14
- export { useLogicSelector } from "./react/useLogicSelector";
15
- export { useRuntime } from "./react/useRuntime";
16
12
  export * from "./devtools";
@@ -1 +1 @@
1
- import{atom as t,effect as e,createSelector as s}from"chrono-state-z";export{createSelector}from"chrono-state-z";import{createScope as n,createIntentBus as o}from"intentx-core-z";import{jsx as i}from"react/jsx-runtime";import*as r from"react";import{useRef as a,useSyncExternalStore as c,useMemo as u}from"react";class l{constructor(){this.handlers={},this.effects={},this.middlewares=[]}use(t){this.middlewares.push(t)}effect(t,e){var s,n;(null!==(s=(n=this.effects)[t])&&void 0!==s?s:n[t]=[]).push(e)}on(t,e){var s,n;const o=this.handlers[t];o&&o.length>0?console.warn(`[IntentBus] Duplicate intent handler "${t}" detected. Only the first handler will be used.`):(null!==(s=(n=this.handlers)[t])&&void 0!==s?s:n[t]=[]).push(e)}async emit(t,e){var s,n;const o=null!==(s=this.handlers[t])&&void 0!==s?s:[],i=null!==(n=this.effects[t])&&void 0!==n?n:[],r=(a=this.middlewares,c=async t=>{t.effects=i;for(const e of o)await e(t)},a.reduceRight((t,e)=>e(t),c));var a,c;await r(e)}}class h{constructor(t,e=n("logic")){this.computedAtoms={},this.subs=new Set,this.bus=new l,this.dirty=!0,this.isComputing=!1,this.computedKeys=new Set,this.markDirty=()=>{this.dirty=!0,this.subs.forEach(t=>t())},this.getSnapshot=()=>(this.dirty&&(this.snapshotCache=this.buildSnapshot(),this.dirty=!1),this.snapshotCache),this.subscribe=t=>(this.subs.add(t),()=>this.subs.delete(t)),this.onIntent=(t,e)=>{this.bus.on(t,e)},this.emit=async(t,e)=>{const s=new AbortController;await this.bus.emit(t,{payload:e,scope:this.scope,signal:s.signal,state:this.getSnapshot,setState:t=>this.setStateInternal(t),emit:this.emit})},this.scope=e,this.stateAtoms=this.createStateAtoms(t);for(const t in this.stateAtoms)this.stateAtoms[t].subscribe(this.markDirty);this.bus.use(function(){const t=new Map,e=new Map;return s=>async n=>{var o;const i=n.effects;if(!(null==i?void 0:i.length))return s(n);for(const s of i){const i=s.handler.toString();if("takeLatest"===s.strategy){null===(o=t.get(i))||void 0===o||o.abort();const e=new AbortController;t.set(i,e),await s.handler({...n,signal:e.signal})}else"debounce"===s.strategy?(clearTimeout(e.get(i)),e.set(i,setTimeout(()=>s.handler(n),s.wait))):await s.handler(n)}await s(n)}}())}createStateAtoms(e){const s={};for(const n in e)s[n]=t(e[n]);return s}buildSnapshot(){const t={};for(const e in this.stateAtoms)t[e]=this.stateAtoms[e]();for(const e in this.computedAtoms)t[e]=this.computedAtoms[e]();return t}createReactiveState(){return new Proxy({},{get:(t,e)=>{const s=this.stateAtoms[e];return s?s():void 0}})}get state(){return this.getSnapshot()}get computed(){const t={};return this.computedKeys.forEach(e=>{t[e]=this.computedAtoms[e]()}),t}getComputedKey(t){return this.computedAtoms[t]()}getComputed(t){const e={};return this.computedKeys.forEach(s=>{e[s]=t[s]}),e}setStateInternal(t){this.isComputing&&console.warn("[logic-runtime] setState() called inside computed()");const e={};for(const t in this.stateAtoms)e[t]=this.stateAtoms[t]();t(e);for(const t in this.stateAtoms)e[t]!==this.stateAtoms[t]()&&this.stateAtoms[t].set(e[t])}useEffect(t,e){this.bus.effect(t,e)}attachComputed(s,n){const o=t(void 0),i=this.createReactiveState();this.computedAtoms[s]=o,this.computedKeys.add(s),e(()=>{this.isComputing=!0,o.set(n({state:i})),this.isComputing=!1}),o.subscribe(this.markDirty)}}function m(t){const e={_kind:"effect",handler:t,strategy:"default",wait:0};return{...e,takeLatest(){return e.strategy="takeLatest",this},debounce(t){return e.strategy="debounce",e.wait=t,this}}}function d(t){return{name:t.name,create(e){var s;const n=new h(structuredClone(t.state),e);if(t.computed)for(const e in t.computed)n.attachComputed(e,t.computed[e]);null===(s=t.intents)||void 0===s||s.call(t,{on:n.onIntent,effect:n.useEffect.bind(n)});const o={};if(t.actions)for(const e in t.actions)o[e]=t.actions[e]({emit:n.emit,getState:n.getSnapshot});return n.actions=o,n}}}function p(t){const e=n("logic"),s={};for(const n in t)s[n]=t[n].create(e);const o={};for(const t in s)o[t]=s[t].actions;return{scope:e,runtimes:s,emit:async(t,e)=>{await Promise.all(Object.values(s).map(s=>s.emit(t,e)))},actions:o,getState:()=>{const t={};for(const e in s)t[e]=s[e].state;return t}}}function f(t){const e=function(){let t=0,e=[];return{get records(){return e.slice()},record:function(s){e.push({...s,id:++t,state:structuredClone(s.state)})},replay:async function(t,s){const{from:n=0,to:o=1/0,scope:i}=null!=s?s:{},r=e.filter(t=>"emit"===t.type&&t.id>=n&&t.id<=o&&(!i||t.scope===i));for(const e of r){const s=t(e.intent,e.payload);s instanceof Promise&&await s}},clear:function(){e=[],t=0}}}();function s(){return"string"==typeof t.scope?t.scope:t.scope.name}return{timeline:e,wrap:function(){const n=t.emit.bind(t);t.emit=async(o,i)=>{e.record({type:"emit:start",intent:o,payload:i,scope:s(),state:t.getSnapshot(),timestamp:Date.now()});try{const t=n(o,i);t instanceof Promise&&await t}finally{e.record({type:"emit:end",intent:o,payload:i,scope:s(),state:t.getSnapshot(),timestamp:Date.now()})}}}}}function g(t){var e,s;let i=structuredClone(t);const r=()=>i,a=t=>{i={...i,...t}},c=n("backend");async function u(t,e){await l.emit(t,e,c)}const l=o(t=>{const e=new AbortController;return{scope:c,payload:t,signal:e.signal,get state(){return r()},setState(){throw new Error("setState is not allowed in backend runtime")},emit:u}});const h={state:r,reset:()=>{i=structuredClone(t)},emit:u,registerIntents:function(t){for(const e in t){const s=t[e];l.on(e,async(t,e)=>{var n;const o={get state(){return r()},signal:null!==(n=t.signal)&&void 0!==n?n:(new AbortController).signal,set:a,emit:u};await s(o)},c)}},onIntent:l.on,effect:l.effect};if("production"!==(null===(s=null===(e=null===globalThis||void 0===globalThis?void 0:globalThis.process)||void 0===e?void 0:e.env)||void 0===s?void 0:s.NODE_ENV)){const t=f(h);t.wrap(h),h.devtools=t}return h}function b(t,e,s){var o,a;const c=o=>{const a=r.useRef(null);a.current||(a.current=t.create("string"==typeof s?n(s):s));const c=a.current,u=r.useSyncExternalStore(c.subscribe,c.getSnapshot,c.getSnapshot),l=r.useCallback((t,e)=>c.emit(t,e),[c]),h=r.useMemo(()=>({state:u,actions:c.actions,emit:l}),[u,l,c]);return i(e,{...o,...h})};return c.displayName=`withLogic(${null!==(a=null!==(o=e.displayName)&&void 0!==o?o:e.name)&&void 0!==a?a:"View"})`,c}function y(t){var e;const s=a(null),o=t&&"function"==typeof t.subscribe&&"function"==typeof t.getSnapshot?t:null!==(e=s.current)&&void 0!==e?e:s.current=t.create(n("react"));return c(o.subscribe,o.getSnapshot,o.getSnapshot)}function w(t){return(t instanceof h?t:y(t).__runtime).actions}function S(t,e){const n=t instanceof h?t:y(t).__runtime;if(!e)return c(n.subscribe,()=>n.getComputed(n.getSnapshot()),()=>n.getComputed(n.getSnapshot()));const o=u(()=>s(e),[e]);return c(n.subscribe,()=>o(n.getComputed(n.getSnapshot())),()=>o(n.getComputed(n.getSnapshot())))}function v(t){const e=c(t.subscribe,t.getSnapshot,t.getSnapshot);return{state:e,computed:t.getComputed(e),actions:t.actions}}function C(t,e){const n=u(()=>s(e),[e]);return c(t.subscribe,()=>n(t.getSnapshot()),()=>n(t.getSnapshot()))}export{h as LogicRuntime,f as attachDevtools,p as composeLogic,g as createBackendRuntime,d as createLogic,m as effect,w as useActions,S as useComputed,v as useLogic,C as useLogicSelector,y as useRuntime,b as withLogic};
1
+ import{atom as t,effect as e}from"chrono-state-z";export{createSelector}from"chrono-state-z";import{createScope as s,createIntentBus as n}from"intentx-core-z";import{jsx as o}from"react/jsx-runtime";import*as i from"react";class a{constructor(){this.handlers={},this.effects={},this.middlewares=[]}use(t){this.middlewares.push(t)}effect(t,e){var s,n;(null!==(s=(n=this.effects)[t])&&void 0!==s?s:n[t]=[]).push(e)}on(t,e){var s,n;const o=this.handlers[t];o&&o.length>0?console.warn(`[IntentBus] Duplicate intent handler "${t}" detected. Only the first handler will be used.`):(null!==(s=(n=this.handlers)[t])&&void 0!==s?s:n[t]=[]).push(e)}async emit(t,e){var s,n;const o=null!==(s=this.handlers[t])&&void 0!==s?s:[],i=null!==(n=this.effects[t])&&void 0!==n?n:[],a=(r=this.middlewares,c=async t=>{t.effects=i;for(const e of o)await e(t)},r.reduceRight((t,e)=>e(t),c));var r,c;await a(e)}}class r{constructor(t,e=s("logic")){this.computedAtoms={},this.subs=new Set,this.bus=new a,this.dirty=!0,this.isComputing=!1,this.computedKeys=new Set,this.markDirty=()=>{this.dirty=!0,this.subs.forEach(t=>t())},this.getSnapshot=()=>(this.dirty&&(this.snapshotCache=this.buildSnapshot(),this.dirty=!1),this.snapshotCache),this.subscribe=t=>(this.subs.add(t),()=>this.subs.delete(t)),this.onIntent=(t,e)=>{this.bus.on(t,e)},this.emit=async(t,e)=>{const s=new AbortController;await this.bus.emit(t,{payload:e,scope:this.scope,signal:s.signal,state:this.getSnapshot,setState:t=>this.setStateInternal(t),emit:this.emit})},this.scope=e,this.stateAtoms=this.createStateAtoms(t);for(const t in this.stateAtoms)this.stateAtoms[t].subscribe(this.markDirty);this.bus.use(function(){const t=new Map,e=new Map;return s=>async n=>{var o;const i=n.effects;if(!(null==i?void 0:i.length))return s(n);for(const s of i){const i=s.id;if("takeLatest"===s.strategy){null===(o=t.get(i))||void 0===o||o.abort();const e=new AbortController;t.set(i,e),await s.handler({...n,signal:e.signal})}else"debounce"===s.strategy?(clearTimeout(e.get(i)),e.set(i,setTimeout(()=>s.handler(n),s.wait))):await s.handler(n)}await s(n)}}())}createStateAtoms(e){const s={};for(const n in e)s[n]=t(e[n]);return s}buildSnapshot(){const t={};for(const e in this.stateAtoms)t[e]=this.stateAtoms[e]();for(const e in this.computedAtoms)t[e]=this.computedAtoms[e]();return t}createReactiveState(){return new Proxy({},{get:(t,e)=>{const s=this.stateAtoms[e];return s?s():void 0}})}get state(){return this.getSnapshot()}get computed(){const t={};return this.computedKeys.forEach(e=>{t[e]=this.computedAtoms[e]()}),t}getComputedKey(t){return this.computedAtoms[t]()}getComputed(t){const e={};return this.computedKeys.forEach(s=>{e[s]=t[s]}),e}setStateInternal(t){this.isComputing&&console.warn("[logic-runtime] setState() called inside computed()");const e={};for(const t in this.stateAtoms)e[t]=this.stateAtoms[t]();t(e);for(const t in this.stateAtoms)e[t]!==this.stateAtoms[t]()&&this.stateAtoms[t].set(e[t])}useEffect(t,e){this.bus.effect(t,e)}attachComputed(s,n){const o=t(void 0),i=this.createReactiveState();this.computedAtoms[s]=o,this.computedKeys.add(s);const a=()=>{this.isComputing=!0,o.set(n({state:i})),this.isComputing=!1};e(a),a(),o.subscribe(this.markDirty)}}function c(t){const e={_kind:"effect",id:Symbol("effect"),handler:t,strategy:"default",wait:0};return{...e,takeLatest(){return e.strategy="takeLatest",this},debounce(t){return e.strategy="debounce",e.wait=t,this}}}function u(t){return{name:t.name,create(e){var s;const n=new r(structuredClone(t.state),e);if(t.computed)for(const e in t.computed)n.attachComputed(e,t.computed[e]);null===(s=t.intents)||void 0===s||s.call(t,{on:n.onIntent,effect:n.useEffect.bind(n)});const o={};if(t.actions){const e=Object.keys(t.actions);for(const s of e){const e=t.actions[s];o[s]=e({emit:n.emit,getState:n.getSnapshot})}}return n.actions=o,n}}}function l(t){const e=function(){let t=0,e=[];return{get records(){return e.slice()},record:function(s){e.push({...s,id:++t,state:structuredClone(s.state)})},replay:async function(t,s){const{from:n=0,to:o=1/0,scope:i}=null!=s?s:{},a=e.filter(t=>"emit"===t.type&&t.id>=n&&t.id<=o&&(!i||t.scope===i));for(const e of a){const s=t(e.intent,e.payload);s instanceof Promise&&await s}},clear:function(){e=[],t=0}}}();function s(){return"string"==typeof t.scope?t.scope:t.scope.name}return{timeline:e,wrap:function(){const n=t.emit.bind(t);t.emit=async(o,i)=>{e.record({type:"emit:start",intent:o,payload:i,scope:s(),state:t.getSnapshot(),timestamp:Date.now()});try{const t=n(o,i);t instanceof Promise&&await t}finally{e.record({type:"emit:end",intent:o,payload:i,scope:s(),state:t.getSnapshot(),timestamp:Date.now()})}}}}}function h(t){var e,o;let i=structuredClone(t);const a=()=>i,r=t=>{i={...i,...t}},c=s("backend");async function u(t,e){await h.emit(t,e,c)}const h=n(t=>{const e=new AbortController;return{scope:c,payload:t,signal:e.signal,get state(){return a()},setState(){throw new Error("setState is not allowed in backend runtime")},emit:u}});const d={state:a,reset:()=>{i=structuredClone(t)},emit:u,registerIntents:function(t){for(const e in t){const s=t[e];h.on(e,async(t,e)=>{var n;const o={get state(){return a()},signal:null!==(n=t.signal)&&void 0!==n?n:(new AbortController).signal,set:r,emit:u};await s(o)},c)}},onIntent:h.on,effect:h.effect};if("production"!==(null===(o=null===(e=null===globalThis||void 0===globalThis?void 0:globalThis.process)||void 0===e?void 0:e.env)||void 0===o?void 0:o.NODE_ENV)){const t=l(d);t.wrap(d),d.devtools=t}return d}function d(t,e,n){var a,r;const c=a=>{const r=i.useRef(null);r.current||(r.current=t.create("string"==typeof n?s(n):n));const c=r.current,u=i.useSyncExternalStore(c.subscribe,c.getSnapshot,c.getSnapshot),l=i.useCallback((t,e)=>c.emit(t,e),[c]),h=i.useMemo(()=>({state:u,actions:c.actions,emit:l}),[u,l,c]);return o(e,{...a,...h})};return c.displayName=`withLogic(${null!==(r=null!==(a=e.displayName)&&void 0!==a?a:e.name)&&void 0!==r?r:"View"})`,c}function m(t,e){const n=i.useRef(null);n.current||(n.current=t.create("string"==typeof e?s(e):e));const o=n.current,a=i.useSyncExternalStore(o.subscribe,o.getSnapshot,o.getSnapshot),r=i.useCallback((t,e)=>o.emit(t,e),[o]),c=o.actions;return i.useMemo(()=>({state:a,actions:c,emit:r}),[a,c,r])}export{r as LogicRuntime,l as attachDevtools,h as createBackendRuntime,u as createLogic,c as effect,m as useLogic,d as withLogic};
@@ -6,18 +6,23 @@ export type LogicFactory<S extends object, C extends ComputedDef<S>, A extends L
6
6
  name?: string;
7
7
  create(scope?: Scope): LogicRuntime<S, C, A>;
8
8
  };
9
- export declare function createLogic<S extends object, C extends ComputedDef<S> = {}, A extends LogicActions = {}>(config: {
9
+ export type ExtractLogicTypes<T> = T extends LogicFactory<infer S, infer C, infer A> ? {
10
+ state: Readonly<S & InferComputed<C>>;
11
+ actions: A;
12
+ emit: (intent: string, payload?: any) => Promise<void>;
13
+ } : never;
14
+ export declare function createLogic<S extends object, C extends ComputedDef<S>, ActionsDef extends Record<string, (context: {
15
+ emit: LogicRuntime<S, C, any>["emit"];
16
+ getState: () => Readonly<S & InferComputed<C>>;
17
+ }) => (...args: any[]) => any>>(config: {
10
18
  name?: string;
11
19
  state: S;
12
- computed?: C;
20
+ computed: C;
13
21
  intents?: (bus: {
14
- on: LogicRuntime<S, C, A>["onIntent"];
22
+ on: LogicRuntime<S, C, any>["onIntent"];
15
23
  effect: (type: string, eff: EffectDef) => void;
16
24
  }) => void;
17
- actions?: {
18
- [K in keyof A]: (context: {
19
- emit: LogicRuntime<S, C, A>["emit"];
20
- getState: () => Readonly<S & InferComputed<C>>;
21
- }) => A[K];
22
- };
23
- }): LogicFactory<S, C, A>;
25
+ actions: ActionsDef;
26
+ }): LogicFactory<S, C, {
27
+ [K in keyof ActionsDef]: ReturnType<ActionsDef[K]>;
28
+ }>;
@@ -1,6 +1,8 @@
1
- import { LogicRuntime, InferComputed } from "../core/runtime";
2
- export declare function useLogic<S extends object, C extends Record<string, any>, A extends Record<string, any>>(runtime: LogicRuntime<S, C, A>): {
3
- state: Readonly<S & InferComputed<C>>;
4
- computed: Readonly<InferComputed<C>>;
1
+ import { Scope } from "intentx-core-z";
2
+ import type { LogicActions, LogicFactory } from "../logic/createLogic";
3
+ import type { ComputedDef } from "../core/runtime";
4
+ export declare function useLogic<S extends object, C extends ComputedDef<S>, A extends LogicActions>(logic: LogicFactory<S, C, A>, scope?: Scope | string): {
5
+ state: Readonly<S & import("../core/runtime").InferComputed<C>>;
5
6
  actions: A;
7
+ emit: (intent: string, payload?: any) => Promise<void>;
6
8
  };
@@ -1,11 +1,12 @@
1
1
  import * as React from "react";
2
2
  import { Scope } from "intentx-core-z";
3
- import type { LogicActions, LogicFactory } from "../logic/createLogic";
3
+ import type { ExtractLogicTypes, LogicActions, LogicFactory } from "../logic/createLogic";
4
4
  import type { ComputedDef, InferComputed } from "../core/runtime";
5
5
  type InjectedProps<S extends object, C extends ComputedDef<S>, A extends LogicActions> = {
6
6
  state: Readonly<S & InferComputed<C>>;
7
7
  actions: A;
8
8
  emit: (intent: string, payload?: any) => Promise<void>;
9
9
  };
10
+ export type LogicViewProps<T extends LogicFactory<any, any, any>> = ExtractLogicTypes<T>;
10
11
  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?: Scope | string): React.FC<Omit<P, keyof InjectedProps<S, C, A>>>;
11
12
  export {};
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "logic-runtime-react-z",
3
- "version": "3.1.1",
4
- "description": "Intent-first business logic runtime. Headless and backend-friendly. Deterministic state, computed graph, and orchestrated async effects. React is just a view layer.",
3
+ "version": "3.1.3",
4
+ "description": "Intent-first business logic runtime. Deterministic, headless, and framework-agnostic.",
5
5
  "license": "MIT",
6
6
  "author": "Delpi.Kye",
7
7
  "sideEffects": false,
8
+
8
9
  "main": "build/index.cjs.js",
9
10
  "module": "build/index.esm.js",
10
11
  "types": "build/index.d.ts",
@@ -13,27 +14,19 @@
13
14
  "types": "./build/index.d.ts",
14
15
  "import": "./build/index.esm.js",
15
16
  "require": "./build/index.cjs.js"
16
- },
17
- "./react": {
18
- "types": "./build/react/index.d.ts",
19
- "import": "./build/react/index.esm.js",
20
- "require": "./build/react/index.cjs.js"
21
- },
22
- "./devtools": {
23
- "types": "./build/devtools/index.d.ts",
24
- "import": "./build/devtools/index.esm.js",
25
- "require": "./build/devtools/index.cjs.js"
26
17
  }
27
18
  },
28
19
  "files": [
29
20
  "build"
30
21
  ],
22
+
31
23
  "scripts": {
32
24
  "clean": "rimraf build",
33
25
  "build": "rollup -c",
34
26
  "cb": "npm run clean && npm run build",
35
27
  "prepublishOnly": "npm run cb"
36
28
  },
29
+
37
30
  "repository": {
38
31
  "type": "git",
39
32
  "url": "https://github.com/delpikye-v/logic-runtime-react.git"
@@ -42,6 +35,7 @@
42
35
  "bugs": {
43
36
  "url": "https://github.com/delpikye-v/logic-runtime-react/issues"
44
37
  },
38
+
45
39
  "keywords": [
46
40
  "intent-first",
47
41
  "intent-runtime",
@@ -60,6 +54,7 @@
60
54
  "takeLatest",
61
55
  "state-management"
62
56
  ],
57
+
63
58
  "peerDependencies": {
64
59
  "react": ">=18.0.0",
65
60
  "react-dom": ">=18.0.0"
@@ -1,21 +0,0 @@
1
- import { Scope } from "intentx-core-z";
2
- import type { ComputedDef, InferComputed } from "../core/runtime";
3
- import { LogicRuntime } from "../core/runtime";
4
- import type { LogicFactory } from "../logic/createLogic";
5
- type AnyLogicFactory = LogicFactory<any, ComputedDef<any>, any>;
6
- type LogicMap = Record<string, AnyLogicFactory>;
7
- type InferRuntime<L> = L extends LogicFactory<infer S, infer C, infer A> ? LogicRuntime<S, C, A> : never;
8
- type InferState<M extends LogicMap> = {
9
- [K in keyof M]: InferRuntime<M[K]> extends LogicRuntime<infer S, infer C, any> ? Readonly<S & InferComputed<C>> : never;
10
- };
11
- type InferActions<M extends LogicMap> = {
12
- [K in keyof M]: InferRuntime<M[K]> extends LogicRuntime<any, any, infer A> ? A : never;
13
- };
14
- export declare function composeLogic<M extends LogicMap>(map: M): {
15
- scope: Scope;
16
- runtimes: { [K in keyof M]: InferRuntime<M[K]>; };
17
- emit: <P = any>(type: string, payload?: P) => Promise<void>;
18
- actions: InferActions<M>;
19
- getState: () => InferState<M>;
20
- };
21
- export {};
@@ -1,4 +0,0 @@
1
- import { LogicFactory, LogicActions } from "../logic/createLogic";
2
- import { LogicRuntime, ComputedDef } from "../core/runtime";
3
- export declare function useActions<A extends LogicActions>(runtime: LogicRuntime<any, any, A>): A;
4
- export declare function useActions<S extends object, C extends ComputedDef<S>, A extends LogicActions>(logic: LogicFactory<S, C, A>): A;
@@ -1,6 +0,0 @@
1
- import { LogicFactory } from "../logic/createLogic";
2
- import { LogicRuntime, ComputedDef, InferComputed } from "../core/runtime";
3
- export declare function useComputed<S extends object, C extends ComputedDef<S>>(runtime: LogicRuntime<S, C, any>): Readonly<InferComputed<C>>;
4
- export declare function useComputed<S extends object, C extends ComputedDef<S>, R>(runtime: LogicRuntime<S, C, any>, selector: (computed: Readonly<InferComputed<C>>) => R): R;
5
- export declare function useComputed<S extends object, C extends ComputedDef<S>, A extends Record<string, any>>(logic: LogicFactory<S, C, A>): Readonly<InferComputed<C>>;
6
- export declare function useComputed<S extends object, C extends ComputedDef<S>, A extends Record<string, any>, R>(logic: LogicFactory<S, C, A>, selector: (computed: Readonly<InferComputed<C>>) => R): R;
@@ -1,2 +0,0 @@
1
- import { LogicRuntime, InferComputed } from "../core/runtime";
2
- export declare function useLogicSelector<S extends object, C extends Record<string, any>, R>(runtime: LogicRuntime<S, C, any>, selector: (state: Readonly<S & InferComputed<C>>) => R): R;
@@ -1,5 +0,0 @@
1
- import type { ComputedDef, InferComputed } from "../core/runtime";
2
- import { LogicRuntime } from "../core/runtime";
3
- import type { LogicFactory } from "../logic/createLogic";
4
- export declare function useRuntime<S extends object, C extends ComputedDef<S>, A extends Record<string, any>>(runtime: LogicRuntime<S, C, A>): Readonly<S & InferComputed<C>>;
5
- export declare function useRuntime<S extends object, C extends ComputedDef<S>, A extends Record<string, any>>(logic: LogicFactory<S, C, A>): Readonly<S & InferComputed<C>>;