logic-runtime-react-z 3.1.0 → 3.1.2
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 +222 -145
- package/build/core/effect.d.ts +4 -0
- package/build/core/runtime.d.ts +20 -7
- package/build/index.cjs.js +1 -1
- package/build/index.d.ts +3 -1
- package/build/index.esm.js +1 -1
- package/build/logic/composeLogic.d.ts +15 -12
- package/build/logic/createLogic.d.ts +11 -16
- package/build/react/useActions.d.ts +4 -6
- package/build/react/useComputed.d.ts +6 -0
- package/build/react/useLogic.d.ts +6 -0
- package/build/react/useLogicSelector.d.ts +2 -2
- package/build/react/useRuntime.d.ts +4 -3
- package/build/react/withLogic.d.ts +5 -4
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -10,15 +10,16 @@ A headless, deterministic, intent-driven runtime for frontend & backend logic.
|
|
|
10
10
|
React components stay pure. Business logic is fully testable, replayable, and framework-agnostic.
|
|
11
11
|
|
|
12
12
|
> **Intent is the only entry point.**
|
|
13
|
+
> **React is optional. `createLogic` is the product. Everything else is an adapter.**
|
|
13
14
|
|
|
14
15
|
---
|
|
15
16
|
|
|
16
17
|
## ✨ Why logic-runtime-react-z?
|
|
17
18
|
|
|
18
|
-
- No
|
|
19
|
+
- No business logic in React components
|
|
19
20
|
- Intent is the *only* entry point
|
|
20
21
|
- Predictable async flows
|
|
21
|
-
-
|
|
22
|
+
- Reactive computed graph with caching
|
|
22
23
|
- Headless & backend-friendly
|
|
23
24
|
- Deterministic testing & devtools replay
|
|
24
25
|
|
|
@@ -37,7 +38,7 @@ UI / HTTP / Queue / Cron
|
|
|
37
38
|
↓
|
|
38
39
|
mutate state
|
|
39
40
|
↓
|
|
40
|
-
computed / subscribers
|
|
41
|
+
computed (derived state) / subscribers
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
Think **events → behavior → state → derived state**.
|
|
@@ -75,23 +76,46 @@ const counterLogic = createLogic({
|
|
|
75
76
|
},
|
|
76
77
|
})
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
async function main() {
|
|
80
|
+
const runtime = counterLogic.create()
|
|
81
|
+
|
|
82
|
+
await runtime.emit("inc")
|
|
83
|
+
await runtime.emit("add", 5)
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
console.log(runtime.state.count)
|
|
86
|
+
}
|
|
82
87
|
|
|
83
|
-
|
|
88
|
+
main()
|
|
84
89
|
```
|
|
85
90
|
|
|
86
|
-
✔ No UI
|
|
87
|
-
✔ Fully testable
|
|
91
|
+
✔ No UI
|
|
92
|
+
✔ Fully testable
|
|
88
93
|
✔ Deterministic
|
|
89
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
|
+
|
|
90
99
|
---
|
|
91
100
|
|
|
92
|
-
##
|
|
101
|
+
## 🧠 Computed State
|
|
93
102
|
|
|
94
|
-
|
|
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
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## ⚛️ React Integration (No Hooks Required)
|
|
117
|
+
|
|
118
|
+
### Define Logic (Framework-Agnostic)
|
|
95
119
|
|
|
96
120
|
```ts
|
|
97
121
|
// counter.logic.ts
|
|
@@ -136,10 +160,11 @@ export const counterLogic = createLogic({
|
|
|
136
160
|
})
|
|
137
161
|
})
|
|
138
162
|
|
|
163
|
+
// effects = side-effects only (no state mutation)
|
|
139
164
|
bus.effect(
|
|
140
165
|
"inc-async",
|
|
141
166
|
effect(async ({ payload }) => {
|
|
142
|
-
console.log("effect run
|
|
167
|
+
console.log("effect run:", payload)
|
|
143
168
|
}).takeLatest()
|
|
144
169
|
)
|
|
145
170
|
},
|
|
@@ -158,26 +183,27 @@ export const counterLogic = createLogic({
|
|
|
158
183
|
},
|
|
159
184
|
},
|
|
160
185
|
})
|
|
186
|
+
|
|
161
187
|
```
|
|
162
188
|
|
|
163
189
|
---
|
|
164
190
|
|
|
165
|
-
### Pure React View (
|
|
191
|
+
### Pure React View (Dumb View)
|
|
166
192
|
|
|
167
193
|
```tsx
|
|
168
194
|
import React from "react"
|
|
169
195
|
import { withLogic } from "logic-runtime-react-z"
|
|
170
196
|
import { counterLogic } from "./counter.logic"
|
|
171
197
|
|
|
172
|
-
function CounterView(props
|
|
198
|
+
function CounterView(props) {
|
|
173
199
|
const { state, actions, emit } = props
|
|
174
200
|
|
|
175
201
|
return (
|
|
176
202
|
<div style={{ padding: 12 }}>
|
|
177
|
-
<div>Count: {state.triple}</div>
|
|
203
|
+
<div>Triple Count: {state.triple}</div>
|
|
178
204
|
|
|
179
|
-
<button onClick={actions.inc}>+1
|
|
180
|
-
<button onClick={() => actions.add(10)}>+10
|
|
205
|
+
<button onClick={actions.inc}>+1</button>
|
|
206
|
+
<button onClick={() => actions.add(10)}>+10</button>
|
|
181
207
|
|
|
182
208
|
<button
|
|
183
209
|
disabled={state.loading}
|
|
@@ -189,74 +215,93 @@ function CounterView(props: any) {
|
|
|
189
215
|
<hr />
|
|
190
216
|
|
|
191
217
|
<button onClick={() => emit("inc")}>
|
|
192
|
-
|
|
218
|
+
emit("inc")
|
|
193
219
|
</button>
|
|
194
220
|
</div>
|
|
195
221
|
)
|
|
196
222
|
}
|
|
197
223
|
|
|
198
|
-
export const CounterPage =
|
|
199
|
-
|
|
224
|
+
export const CounterPage = withLogic(counterLogic, CounterView)
|
|
225
|
+
|
|
200
226
|
```
|
|
201
227
|
|
|
202
|
-
✔ Props inferred
|
|
203
|
-
✔ No generics
|
|
204
|
-
✔ No interfaces
|
|
205
|
-
✔ View stays dumb
|
|
228
|
+
✔ Props are inferred when using withLogic, no manual generics required.
|
|
206
229
|
|
|
207
230
|
---
|
|
208
231
|
|
|
209
|
-
## 🧪 Backend Runtime
|
|
232
|
+
## 🧪 Backend Usage (Same Runtime)
|
|
210
233
|
|
|
211
234
|
```ts
|
|
212
|
-
import {
|
|
235
|
+
import { createLogic } from "logic-runtime-react-z"
|
|
213
236
|
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
237
|
+
const authLogic = createLogic({
|
|
238
|
+
state: {
|
|
239
|
+
user: null,
|
|
240
|
+
loading: false,
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
intents: bus => {
|
|
244
|
+
bus.on("login", async ({ setState }) => {
|
|
245
|
+
setState(s => {
|
|
246
|
+
s.loading = true
|
|
247
|
+
})
|
|
218
248
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
loading: false,
|
|
249
|
+
await new Promise(r => setTimeout(r, 500))
|
|
250
|
+
|
|
251
|
+
setState(s => {
|
|
252
|
+
s.user = { name: "Alice" }
|
|
253
|
+
s.loading = false
|
|
254
|
+
})
|
|
226
255
|
})
|
|
227
|
-
},
|
|
228
256
|
|
|
229
|
-
|
|
230
|
-
|
|
257
|
+
bus.on("logout", ({ setState }) => {
|
|
258
|
+
setState(s => {
|
|
259
|
+
s.user = null
|
|
260
|
+
})
|
|
261
|
+
})
|
|
231
262
|
},
|
|
232
263
|
})
|
|
233
264
|
|
|
234
|
-
|
|
235
|
-
|
|
265
|
+
async function run() {
|
|
266
|
+
const runtime = authLogic.create()
|
|
236
267
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
console.log(devtools.timeline.records)
|
|
268
|
+
await runtime.emit("login")
|
|
269
|
+
await runtime.emit("logout")
|
|
240
270
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
271
|
+
console.log(runtime.getSnapshot())
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
run()
|
|
245
275
|
```
|
|
246
276
|
|
|
247
|
-
✔ Same
|
|
277
|
+
✔ Same runtime, same behavior, no React involved.
|
|
248
278
|
✔ No React
|
|
249
279
|
✔ Replayable
|
|
250
|
-
✔ Devtools is backend-first.
|
|
251
280
|
|
|
252
281
|
---
|
|
253
282
|
|
|
254
|
-
## 🪝 Hooks
|
|
283
|
+
## 🪝 Hooks Examples (Optional, Thin Adapters)
|
|
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.
|
|
255
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)
|
|
256
303
|
```ts
|
|
257
|
-
// useActions
|
|
258
304
|
import { useActions } from "logic-runtime-react-z"
|
|
259
|
-
import { counterLogic } from "./counter.logic"
|
|
260
305
|
|
|
261
306
|
function Buttons() {
|
|
262
307
|
const actions = useActions(counterLogic)
|
|
@@ -268,36 +313,72 @@ function Buttons() {
|
|
|
268
313
|
</>
|
|
269
314
|
)
|
|
270
315
|
}
|
|
316
|
+
```
|
|
271
317
|
|
|
272
|
-
// useLogicSelector
|
|
273
|
-
import { useLogicSelector } from "logic-runtime-react-z"
|
|
274
|
-
import { counterLogic } from "./counter.logic"
|
|
275
318
|
|
|
276
|
-
|
|
277
|
-
|
|
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(
|
|
278
352
|
counterLogic,
|
|
279
|
-
|
|
353
|
+
c => c.double
|
|
280
354
|
)
|
|
281
355
|
|
|
282
356
|
return <div>Double: {double}</div>
|
|
283
357
|
}
|
|
358
|
+
```
|
|
284
359
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
360
|
+
✔ Component re-renders only when double changes
|
|
361
|
+
✔ No extra dependencies
|
|
362
|
+
✔ Type-safe selector
|
|
288
363
|
|
|
289
|
-
|
|
290
|
-
|
|
364
|
+
#### useLogicSelector – State selector (Redux-like)
|
|
365
|
+
```ts
|
|
366
|
+
import { useLogicSelector } from "logic-runtime-react-z"
|
|
291
367
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
368
|
+
function CountLabel() {
|
|
369
|
+
const count = useLogicSelector(
|
|
370
|
+
counterLogic,
|
|
371
|
+
state => state.count
|
|
296
372
|
)
|
|
297
|
-
}
|
|
298
373
|
|
|
374
|
+
return <span>{count}</span>
|
|
375
|
+
}
|
|
299
376
|
```
|
|
300
377
|
|
|
378
|
+
✔ Memoized selector
|
|
379
|
+
✔ Fine-grained subscriptions
|
|
380
|
+
✔ Familiar mental model
|
|
381
|
+
|
|
301
382
|
---
|
|
302
383
|
|
|
303
384
|
## 🧱 Composing Multiple Logic Modules
|
|
@@ -307,20 +388,16 @@ import { composeLogic } from "logic-runtime-react-z"
|
|
|
307
388
|
import { userLogic } from "./user.logic"
|
|
308
389
|
import { cartLogic } from "./cart.logic"
|
|
309
390
|
|
|
310
|
-
|
|
391
|
+
const app = composeLogic({
|
|
311
392
|
user: userLogic,
|
|
312
393
|
cart: cartLogic,
|
|
313
394
|
})
|
|
314
395
|
|
|
396
|
+
await app.emit("login")
|
|
315
397
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
await runtime.emit("user:login", credentials)
|
|
320
|
-
|
|
321
|
-
const snapshot = runtime.getSnapshot()
|
|
322
|
-
snapshot.user // user state
|
|
323
|
-
snapshot.cart // cart state
|
|
398
|
+
const state = app.getState()
|
|
399
|
+
state.user
|
|
400
|
+
state.cart
|
|
324
401
|
|
|
325
402
|
```
|
|
326
403
|
|
|
@@ -345,108 +422,108 @@ const logic = createLogic({
|
|
|
345
422
|
},
|
|
346
423
|
})
|
|
347
424
|
|
|
348
|
-
const runtime =
|
|
425
|
+
const runtime = counterLogic.create()
|
|
349
426
|
|
|
350
427
|
await runtime.emit("set", 4)
|
|
351
|
-
|
|
352
|
-
expect(runtime.state.squared).toBe(16)
|
|
428
|
+
expect(runtime.computed.squared).toBe(16)
|
|
353
429
|
```
|
|
354
430
|
|
|
431
|
+
✔ Computed values are tested like plain data
|
|
432
|
+
|
|
355
433
|
---
|
|
356
434
|
|
|
357
|
-
##
|
|
435
|
+
## 🔍 Comparison
|
|
358
436
|
|
|
359
|
-
|
|
437
|
+
| Criteria | logic-runtime-react-z | Redux | Zustand | Recoil | MobX |
|
|
438
|
+
| ----------------------------------------- | :---------------------: | :------------------: | :-------------------: | :----------------------------: | :-------------------------: |
|
|
439
|
+
| **Intent-first model** | ✅ | ❌ | ❌ | ⚠️ | ⚠️ |
|
|
440
|
+
| **State-first model** | ❌ | ✅ | ✅ | ✅ | ✅ |
|
|
441
|
+
| **First-class effect orchestration** | ✅ (built-in) | ⚠️ (via middleware) | ⚠️ (via middleware) | ⚠️ (selectors + async helpers) | ⚠️ (actions + reactions) |
|
|
442
|
+
| **Fine-grained derived state (computed)** | ✅ (reactive & cached) | ❌ | ⚠️ (simple selectors) | ✅ (dependency graph) | ⚠️ (observable derivations) |
|
|
443
|
+
| **Predictable execution semantics** | 🔁 intent queue/rules | 👍 sync + middleware | 👍 sync | 👍 sync | 👍 sync |
|
|
444
|
+
| **Async control strategies built-in** | ✅ takeLatest / debounce | ❌ | ❌ | ❌ | ❌ |
|
|
445
|
+
| **Logic outside React** | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ |
|
|
446
|
+
| **Framework-agnostic** | ✅ | ⚫ | ⚫ | ⚫ | ⚫ |
|
|
447
|
+
| **Backend-ready usage** | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ |
|
|
448
|
+
| **Type-inferred actions** | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ |
|
|
449
|
+
| **Minimal re-render strategies** | ✓ selectors/hooks | ✓ selectors | ✓ selectors | ✓ atoms/selectors | ✗ global observables |
|
|
450
|
+
| **Devtools ecosystem** | ⚠️ (nascent) | ✅ | ⚠️ | ⚠️ | ⚠️ |
|
|
360
451
|
|
|
361
|
-
```tsx
|
|
362
|
-
useEffect(() => {
|
|
363
|
-
fetchData()
|
|
364
|
-
}, [])
|
|
365
|
-
```
|
|
366
452
|
|
|
367
|
-
✅ Correct
|
|
368
453
|
|
|
369
|
-
|
|
370
|
-
emit("data:fetch")
|
|
371
|
-
```
|
|
454
|
+
<b>⚠️ via selectors, not a true dependency graph. </b>
|
|
372
455
|
|
|
373
456
|
---
|
|
374
457
|
|
|
375
|
-
|
|
458
|
+
## ❓ Why not Redux + RTK?
|
|
376
459
|
|
|
377
|
-
|
|
378
|
-
runtime.state.user.name = "admin"
|
|
379
|
-
```
|
|
460
|
+
Redux focuses on state transitions.
|
|
380
461
|
|
|
381
|
-
|
|
462
|
+
logic-runtime-react-z models system behavior.
|
|
382
463
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
464
|
+
In Redux:
|
|
465
|
+
- async flow is external (thunk/saga)
|
|
466
|
+
- effects are not first-class
|
|
467
|
+
- execution model depends on middleware setup
|
|
386
468
|
|
|
387
|
-
|
|
469
|
+
In logic-runtime-react-z:
|
|
470
|
+
- async orchestration is built-in
|
|
471
|
+
- intent is the only entry point
|
|
472
|
+
- execution order is guaranteed
|
|
388
473
|
|
|
389
|
-
### ❌ Generic Redux-style intents
|
|
390
474
|
|
|
391
|
-
|
|
392
|
-
emit("SET_STATE", { loading: true })
|
|
393
|
-
```
|
|
475
|
+
---
|
|
394
476
|
|
|
395
|
-
|
|
477
|
+
## 🧠 One-liner Takeaway
|
|
396
478
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
emit("login:success", user)
|
|
400
|
-
emit("login:failed", error)
|
|
401
|
-
```
|
|
479
|
+
- Redux & Zustand manage **state**
|
|
480
|
+
- logic-runtime-react-z orchestrates **logic**
|
|
402
481
|
|
|
403
482
|
---
|
|
404
483
|
|
|
405
|
-
##
|
|
406
|
-
|
|
407
|
-
- Complex async flows
|
|
408
|
-
- Shared logic across UI / backend
|
|
409
|
-
- Need deterministic tests
|
|
410
|
-
- Want to remove logic from React
|
|
484
|
+
## 🧬 Deterministic Execution Model
|
|
411
485
|
|
|
412
|
-
|
|
486
|
+
- Intents are processed sequentially
|
|
487
|
+
- State mutations are isolated
|
|
488
|
+
- Async effects follow declared strategies (takeLatest, debounce, etc.)
|
|
489
|
+
- Execution order is predictable
|
|
413
490
|
|
|
414
|
-
|
|
415
|
-
- Throwaway components
|
|
491
|
+
Given the same intent sequence, the resulting state is reproducible.
|
|
416
492
|
|
|
417
493
|
---
|
|
418
494
|
|
|
419
|
-
##
|
|
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 | ✅ | ❌ | ✅ |
|
|
495
|
+
## 📐 Architecture Diagram (High-level)
|
|
435
496
|
|
|
436
497
|
```bash
|
|
437
|
-
|
|
438
|
-
UI
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
498
|
+
┌─────────────┐
|
|
499
|
+
│ React UI │
|
|
500
|
+
└──────┬──────┘
|
|
501
|
+
│ adapter
|
|
502
|
+
┌──────▼──────┐
|
|
503
|
+
│ Runtime │
|
|
504
|
+
│ (create) │
|
|
505
|
+
├─────────────┤
|
|
506
|
+
│ Intent Bus │
|
|
507
|
+
│ Effects │
|
|
508
|
+
│ Handlers │
|
|
509
|
+
│ State │
|
|
510
|
+
│ Computed │
|
|
511
|
+
└─────────────┘
|
|
442
512
|
```
|
|
443
513
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
514
|
+
<b>Runtime is the product. React is an adapter.</b>
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## ⚠️ When Not to Use
|
|
519
|
+
|
|
520
|
+
- Simple local component state
|
|
521
|
+
- Small apps without async complexity
|
|
522
|
+
- Teams unfamiliar with event-driven models
|
|
447
523
|
|
|
448
524
|
---
|
|
449
525
|
|
|
526
|
+
|
|
450
527
|
## License
|
|
451
528
|
|
|
452
529
|
MIT
|
package/build/core/effect.d.ts
CHANGED
|
@@ -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;
|
package/build/core/runtime.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
23
|
+
private createStateAtoms;
|
|
16
24
|
private buildSnapshot;
|
|
17
25
|
private markDirty;
|
|
18
|
-
|
|
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
|
|
44
|
+
attachComputed<K extends keyof C & string>(key: K, compute: C[K]): void;
|
|
32
45
|
}
|
package/build/index.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var t=require("chrono-state-z"),e=require("intentx-core-z"),
|
|
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.id;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",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.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
|
@@ -8,7 +8,9 @@ export type { LogicFactory, LogicActions, } from "./logic/createLogic";
|
|
|
8
8
|
export { composeLogic } from "./logic/composeLogic";
|
|
9
9
|
export { createBackendRuntime } from "./logic/createBackendRuntime";
|
|
10
10
|
export { withLogic } from "./react/withLogic";
|
|
11
|
-
export { useRuntime } from "./react/useRuntime";
|
|
12
11
|
export { useActions } from "./react/useActions";
|
|
12
|
+
export { useComputed } from "./react/useComputed";
|
|
13
|
+
export { useLogic } from "./react/useLogic";
|
|
13
14
|
export { useLogicSelector } from "./react/useLogicSelector";
|
|
15
|
+
export { useRuntime } from "./react/useRuntime";
|
|
14
16
|
export * from "./devtools";
|
package/build/index.esm.js
CHANGED
|
@@ -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.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.
|
|
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.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),e(()=>{this.isComputing=!0,o.set(n({state:i})),this.isComputing=!1}),o.subscribe(this.markDirty)}}function m(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 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,18 +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
4
|
import type { LogicFactory } from "../logic/createLogic";
|
|
3
|
-
type
|
|
4
|
-
type
|
|
5
|
-
|
|
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;
|
|
6
10
|
};
|
|
7
|
-
type
|
|
8
|
-
[K in keyof M]:
|
|
11
|
+
type InferActions<M extends LogicMap> = {
|
|
12
|
+
[K in keyof M]: InferRuntime<M[K]> extends LogicRuntime<any, any, infer A> ? A : never;
|
|
9
13
|
};
|
|
10
|
-
export declare function composeLogic<M extends LogicMap>(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
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>;
|
|
17
20
|
};
|
|
18
21
|
export {};
|
|
@@ -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?:
|
|
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]:
|
|
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,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { LogicRuntime } from "../core/runtime";
|
|
3
|
-
export declare function useActions<A extends
|
|
4
|
-
|
|
5
|
-
}): A;
|
|
6
|
-
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
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { LogicRuntime } from "../core/runtime";
|
|
2
|
-
export declare function useLogicSelector<S extends object, R>(runtime: LogicRuntime<S>, selector: (state: Readonly<S
|
|
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
|
|
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
|
|
5
|
-
|
|
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,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "logic-runtime-react-z",
|
|
3
|
-
"version": "3.1.
|
|
4
|
-
"description": "Intent-first business logic runtime.
|
|
3
|
+
"version": "3.1.2",
|
|
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",
|
|
@@ -28,12 +29,14 @@
|
|
|
28
29
|
"files": [
|
|
29
30
|
"build"
|
|
30
31
|
],
|
|
32
|
+
|
|
31
33
|
"scripts": {
|
|
32
34
|
"clean": "rimraf build",
|
|
33
35
|
"build": "rollup -c",
|
|
34
36
|
"cb": "npm run clean && npm run build",
|
|
35
37
|
"prepublishOnly": "npm run cb"
|
|
36
38
|
},
|
|
39
|
+
|
|
37
40
|
"repository": {
|
|
38
41
|
"type": "git",
|
|
39
42
|
"url": "https://github.com/delpikye-v/logic-runtime-react.git"
|
|
@@ -42,6 +45,7 @@
|
|
|
42
45
|
"bugs": {
|
|
43
46
|
"url": "https://github.com/delpikye-v/logic-runtime-react/issues"
|
|
44
47
|
},
|
|
48
|
+
|
|
45
49
|
"keywords": [
|
|
46
50
|
"intent-first",
|
|
47
51
|
"intent-runtime",
|
|
@@ -60,6 +64,7 @@
|
|
|
60
64
|
"takeLatest",
|
|
61
65
|
"state-management"
|
|
62
66
|
],
|
|
67
|
+
|
|
63
68
|
"peerDependencies": {
|
|
64
69
|
"react": ">=18.0.0",
|
|
65
70
|
"react-dom": ">=18.0.0"
|