logic-runtime-react-z 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,19 @@
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.**
12
13
 
13
14
  ---
14
15
 
15
- ## ✨ logic-runtime-react-z?
16
-
17
- > **Intent is the only entry point.**
16
+ ## ✨ Why logic-runtime-react-z?
18
17
 
19
18
  - No React hooks in views
20
19
  - Intent is the *only* entry point
@@ -90,7 +89,7 @@ console.log(runtime.state.count) // 6
90
89
 
91
90
  ---
92
91
 
93
- ## ⚛️ React Integration (Type Inference, No Hooks)
92
+ ## ⚛️ React Integration (No Hooks)
94
93
 
95
94
  ### Define Logic
96
95
 
@@ -108,7 +107,7 @@ export const counterLogic = createLogic({
108
107
 
109
108
  computed: {
110
109
  double: ({ state }) => state.count * 2,
111
- tripple: ({ state }) => state.count * 3,
110
+ triple: ({ state }) => state.count * 3,
112
111
  },
113
112
 
114
113
  intents: bus => {
@@ -175,7 +174,7 @@ function CounterView(props: any) {
175
174
 
176
175
  return (
177
176
  <div style={{ padding: 12 }}>
178
- <div>Count: {state.tripple}</div>
177
+ <div>Count: {state.triple}</div>
179
178
 
180
179
  <button onClick={actions.inc}>+1 (action)</button>
181
180
  <button onClick={() => actions.add(10)}>+10 (action)</button>
@@ -270,12 +269,12 @@ function Buttons() {
270
269
  )
271
270
  }
272
271
 
273
- // useSelector
274
- import { useSelector } from "logic-runtime-react-z"
272
+ // useLogicSelector
273
+ import { useLogicSelector } from "logic-runtime-react-z"
275
274
  import { counterLogic } from "./counter.logic"
276
275
 
277
276
  function DoubleValue() {
278
- const double = useSelector(
277
+ const double = useLogicSelector(
279
278
  counterLogic,
280
279
  s => s.double
281
280
  )
@@ -417,6 +416,37 @@ emit("login:failed", error)
417
416
 
418
417
  ---
419
418
 
420
- ## 📜 License
419
+ ## 🔍 Comparison with: Redux vs Zustand
420
+
421
+ | Capability / Library | logic-runtime-react-z | Redux | Zustand |
422
+ |--------------------------|:---------------------:|:-----:|:-------:|
423
+ | Intent-first model | ✅ | ❌ | ❌ |
424
+ | State-first model | ❌ | ✅ | ✅ |
425
+ | First-class effects | ✅ | ❌ | ❌ |
426
+ | Built-in async handling | ✅ | ❌ | ❌ |
427
+ | Computed state graph | ✅ | ❌ | ⚠️ |
428
+ | Deterministic execution | ✅ | ❌ | ❌ |
429
+ | Logic outside React | ✅ | ❌ | ❌ |
430
+ | Backend-safe | ✅ | ❌ | ❌ |
431
+ | Intent / effect tracing | ✅ | ❌ | ❌ |
432
+ | Centralized state store | ❌ | ✅ | ✅ |
433
+ | Easy global state | ⚠️ | ✅ | ✅ |
434
+ | Minimal boilerplate | ✅ | ❌ | ✅ |
435
+
436
+ ```bash
437
+ Redux / Zustand:
438
+ UI → setState → store → re-render
439
+
440
+ logic-runtime-react-z:
441
+ UI → intent → logic → effect → state
442
+ ```
443
+
444
+ ### One-liner takeaway
445
+ - Redux and Zustand manage **state**.
446
+ - logic-runtime-react-z orchestrates **logic**.
447
+
448
+ ---
449
+
450
+ ## License
421
451
 
422
- MIT / Delpi
452
+ MIT
@@ -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"),n=require("react/jsx-runtime"),s=require("react");function o(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(n){if("default"!==n){var s=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,s.get?s:{enumerable:!0,get:function(){return t[n]}})}}),e.default=t,Object.freeze(e)}var r=o(s);class i{constructor(){this.handlers={},this.effects={},this.middlewares=[]}use(t){this.middlewares.push(t)}effect(t,e){var n,s;(null!==(n=(s=this.effects)[t])&&void 0!==n?n:s[t]=[]).push(e)}on(t,e){var n,s;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!==(n=(s=this.handlers)[t])&&void 0!==n?n:s[t]=[]).push(e)}async emit(t,e){var n,s;const o=null!==(n=this.handlers[t])&&void 0!==n?n:[],r=null!==(s=this.effects[t])&&void 0!==s?s:[],i=(a=this.middlewares,c=async t=>{t.effects=r;for(const e of o)await e(t)},a.reduceRight((t,e)=>e(t),c));var a,c;await i(e)}}class a{constructor(t,n=e.createScope("logic")){this.subs=new Set,this.bus=new i,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 n=new AbortController;await this.bus.emit(t,{payload:e,scope:this.scope,signal:n.signal,state:this.getSnapshot,setState:t=>this.setStateInternal(t),emit:this.emit})},this.scope=n,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 n=>async s=>{var o;const r=s.effects;if(!(null==r?void 0:r.length))return n(s);for(const n of r){const r=n.handler.toString();if("takeLatest"===n.strategy){null===(o=t.get(r))||void 0===o||o.abort();const e=new AbortController;t.set(r,e),await n.handler({...s,signal:e.signal})}else"debounce"===n.strategy?(clearTimeout(e.get(r)),e.set(r,setTimeout(()=>n.handler(s),n.wait))):await n.handler(s)}await n(s)}}())}createAtoms(e){const n={};for(const s in e)n[s]=t.atom(e[s]);return n}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,n){const s=t.atom(void 0);t.effect(()=>{this.isComputing=!0;const t=n();s.set(t),this.isComputing=!1}),this.atoms[e]=s,s.subscribe(this.markDirty)}}function c(t){const e=function(){let t=0,e=[];return{get records(){return e.slice()},record:function(n){e.push({...n,id:++t,state:structuredClone(n.state)})},replay:async function(t,n){const{from:s=0,to:o=1/0,scope:r}=null!=n?n:{},i=e.filter(t=>"emit"===t.type&&t.id>=s&&t.id<=o&&(!r||t.scope===r));for(const e of i){const n=t(e.intent,e.payload);n instanceof Promise&&await n}},clear:function(){e=[],t=0}}}();function n(){return"string"==typeof t.scope?t.scope:t.scope.name}return{timeline:e,wrap:function(){const s=t.emit.bind(t);t.emit=async(o,r)=>{e.record({type:"emit:start",intent:o,payload:r,scope:n(),state:t.getSnapshot(),timestamp:Date.now()});try{const t=s(o,r);t instanceof Promise&&await t}finally{e.record({type:"emit:end",intent:o,payload:r,scope:n(),state:t.getSnapshot(),timestamp:Date.now()})}}}}}function u(t){var n;const o=s.useRef(null),r=null!=t&&"function"==typeof t.subscribe&&"function"==typeof t.getSnapshot?t:null!==(n=o.current)&&void 0!==n?n:o.current=t.create(e.createScope("react"));return s.useSyncExternalStore(r.subscribe,r.getSnapshot,r.getSnapshot)}Object.defineProperty(exports,"createSelector",{enumerable:!0,get:function(){return t.createSelector}}),exports.LogicRuntime=a,exports.attachDevtools=c,exports.composeLogic=function(t){return{create(e){const n={};for(const s in t){const o=s;n[o]=t[o].create(e)}return{async emit(t,e){for(const s of Object.values(n))await s.emit(t,e)},subscribe(t){const e=Object.values(n).map(e=>e.subscribe(t));return()=>e.forEach(t=>t())},getSnapshot(){const t={};for(const e in n)t[e]=n[e].getSnapshot();return t},runtimes:n}}}},exports.createBackendRuntime=function(t){var n,s;let o=structuredClone(t);const r=()=>o,i=t=>{o={...o,...t}},a=e.createScope("backend");async function u(t,e){await l.emit(t,e,a)}const l=e.createIntentBus(t=>{const e=new AbortController;return{scope:a,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 n=t[e];l.on(e,async(t,e)=>{var s;const o={get state(){return r()},signal:null!==(s=t.signal)&&void 0!==s?s:(new AbortController).signal,set:i,emit:u};await n(o)},a)}},onIntent:l.on,effect:l.effect};if("production"!==(null===(s=null===(n=null===globalThis||void 0===globalThis?void 0:globalThis.process)||void 0===n?void 0:n.env)||void 0===s?void 0:s.NODE_ENV)){const t=c(h);t.wrap(h),h.devtools=t}return h},exports.createLogic=function(t){return{name:t.name,create(e){var n;const s=new a(structuredClone(t.state),e);if(t.computed)for(const e in t.computed)s.attachComputed(e,()=>t.computed[e]({state:s.getSnapshot()}));null===(n=t.intents)||void 0===n||n.call(t,{on:s.onIntent,effect:s.useEffect.bind(s)});const o={};if(t.actions)for(const e in t.actions)o[e]=t.actions[e]({emit:s.emit,getState:s.getSnapshot});return s.actions=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(null!=t&&"actions"in t?t:u(t)).actions},exports.useLogicSelector=function(e,n){const o=s.useMemo(()=>t.createSelector(n),[n]);return s.useSyncExternalStore(e.subscribe,()=>o(e.getSnapshot()),()=>o(e.getSnapshot()))},exports.useRuntime=u,exports.withLogic=function(t,s,o){var i,a;const c=i=>{const a=r.useRef(null);a.current||(a.current=t.create("string"==typeof o?e.createScope(o):o));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 n.jsx(s,{...i,...h})};return c.displayName=`withLogic(${null!==(a=null!==(i=s.displayName)&&void 0!==i?i:s.name)&&void 0!==a?a:"View"})`,c};
package/build/index.d.ts CHANGED
@@ -1,12 +1,14 @@
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";
8
- export { useActions } from "./react/useActions";
9
- export { useRuntime } from "./react/useRuntime";
10
- export { useSelector } from "./react/useSelector";
11
10
  export { withLogic } from "./react/withLogic";
11
+ export { useRuntime } from "./react/useRuntime";
12
+ export { useActions } from "./react/useActions";
13
+ export { useLogicSelector } from "./react/useLogicSelector";
12
14
  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.subs=new Set,this.bus=new l,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 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(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 o=t(void 0);e(()=>{this.isComputing=!0;const t=n();o.set(t),this.isComputing=!1}),this.atoms[s]=o,o.subscribe(this.markDirty)}}function f(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]({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}}}function m(t){return{create(e){const s={};for(const n in t){const o=n;s[o]=t[o].create(e)}return{async emit(t,e){for(const n of Object.values(s))await n.emit(t,e)},subscribe(t){const e=Object.values(s).map(e=>e.subscribe(t));return()=>e.forEach(t=>t())},getSnapshot(){const t={};for(const e in s)t[e]=s[e].getSnapshot();return t},runtimes:s}}}}function p(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=p(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=null!=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(null!=t&&"actions"in t?t:y(t)).actions}function v(t,e){const n=u(()=>s(e),[e]);return c(t.subscribe,()=>n(t.getSnapshot()),()=>n(t.getSnapshot()))}export{h as LogicRuntime,p as attachDevtools,m as composeLogic,g as createBackendRuntime,d as createLogic,f as effect,w as useActions,v as useLogicSelector,y as useRuntime,b as withLogic};
@@ -1,16 +1,18 @@
1
1
  import { LogicRuntime } from "../core/runtime";
2
- type LogicFactory<S extends object> = {
3
- create(): LogicRuntime<S, any>;
2
+ import type { LogicFactory } from "../logic/createLogic";
3
+ type LogicMap = Record<string, LogicFactory<any, any>>;
4
+ type RuntimeMap<M extends LogicMap> = {
5
+ [K in keyof M]: ReturnType<M[K]["create"]>;
4
6
  };
5
- type LogicMap = Record<string, LogicFactory<any>>;
6
7
  type SnapshotOf<M extends LogicMap> = {
7
8
  [K in keyof M]: ReturnType<M[K]["create"]> extends LogicRuntime<infer S extends object, any> ? Readonly<S> : never;
8
9
  };
9
10
  export declare function composeLogic<M extends LogicMap>(logics: M): {
10
- create(): {
11
+ create(scope?: any): {
11
12
  emit(intent: string, payload?: any): Promise<void>;
12
13
  subscribe(fn: () => void): () => void;
13
14
  getSnapshot(): SnapshotOf<M>;
15
+ runtimes: RuntimeMap<M>;
14
16
  };
15
17
  };
16
18
  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,5 +1,6 @@
1
1
  import { LogicActions, LogicFactory } from "../logic/createLogic";
2
- export declare function useActions<A extends object>(runtime: {
2
+ import { LogicRuntime } from "../core/runtime";
3
+ export declare function useActions<A extends object>(runtime: LogicRuntime<any> & {
3
4
  actions: A;
4
5
  }): A;
5
6
  export declare function useActions<S extends object, A extends LogicActions>(logic: LogicFactory<S, A>): A;
@@ -0,0 +1,2 @@
1
+ import { LogicRuntime } from "../core/runtime";
2
+ export declare function useLogicSelector<S extends object, R>(runtime: LogicRuntime<S>, selector: (state: Readonly<S>) => R): R;
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.0",
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;