logic-runtime-react-z 3.0.0 → 3.1.1

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
@@ -1,25 +1,25 @@
1
- # ⚙️ logic-runtime-react-z
1
+ # 🧩 logic-runtime-react-z
2
2
 
3
3
  [![NPM](https://img.shields.io/npm/v/logic-runtime-react-z.svg)](https://www.npmjs.com/package/logic-runtime-react-z) ![Downloads](https://img.shields.io/npm/dt/logic-runtime-react-z.svg)
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
8
 
8
- **Intent-first business logic runtime**: React is a view - Logic lives elsewhere.
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
11
 
10
- > A headless, deterministic, intent-driven runtime for frontend & backend logic.
11
- > React components stay pure. Business logic is fully testable and replayable.
12
+ > **Intent is the only entry point.**
13
+ > **React is optional. createLogic is the product. Everything else is an adapter.**
12
14
 
13
15
  ---
14
16
 
15
- ## ✨ logic-runtime-react-z?
17
+ ## ✨ Why logic-runtime-react-z?
16
18
 
17
- > **Intent is the only entry point.**
18
-
19
- - No React hooks in views
19
+ - No business logic in React components
20
20
  - Intent is the *only* entry point
21
21
  - Predictable async flows
22
- - Computed graph with caching
22
+ - Reactive computed graph with caching
23
23
  - Headless & backend-friendly
24
24
  - Deterministic testing & devtools replay
25
25
 
@@ -38,7 +38,7 @@ UI / HTTP / Queue / Cron
38
38
 
39
39
  mutate state
40
40
 
41
- computed / subscribers
41
+ computed (derived state) / subscribers
42
42
  ```
43
43
 
44
44
  Think **events → behavior → state → derived state**.
@@ -76,23 +76,46 @@ const counterLogic = createLogic({
76
76
  },
77
77
  })
78
78
 
79
- const runtime = counterLogic.create()
79
+ async function main() {
80
+ const runtime = counterLogic.create()
80
81
 
81
- await runtime.emit("inc")
82
- await runtime.emit("add", 5)
82
+ await runtime.emit("inc")
83
+ await runtime.emit("add", 5)
84
+
85
+ console.log(runtime.state.count)
86
+ }
83
87
 
84
- console.log(runtime.state.count) // 6
88
+ main()
85
89
  ```
86
90
 
87
- ✔ No UI
88
- ✔ Fully testable
91
+ ✔ No UI
92
+ ✔ Fully testable
89
93
  ✔ Deterministic
90
94
 
95
+ > 💡 This is the core usage.
96
+ > createLogic() + runtime.emit() already gives you state, computed and effects.
97
+ > React integration is just a convenience layer on top of this runtime.
98
+
99
+ ---
100
+
101
+ ## 🧠 Computed State
102
+
103
+ ```ts
104
+ computed: {
105
+ double: ({ state }) => state.count * 2,
106
+ triple: ({ state }) => state.count * 3,
107
+ }
108
+ ```
109
+
110
+ - `state` inside `computed` is **reactive**.
111
+ - Reading `state.count` automatically tracks dependencies.
112
+ - Computed values are cached and only re-evaluated when tracked dependencies change.
113
+
91
114
  ---
92
115
 
93
- ## ⚛️ React Integration (Type Inference, No Hooks)
116
+ ## ⚛️ React Integration (No Hooks Required)
94
117
 
95
- ### Define Logic
118
+ ### Define Logic (Framework-Agnostic)
96
119
 
97
120
  ```ts
98
121
  // counter.logic.ts
@@ -108,7 +131,7 @@ export const counterLogic = createLogic({
108
131
 
109
132
  computed: {
110
133
  double: ({ state }) => state.count * 2,
111
- tripple: ({ state }) => state.count * 3,
134
+ triple: ({ state }) => state.count * 3,
112
135
  },
113
136
 
114
137
  intents: bus => {
@@ -137,10 +160,11 @@ export const counterLogic = createLogic({
137
160
  })
138
161
  })
139
162
 
163
+ // effects = side-effects only (no state mutation)
140
164
  bus.effect(
141
165
  "inc-async",
142
166
  effect(async ({ payload }) => {
143
- console.log("effect run, payload =", payload)
167
+ console.log("effect run:", payload)
144
168
  }).takeLatest()
145
169
  )
146
170
  },
@@ -159,26 +183,27 @@ export const counterLogic = createLogic({
159
183
  },
160
184
  },
161
185
  })
186
+
162
187
  ```
163
188
 
164
189
  ---
165
190
 
166
- ### Pure React View (No Types Needed)
191
+ ### Pure React View (Dumb View)
167
192
 
168
193
  ```tsx
169
194
  import React from "react"
170
195
  import { withLogic } from "logic-runtime-react-z"
171
196
  import { counterLogic } from "./counter.logic"
172
197
 
173
- function CounterView(props: any) {
198
+ function CounterView(props) {
174
199
  const { state, actions, emit } = props
175
200
 
176
201
  return (
177
202
  <div style={{ padding: 12 }}>
178
- <div>Count: {state.tripple}</div>
203
+ <div>Triple Count: {state.triple}</div>
179
204
 
180
- <button onClick={actions.inc}>+1 (action)</button>
181
- <button onClick={() => actions.add(10)}>+10 (action)</button>
205
+ <button onClick={actions.inc}>+1</button>
206
+ <button onClick={() => actions.add(10)}>+10</button>
182
207
 
183
208
  <button
184
209
  disabled={state.loading}
@@ -190,74 +215,93 @@ function CounterView(props: any) {
190
215
  <hr />
191
216
 
192
217
  <button onClick={() => emit("inc")}>
193
- +1 (emit directly)
218
+ emit("inc")
194
219
  </button>
195
220
  </div>
196
221
  )
197
222
  }
198
223
 
199
- export const CounterPage =
200
- withLogic(counterLogic, CounterView)
224
+ export const CounterPage = withLogic(counterLogic, CounterView)
225
+
201
226
  ```
202
227
 
203
- ✔ Props inferred automatically
204
- ✔ No generics
205
- ✔ No interfaces
206
- ✔ View stays dumb
228
+ ✔ Props are inferred when using withLogic, no manual generics required.
207
229
 
208
230
  ---
209
231
 
210
- ## 🧪 Backend Runtime Example
232
+ ## 🧪 Backend Usage (Same Runtime)
211
233
 
212
234
  ```ts
213
- import { createBackendRuntime } from "logic-runtime-react-z"
235
+ import { createLogic } from "logic-runtime-react-z"
214
236
 
215
- const runtime = createBackendRuntime({
216
- user: null,
217
- loading: false,
218
- })
237
+ const authLogic = createLogic({
238
+ state: {
239
+ user: null,
240
+ loading: false,
241
+ },
219
242
 
220
- runtime.registerIntents({
221
- async login({ set }) {
222
- set({ loading: true })
223
- await new Promise(r => setTimeout(r, 500))
224
- set({
225
- user: { name: "Alice" },
226
- loading: false,
243
+ intents: bus => {
244
+ bus.on("login", async ({ setState }) => {
245
+ setState(s => {
246
+ s.loading = true
247
+ })
248
+
249
+ await new Promise(r => setTimeout(r, 500))
250
+
251
+ setState(s => {
252
+ s.user = { name: "Alice" }
253
+ s.loading = false
254
+ })
227
255
  })
228
- },
229
256
 
230
- logout({ set }) {
231
- set({ user: null })
257
+ bus.on("logout", ({ setState }) => {
258
+ setState(s => {
259
+ s.user = null
260
+ })
261
+ })
232
262
  },
233
263
  })
234
264
 
235
- await runtime.emit("login")
236
- await runtime.emit("logout")
265
+ async function run() {
266
+ const runtime = authLogic.create()
237
267
 
238
- // 👇 backend devtools
239
- const devtools = runtime.devtools
240
- console.log(devtools.timeline.records)
268
+ await runtime.emit("login")
269
+ await runtime.emit("logout")
241
270
 
242
- // relay
243
- // await devtools.timeline.replay(runtime.emit, {
244
- // scope: "backend"
245
- })
271
+ console.log(runtime.getSnapshot())
272
+ }
273
+
274
+ run()
246
275
  ```
247
276
 
248
- ✔ Same intent model
277
+ ✔ Same runtime, same behavior, no React involved.
249
278
  ✔ No React
250
279
  ✔ Replayable
251
- ✔ Devtools is backend-first.
252
280
 
253
281
  ---
254
282
 
255
- ## 🪝 Hooks API (Optional)
283
+ ## 🪝 Hooks Examples (Optional, Thin Adapters)
256
284
 
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
+
288
+ #### useRuntime – full snapshot
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
+ ```
297
+
298
+ ✔ Subscribes to full snapshot
299
+ ✔ Includes state + computed
300
+ ✔ Read-only
301
+
302
+ #### useActions – actions only (no re-render)
257
303
  ```ts
258
- // useActions
259
304
  import { useActions } from "logic-runtime-react-z"
260
- import { counterLogic } from "./counter.logic"
261
305
 
262
306
  function Buttons() {
263
307
  const actions = useActions(counterLogic)
@@ -269,36 +313,72 @@ function Buttons() {
269
313
  </>
270
314
  )
271
315
  }
316
+ ```
272
317
 
273
- // useSelector
274
- import { useSelector } from "logic-runtime-react-z"
275
- import { counterLogic } from "./counter.logic"
276
318
 
277
- function DoubleValue() {
278
- const double = useSelector(
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"
326
+
327
+ function Stats() {
328
+ const { double, triple } = useComputed(counterLogic)
329
+
330
+ return (
331
+ <>
332
+ <div>Double: {double}</div>
333
+ <div>Triple: {triple}</div>
334
+ </>
335
+ )
336
+ }
337
+
338
+ function DoubleOnly() {
339
+ const double = useComputed(counterLogic, c => c.double)
340
+ return <div>{double}</div>
341
+ }
342
+ ```
343
+
344
+ ✔ Only derived data
345
+ ✔ Cached & reactive
346
+ ✔ No state mutation possible
347
+
348
+ #### useComputed with selector (recommended)
349
+ ```ts
350
+ function DoubleOnly() {
351
+ const double = useComputed(
279
352
  counterLogic,
280
- s => s.double
353
+ c => c.double
281
354
  )
282
355
 
283
356
  return <div>Double: {double}</div>
284
357
  }
358
+ ```
285
359
 
286
- // useRuntime
287
- import { useRuntime } from "logic-runtime-react-z"
288
- import { counterLogic } from "./counter.logic"
360
+ Component re-renders only when double changes
361
+ No extra dependencies
362
+ Type-safe selector
289
363
 
290
- function DebugPanel() {
291
- const runtime = useRuntime(counterLogic)
364
+ #### useLogicSelector – State selector (Redux-like)
365
+ ```ts
366
+ import { useLogicSelector } from "logic-runtime-react-z"
292
367
 
293
- return (
294
- <button onClick={() => runtime.emit("inc")}>
295
- Emit directly
296
- </button>
368
+ function CountLabel() {
369
+ const count = useLogicSelector(
370
+ counterLogic.create(),
371
+ state => state.count
297
372
  )
298
- }
299
373
 
374
+ return <span>{count}</span>
375
+ }
300
376
  ```
301
377
 
378
+ ✔ Memoized selector
379
+ ✔ Fine-grained subscriptions
380
+ ✔ Familiar mental model
381
+
302
382
  ---
303
383
 
304
384
  ## 🧱 Composing Multiple Logic Modules
@@ -308,20 +388,16 @@ import { composeLogic } from "logic-runtime-react-z"
308
388
  import { userLogic } from "./user.logic"
309
389
  import { cartLogic } from "./cart.logic"
310
390
 
311
- export const appLogic = composeLogic({
391
+ const app = composeLogic({
312
392
  user: userLogic,
313
393
  cart: cartLogic,
314
394
  })
315
395
 
396
+ await app.emit("login")
316
397
 
317
- // usage
318
- const runtime = appLogic.create()
319
-
320
- await runtime.emit("user:login", credentials)
321
-
322
- const snapshot = runtime.getSnapshot()
323
- snapshot.user // user state
324
- snapshot.cart // cart state
398
+ const state = app.getState()
399
+ state.user
400
+ state.cart
325
401
 
326
402
  ```
327
403
 
@@ -346,77 +422,49 @@ const logic = createLogic({
346
422
  },
347
423
  })
348
424
 
349
- const runtime = logic.create()
425
+ const runtime = counterLogic.create()
350
426
 
351
427
  await runtime.emit("set", 4)
352
-
353
- expect(runtime.state.squared).toBe(16)
354
- ```
355
-
356
- ---
357
-
358
- ## 🚫 Anti-patterns (What NOT to do)
359
-
360
- ### ❌ Business logic in React
361
-
362
- ```tsx
363
- useEffect(() => {
364
- fetchData()
365
- }, [])
428
+ expect(runtime.computed.squared).toBe(16)
366
429
  ```
367
430
 
368
- Correct
369
-
370
- ```ts
371
- emit("data:fetch")
372
- ```
431
+ Computed values are tested like plain data
373
432
 
374
433
  ---
375
434
 
376
- ### Mutating state directly
435
+ ## 🔍 Comparison: Redux vs Zustand
377
436
 
378
- ```ts
379
- runtime.state.user.name = "admin"
380
- ```
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 | ✅ | ❌ | ❌ |
381
446
 
382
- ✅ Correct
383
447
 
384
- ```ts
385
- emit("update:user:name", "admin")
386
- ```
448
+ ##### ⚠️ via selectors, not a true dependency graph
387
449
 
388
450
  ---
389
451
 
390
- ### Generic Redux-style intents
391
-
392
- ```ts
393
- emit("SET_STATE", { loading: true })
394
- ```
395
-
396
- ✅ Correct
452
+ ## 🧠 One-liner Takeaway
397
453
 
398
- ```ts
399
- emit("login:start")
400
- emit("login:success", user)
401
- emit("login:failed", error)
402
- ```
454
+ - Redux & Zustand manage **state**
455
+ - logic-runtime-react-z orchestrates **logic**
403
456
 
404
457
  ---
405
458
 
406
- ## 🧩 When to Use This
407
-
408
- - Complex async flows
409
- - Shared logic across UI / backend
410
- - Need deterministic tests
411
- - Want to remove logic from React
412
-
413
- ## 🚫 When NOT to Use
459
+ ## 🧬 Determinism Guarantee
414
460
 
415
- - Simple local UI state
416
- - Throwaway components
461
+ - Intents are processed sequentially
462
+ - State mutations are isolated
463
+ - Async flows are predictable
464
+ - Same inputs → same outputs
417
465
 
418
466
  ---
419
467
 
420
- ## 📜 License
468
+ ## License
421
469
 
422
- MIT / Delpi
470
+ MIT
@@ -1,26 +1,39 @@
1
- import { Atom } from "chrono-state-z/build/core/atom";
1
+ import type { Atom } from "chrono-state-z/build/core/atom";
2
2
  import { Scope } from "intentx-core-z";
3
3
  import { EffectDef } from "./effect";
4
4
  export type AtomAccessor<T> = Atom<T>;
5
- export declare class LogicRuntime<S extends object, A extends Record<string, any> = {}> {
5
+ export type ComputedDef<S> = Record<string, (context: {
6
+ state: Readonly<S>;
7
+ }) => any>;
8
+ export type InferComputed<C> = {
9
+ [K in keyof C]: C[K] extends (...args: any[]) => infer R ? R : never;
10
+ };
11
+ export declare class LogicRuntime<S extends object, C extends ComputedDef<S>, A extends Record<string, any>> {
6
12
  readonly scope: Scope;
7
- private atoms;
13
+ private stateAtoms;
14
+ private computedAtoms;
8
15
  private subs;
9
16
  private bus;
10
17
  private snapshotCache;
11
18
  private dirty;
12
19
  private isComputing;
20
+ private computedKeys;
13
21
  actions: A;
14
22
  constructor(initial: S, scope?: Scope);
15
- private createAtoms;
23
+ private createStateAtoms;
16
24
  private buildSnapshot;
17
25
  private markDirty;
18
- getSnapshot: () => Readonly<S>;
26
+ private createReactiveState;
27
+ getSnapshot: () => Readonly<S & InferComputed<C>>;
19
28
  subscribe: (fn: () => void) => () => boolean;
29
+ get state(): Readonly<S & InferComputed<C>>;
30
+ get computed(): Readonly<InferComputed<C>>;
31
+ getComputedKey<K extends keyof InferComputed<C>>(key: K): InferComputed<C>[K];
32
+ getComputed(snapshot: Readonly<S & InferComputed<C>>): Readonly<InferComputed<C>>;
20
33
  private setStateInternal;
21
34
  onIntent: <P = any>(type: string, handler: (context: {
22
35
  payload: P;
23
- state: () => Readonly<S>;
36
+ state: () => Readonly<S & InferComputed<C>>;
24
37
  scope: Scope;
25
38
  signal: AbortSignal;
26
39
  setState(fn: (draft: S) => void): void;
@@ -28,5 +41,5 @@ export declare class LogicRuntime<S extends object, A extends Record<string, any
28
41
  }) => any) => void;
29
42
  useEffect(type: string, eff: EffectDef): void;
30
43
  emit: <P = any>(type: string, payload?: P) => Promise<void>;
31
- attachComputed<K extends string, T>(key: K, compute: () => T): void;
44
+ attachComputed<K extends keyof C & string>(key: K, compute: C[K]): void;
32
45
  }
@@ -1 +1 @@
1
- "use strict";var t=require("chrono-state-z"),e=require("intentx-core-z"),s=require("react"),n=require("react/jsx-runtime");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 i=o(s);class r{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 a{constructor(t,s=e.createScope("logic")){this.subs=new Set,this.bus=new r,this.dirty=!0,this.isComputing=!1,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.atoms=this.createAtoms(t);for(const t in this.atoms)this.atoms[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)}}())}createAtoms(e){const s={};for(const n in e)s[n]=t.atom(e[n]);return s}buildSnapshot(){const t={};for(const e in this.atoms)t[e]=this.atoms[e]();return t}setStateInternal(t){this.isComputing&&console.warn("[logic-runtime] Side-effect detected: setState() called inside computed()");const e=this.buildSnapshot();t(e);for(const t in e)e[t]!==this.atoms[t]()&&this.atoms[t].set(e[t])}useEffect(t,e){this.bus.effect(t,e)}attachComputed(e,s){const n=t.atom(void 0);t.effect(()=>{this.isComputing=!0;const t=s();n.set(t),this.isComputing=!1}),this.atoms[e]=n,n.subscribe(this.markDirty)}}function c(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 u(t){if(t instanceof a)return s.useSyncExternalStore(t.subscribe,t.getSnapshot,t.getSnapshot);const n=t.create(e.createScope("react"));return s.useSyncExternalStore(n.subscribe,n.getSnapshot,n.getSnapshot)}exports.LogicRuntime=a,exports.attachDevtools=c,exports.composeLogic=function(t){return{create(){const e={};for(const s in t){const n=s;e[n]=t[n].create()}return{async emit(t,s){for(const n of Object.values(e))await n.emit(t,s)},subscribe(t){const s=Object.values(e).map(e=>e.subscribe(t));return()=>s.forEach(t=>t())},getSnapshot(){const t={};for(const s in e)t[s]=e[s].getSnapshot();return t}}}}},exports.createBackendRuntime=function(t){var s,n;let o=structuredClone(t);const i=()=>structuredClone(o),r=t=>{o=Object.assign(i(),t)},a=e.createScope("backend");async function u(t,e){await l.emit(t,e,a)}const l=e.createIntentBus(t=>({payload:t,signal:(new AbortController).signal,state:i(),setState(){throw new Error("setState is not allowed in backend runtime")},emit:u}));function h(t,e,s){return{intent:t,payload:e,state:s.state,signal:s.signal,set:r,emit:u}}function f(t){return{state:t.state,signal:t.signal,set:t.set,emit:t.emit}}const d={state:()=>o,reset:()=>{o=structuredClone(t)},emit:u,registerIntents:function(t){for(const e in t){const s=t[e];l.on(e,async(t,n)=>{const o=f(h(e,n,t));await s(o)},a)}},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=c(d);t.wrap(d),d.devtools=t}return d},exports.createLogic=function(t){return{name:t.name,create(e){var s;const n=new a(structuredClone(t.state),e);if(t.computed)for(const e in t.computed)n.attachComputed(e,()=>t.computed[e]({state:n.getSnapshot()}));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.createSelector=function(t,e=Object.is){let s,n=null;return o=>{if(null!==n){const i=t(o);return e(s,i)?s:(s=i,n=o,i)}return n=o,s=t(o),s}},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"actions"in t?t.actions:u(t).actions},exports.useRuntime=u,exports.useSelector=function(t,e){return s.useSyncExternalStore(t.subscribe,()=>e(t.getSnapshot()),()=>e(t.getSnapshot()))},exports.withLogic=function(t,s,o){var r,a;const c=r=>{const a=i.useRef(null);a.current||(a.current=t.create("string"==typeof o?e.createScope(o):o));const c=a.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 n.jsx(s,{...r,...h})};return c.displayName=`withLogic(${null!==(a=null!==(r=s.displayName)&&void 0!==r?r:s.name)&&void 0!==a?a:"View"})`,c};
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};
package/build/index.d.ts CHANGED
@@ -1,12 +1,16 @@
1
+ export { createSelector } from "chrono-state-z";
1
2
  export { LogicRuntime } from "./core/runtime";
3
+ export type { AtomAccessor } from "./core/runtime";
2
4
  export { effect } from "./core/effect";
5
+ export type { IntentMiddleware, IntentNext, } from "./core/middleware";
3
6
  export { createLogic } from "./logic/createLogic";
4
- export type { LogicFactory } from "./logic/createLogic";
7
+ export type { LogicFactory, LogicActions, } from "./logic/createLogic";
5
8
  export { composeLogic } from "./logic/composeLogic";
6
9
  export { createBackendRuntime } from "./logic/createBackendRuntime";
7
- export { createSelector } from "./react/selector";
10
+ export { withLogic } from "./react/withLogic";
8
11
  export { useActions } from "./react/useActions";
12
+ export { useComputed } from "./react/useComputed";
13
+ export { useLogic } from "./react/useLogic";
14
+ export { useLogicSelector } from "./react/useLogicSelector";
9
15
  export { useRuntime } from "./react/useRuntime";
10
- export { useSelector } from "./react/useSelector";
11
- export { withLogic } from "./react/withLogic";
12
16
  export * from "./devtools";
@@ -1 +1 @@
1
- import{atom as t,effect as e}from"chrono-state-z";import{createScope as s,createIntentBus as n}from"intentx-core-z";import*as i from"react";import{useSyncExternalStore as o}from"react";import{jsx as a}from"react/jsx-runtime";class r{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 i=this.handlers[t];i&&i.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 i=null!==(s=this.handlers[t])&&void 0!==s?s:[],o=null!==(n=this.effects[t])&&void 0!==n?n:[],a=(r=this.middlewares,c=async t=>{t.effects=o;for(const e of i)await e(t)},r.reduceRight((t,e)=>e(t),c));var r,c;await a(e)}}class c{constructor(t,e=s("logic")){this.subs=new Set,this.bus=new r,this.dirty=!0,this.isComputing=!1,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.atoms=this.createAtoms(t);for(const t in this.atoms)this.atoms[t].subscribe(this.markDirty);this.bus.use(function(){const t=new Map,e=new Map;return s=>async n=>{var i;const o=n.effects;if(!(null==o?void 0:o.length))return s(n);for(const s of o){const o=s.handler.toString();if("takeLatest"===s.strategy){null===(i=t.get(o))||void 0===i||i.abort();const e=new AbortController;t.set(o,e),await s.handler({...n,signal:e.signal})}else"debounce"===s.strategy?(clearTimeout(e.get(o)),e.set(o,setTimeout(()=>s.handler(n),s.wait))):await s.handler(n)}await s(n)}}())}createAtoms(e){const s={};for(const n in e)s[n]=t(e[n]);return s}buildSnapshot(){const t={};for(const e in this.atoms)t[e]=this.atoms[e]();return t}setStateInternal(t){this.isComputing&&console.warn("[logic-runtime] Side-effect detected: setState() called inside computed()");const e=this.buildSnapshot();t(e);for(const t in e)e[t]!==this.atoms[t]()&&this.atoms[t].set(e[t])}useEffect(t,e){this.bus.effect(t,e)}attachComputed(s,n){const i=t(void 0);e(()=>{this.isComputing=!0;const t=n();i.set(t),this.isComputing=!1}),this.atoms[s]=i,i.subscribe(this.markDirty)}}function u(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 l(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]({state:n.getSnapshot()}));null===(s=t.intents)||void 0===s||s.call(t,{on:n.onIntent,effect:n.useEffect.bind(n)});const i={};if(t.actions)for(const e in t.actions)i[e]=t.actions[e]({emit:n.emit,getState:n.getSnapshot});return n.actions=i,n}}}function h(t){return{create(){const e={};for(const s in t){const n=s;e[n]=t[n].create()}return{async emit(t,s){for(const n of Object.values(e))await n.emit(t,s)},subscribe(t){const s=Object.values(e).map(e=>e.subscribe(t));return()=>s.forEach(t=>t())},getSnapshot(){const t={};for(const s in e)t[s]=e[s].getSnapshot();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:i=1/0,scope:o}=null!=s?s:{},a=e.filter(t=>"emit"===t.type&&t.id>=n&&t.id<=i&&(!o||t.scope===o));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(i,o)=>{e.record({type:"emit:start",intent:i,payload:o,scope:s(),state:t.getSnapshot(),timestamp:Date.now()});try{const t=n(i,o);t instanceof Promise&&await t}finally{e.record({type:"emit:end",intent:i,payload:o,scope:s(),state:t.getSnapshot(),timestamp:Date.now()})}}}}}function d(t){var e,i;let o=structuredClone(t);const a=()=>structuredClone(o),r=t=>{o=Object.assign(a(),t)},c=s("backend");async function u(t,e){await l.emit(t,e,c)}const l=n(t=>({payload:t,signal:(new AbortController).signal,state:a(),setState(){throw new Error("setState is not allowed in backend runtime")},emit:u}));function h(t,e,s){return{intent:t,payload:e,state:s.state,signal:s.signal,set:r,emit:u}}function d(t){return{state:t.state,signal:t.signal,set:t.set,emit:t.emit}}const m={state:()=>o,reset:()=>{o=structuredClone(t)},emit:u,registerIntents:function(t){for(const e in t){const s=t[e];l.on(e,async(t,n)=>{const i=d(h(e,n,t));await s(i)},c)}},onIntent:l.on,effect:l.effect};if("production"!==(null===(i=null===(e=null===globalThis||void 0===globalThis?void 0:globalThis.process)||void 0===e?void 0:e.env)||void 0===i?void 0:i.NODE_ENV)){const t=f(m);t.wrap(m),m.devtools=t}return m}function m(t,e=Object.is){let s,n=null;return i=>{if(null!==n){const o=t(i);return e(s,o)?s:(s=o,n=i,o)}return n=i,s=t(i),s}}function p(t){if(t instanceof c)return o(t.subscribe,t.getSnapshot,t.getSnapshot);const e=t.create(s("react"));return o(e.subscribe,e.getSnapshot,e.getSnapshot)}function g(t){if("actions"in t)return t.actions;return p(t).actions}function b(t,e){return o(t.subscribe,()=>e(t.getSnapshot()),()=>e(t.getSnapshot()))}function y(t,e,n){var o,r;const c=o=>{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 a(e,{...o,...h})};return c.displayName=`withLogic(${null!==(r=null!==(o=e.displayName)&&void 0!==o?o:e.name)&&void 0!==r?r:"View"})`,c}export{c as LogicRuntime,f as attachDevtools,h as composeLogic,d as createBackendRuntime,l as createLogic,m as createSelector,u as effect,g as useActions,p as useRuntime,b as useSelector,y as withLogic};
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,16 +1,21 @@
1
+ import { Scope } from "intentx-core-z";
2
+ import type { ComputedDef, InferComputed } from "../core/runtime";
1
3
  import { LogicRuntime } from "../core/runtime";
2
- type LogicFactory<S extends object> = {
3
- create(): LogicRuntime<S, any>;
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;
4
10
  };
5
- type LogicMap = Record<string, LogicFactory<any>>;
6
- type SnapshotOf<M extends LogicMap> = {
7
- [K in keyof M]: ReturnType<M[K]["create"]> extends LogicRuntime<infer S extends object, any> ? Readonly<S> : never;
11
+ type InferActions<M extends LogicMap> = {
12
+ [K in keyof M]: InferRuntime<M[K]> extends LogicRuntime<any, any, infer A> ? A : never;
8
13
  };
9
- export declare function composeLogic<M extends LogicMap>(logics: M): {
10
- create(): {
11
- emit(intent: string, payload?: any): Promise<void>;
12
- subscribe(fn: () => void): () => void;
13
- getSnapshot(): SnapshotOf<M>;
14
- };
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>;
15
20
  };
16
21
  export {};
@@ -8,10 +8,10 @@ export type BackendContext<S> = {
8
8
  export type BackendIntent<S> = (context: BackendContext<S>) => void | Promise<void>;
9
9
  export type BackendIntents<S> = Record<string, BackendIntent<S>>;
10
10
  export declare function createBackendRuntime<S extends object>(initial: S): {
11
- state: () => S;
11
+ state: () => Readonly<S>;
12
12
  reset: () => void;
13
13
  emit: (intent: string, payload?: any) => Promise<void>;
14
14
  registerIntents: (intents: BackendIntents<S>) => void;
15
- onIntent: (type: string, handler: import("intentx-core-z").IntentHandler<S>, scope?: Scope) => () => void;
16
- effect: (type: string, fx: import("intentx-core-z/build/intent/effect").IntentEffect<S>, scope?: Scope) => void;
15
+ onIntent: (type: string, h: import("intentx-core-z").IntentHandler<S>, scope?: Scope) => () => void;
16
+ effect: (type: string, fx: import("intentx-core-z/build/intent/types").IntentEffect<S>, scope?: Scope) => () => void;
17
17
  };
@@ -1,28 +1,23 @@
1
1
  import { Scope } from "intentx-core-z";
2
- import { LogicRuntime } from "../core/runtime";
2
+ import { LogicRuntime, ComputedDef, InferComputed } from "../core/runtime";
3
3
  import { EffectDef } from "../core/effect";
4
- type ComputedFactory<S> = (context: {
5
- state: Readonly<S>;
6
- }) => any;
7
- type ActionFactory<S extends object, Fn extends (...args: any[]) => any> = (context: {
8
- emit: LogicRuntime<S, any>["emit"];
9
- getState: () => Readonly<S>;
10
- }) => Fn;
11
4
  export type LogicActions = Record<string, (...args: any[]) => any>;
12
- export type LogicFactory<S extends object, A extends LogicActions> = {
5
+ export type LogicFactory<S extends object, C extends ComputedDef<S>, A extends LogicActions> = {
13
6
  name?: string;
14
- create(scope?: Scope): LogicRuntime<S, A>;
7
+ create(scope?: Scope): LogicRuntime<S, C, A>;
15
8
  };
16
- export declare function createLogic<S extends object, A extends LogicActions>(config: {
9
+ export declare function createLogic<S extends object, C extends ComputedDef<S> = {}, A extends LogicActions = {}>(config: {
17
10
  name?: string;
18
11
  state: S;
19
- computed?: Record<string, ComputedFactory<S>>;
12
+ computed?: C;
20
13
  intents?: (bus: {
21
- on: LogicRuntime<S>["onIntent"];
14
+ on: LogicRuntime<S, C, A>["onIntent"];
22
15
  effect: (type: string, eff: EffectDef) => void;
23
16
  }) => void;
24
17
  actions?: {
25
- [K in keyof A]: ActionFactory<S, A[K]>;
18
+ [K in keyof A]: (context: {
19
+ emit: LogicRuntime<S, C, A>["emit"];
20
+ getState: () => Readonly<S & InferComputed<C>>;
21
+ }) => A[K];
26
22
  };
27
- }): LogicFactory<S, A>;
28
- export {};
23
+ }): LogicFactory<S, C, A>;
@@ -1,5 +1,4 @@
1
- import { LogicActions, LogicFactory } from "../logic/createLogic";
2
- export declare function useActions<A extends object>(runtime: {
3
- actions: A;
4
- }): A;
5
- export declare function useActions<S extends object, A extends LogicActions>(logic: LogicFactory<S, A>): A;
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;
@@ -0,0 +1,6 @@
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;
@@ -0,0 +1,6 @@
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>>;
5
+ actions: A;
6
+ };
@@ -0,0 +1,2 @@
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,4 +1,5 @@
1
+ import type { ComputedDef, InferComputed } from "../core/runtime";
1
2
  import { LogicRuntime } from "../core/runtime";
2
- import { LogicFactory } from "../logic/createLogic";
3
- export declare function useRuntime<S extends object>(runtime: LogicRuntime<S>): Readonly<S>;
4
- export declare function useRuntime<S extends object, A extends Record<string, any>>(logic: LogicFactory<S, A>): Readonly<S>;
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>>;
@@ -1,10 +1,11 @@
1
1
  import * as React from "react";
2
2
  import { Scope } from "intentx-core-z";
3
- import { LogicActions, LogicFactory } from "../logic/createLogic";
4
- type InjectedProps<S extends object, A extends LogicActions> = {
5
- state: Readonly<S>;
3
+ import type { LogicActions, LogicFactory } from "../logic/createLogic";
4
+ import type { ComputedDef, InferComputed } from "../core/runtime";
5
+ type InjectedProps<S extends object, C extends ComputedDef<S>, A extends LogicActions> = {
6
+ state: Readonly<S & InferComputed<C>>;
6
7
  actions: A;
7
8
  emit: (intent: string, payload?: any) => Promise<void>;
8
9
  };
9
- export declare function withLogic<S extends object, A extends LogicActions, P extends object>(logic: LogicFactory<S, A>, View: React.ComponentType<P & InjectedProps<S, A>>, scope?: Scope | string): React.FC<Omit<P, keyof InjectedProps<S, A>>>;
10
+ 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>>>;
10
11
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "logic-runtime-react-z",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
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.",
5
5
  "license": "MIT",
6
6
  "author": "Delpi.Kye",
@@ -47,21 +47,17 @@
47
47
  "intent-runtime",
48
48
  "business-logic",
49
49
  "logic-runtime",
50
-
51
50
  "headless",
52
51
  "backend-friendly",
53
52
  "ui-agnostic",
54
-
55
53
  "react",
56
54
  "react-architecture",
57
55
  "no-hooks",
58
56
  "external-store",
59
-
60
57
  "computed-state",
61
58
  "derived-state",
62
59
  "async-effects",
63
60
  "takeLatest",
64
-
65
61
  "state-management"
66
62
  ],
67
63
  "peerDependencies": {
@@ -84,7 +80,7 @@
84
80
  "typescript": "^5.3.3"
85
81
  },
86
82
  "dependencies": {
87
- "chrono-state-z": "^2.0.1",
88
- "intentx-core-z": "^1.0.0"
83
+ "chrono-state-z": "^2.1.0",
84
+ "intentx-core-z": "^2.1.0"
89
85
  }
90
86
  }
@@ -1 +0,0 @@
1
- export declare function createSelector<T, R>(select: (state: T) => R, isEqual?: (value1: any, value2: any) => boolean): (state: T) => R;
@@ -1,2 +0,0 @@
1
- import { LogicRuntime } from "../core/runtime";
2
- export declare function useSelector<S extends object, R>(runtime: LogicRuntime<S>, selector: (state: Readonly<S>) => R): R;