react-flow-z 1.0.3 → 1.0.5
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 +100 -33
- package/build/flow-system/Flow.d.ts +5 -1
- package/build/flow-system/utils.d.ts +2 -0
- package/build/index.cjs.js +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.esm.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,20 +7,15 @@
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
`react-flow-z` is a **small, framework-agnostic async flow runtime**.
|
|
10
|
-
|
|
11
|
-
It focuses on **how async logic runs**, not:
|
|
12
|
-
- how state is stored
|
|
13
|
-
- how UI renders
|
|
14
|
-
- or how effects are magically managed
|
|
10
|
+
It focuses on **how async logic runs**
|
|
15
11
|
|
|
16
12
|
> This library is about **orchestration**, not reactivity.
|
|
17
13
|
|
|
18
14
|
---
|
|
19
15
|
|
|
20
16
|
## Why react-flow-z
|
|
21
|
-
|
|
22
17
|
- Typed async execution pipeline
|
|
23
|
-
-
|
|
18
|
+
- Declarative flow composition
|
|
24
19
|
- Abort & cancellation via `AbortController`
|
|
25
20
|
- Async orchestration operators: `debounce` · `retry` · `timeout` · `switchMap` ·` parallel`...
|
|
26
21
|
- Control flow: `filter` · `take` · `conditional execution`...
|
|
@@ -45,11 +40,16 @@ import { Flow } from "react-flow-z"
|
|
|
45
40
|
|
|
46
41
|
new Flow()
|
|
47
42
|
.debounce(300)
|
|
48
|
-
.switchMap(q =>
|
|
49
|
-
|
|
43
|
+
.switchMap(async q => {
|
|
44
|
+
const res = await fetch(`/search?q=${q}`)
|
|
45
|
+
return res.json()
|
|
46
|
+
})
|
|
47
|
+
.tap(console.log)
|
|
50
48
|
.run("hello")
|
|
51
49
|
```
|
|
52
50
|
|
|
51
|
+
> Avoid mutating a shared Flow instance inside React render loops.
|
|
52
|
+
|
|
53
53
|
---
|
|
54
54
|
|
|
55
55
|
## Cancellation
|
|
@@ -104,39 +104,85 @@ useFlow(
|
|
|
104
104
|
|
|
105
105
|
##### searchFlow.ts
|
|
106
106
|
```ts
|
|
107
|
-
import { Flow } from "react-flow-z"
|
|
107
|
+
import { Flow, createFlow } from "react-flow-z";
|
|
108
|
+
|
|
109
|
+
export type Post = {
|
|
110
|
+
id: number;
|
|
111
|
+
title: string;
|
|
112
|
+
};
|
|
108
113
|
|
|
109
|
-
export const searchFlow = new Flow()
|
|
110
|
-
.onStart(() => console.log("loading..."))
|
|
114
|
+
export const searchFlow = new Flow<string, Post[]>()
|
|
111
115
|
.debounce(300)
|
|
112
|
-
.filter(
|
|
113
|
-
.switchMap(async
|
|
116
|
+
.filter(q => q.trim().length > 0)
|
|
117
|
+
.switchMap(async q => {
|
|
114
118
|
const res = await fetch(
|
|
115
119
|
`https://jsonplaceholder.typicode.com/posts?q=${q}`
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (!res.ok) throw new Error("network error");
|
|
123
|
+
|
|
124
|
+
return res.json();
|
|
125
|
+
});
|
|
122
126
|
|
|
123
127
|
// searchFlow.run("r")
|
|
124
128
|
// searchFlow.run("re")
|
|
125
129
|
// searchFlow.run("react")
|
|
126
130
|
```
|
|
127
131
|
|
|
132
|
+
##### createFlow.ts
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { Flow, createFlow } from "react-flow-z";
|
|
136
|
+
export const searchFlow = createFlow<string, Post[]>()
|
|
137
|
+
.debounce(300)
|
|
138
|
+
.filter(q => q.trim().length > 0)
|
|
139
|
+
.switchMap(async q => {
|
|
140
|
+
const res = await fetch(
|
|
141
|
+
`https://jsonplaceholder.typicode.com/posts?q=${q}`
|
|
142
|
+
)
|
|
143
|
+
return res.json()
|
|
144
|
+
})
|
|
145
|
+
```
|
|
146
|
+
|
|
128
147
|
##### React usage
|
|
129
148
|
```ts
|
|
149
|
+
import { useEffect, useState } from "react";
|
|
150
|
+
import { searchFlow, Post } from "./searchFlow";
|
|
151
|
+
import "./styles.css";
|
|
152
|
+
|
|
130
153
|
function SearchExample() {
|
|
131
|
-
const [q, setQ] = useState("")
|
|
132
|
-
const [posts, setPosts] = useState<
|
|
154
|
+
const [q, setQ] = useState("");
|
|
155
|
+
const [posts, setPosts] = useState<Post[]>([]);
|
|
156
|
+
const [loading, setLoading] = useState(false);
|
|
157
|
+
const [error, setError] = useState<string | null>(null);
|
|
133
158
|
|
|
134
159
|
useEffect(() => {
|
|
160
|
+
if (!q) {
|
|
161
|
+
setPosts([]);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
setLoading(true);
|
|
166
|
+
setError(null);
|
|
167
|
+
|
|
135
168
|
searchFlow
|
|
136
|
-
.tap(setPosts)
|
|
137
|
-
.catch(() => [])
|
|
138
169
|
.run(q)
|
|
139
|
-
|
|
170
|
+
.then(result => {
|
|
171
|
+
setPosts(Array.isArray(result) ? result : []);
|
|
172
|
+
})
|
|
173
|
+
.catch(err => {
|
|
174
|
+
console.error(err);
|
|
175
|
+
setError("Something went wrong");
|
|
176
|
+
setPosts([]);
|
|
177
|
+
})
|
|
178
|
+
.finally(() => {
|
|
179
|
+
setLoading(false);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return () => {
|
|
183
|
+
searchFlow.cancel();
|
|
184
|
+
};
|
|
185
|
+
}, [q]);
|
|
140
186
|
|
|
141
187
|
return (
|
|
142
188
|
<>
|
|
@@ -145,13 +191,26 @@ function SearchExample() {
|
|
|
145
191
|
onChange={e => setQ(e.target.value)}
|
|
146
192
|
placeholder="Search posts..."
|
|
147
193
|
/>
|
|
194
|
+
|
|
195
|
+
{loading && <p>Loading...</p>}
|
|
196
|
+
{error && <p style={{ color: "red" }}>{error}</p>}
|
|
197
|
+
|
|
148
198
|
<ul>
|
|
149
199
|
{posts.map(p => (
|
|
150
200
|
<li key={p.id}>{p.title}</li>
|
|
151
201
|
))}
|
|
152
202
|
</ul>
|
|
153
203
|
</>
|
|
154
|
-
)
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default function App() {
|
|
208
|
+
return (
|
|
209
|
+
<div className="App">
|
|
210
|
+
<h2>react-flow-z + React</h2>
|
|
211
|
+
<SearchExample />
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
155
214
|
}
|
|
156
215
|
|
|
157
216
|
```
|
|
@@ -195,13 +254,13 @@ export function runSearch(q: string) {
|
|
|
195
254
|
|
|
196
255
|
## Compare high-level
|
|
197
256
|
|
|
198
|
-
| Point | react-flow-z | RxJS | Redux-Saga | XState |
|
|
199
|
-
| ---------------------- | ------------ | ----- | ---------- | ------- |
|
|
200
|
-
| Async orchestration | ✅ | ✅ | ✅ | 🟡 |
|
|
201
|
-
| Debounce / cancel | ✅ | ✅ | 🟡 | 🟡 |
|
|
202
|
-
| Execution-first design | ✅ | ❌ | 🟡 | ❌ |
|
|
203
|
-
| Framework-agnostic | ✅ | ✅ | ❌ | 🟡 |
|
|
204
|
-
| Learning curve | ⭐ easy | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
|
|
257
|
+
| Point | react-flow-z | RxJS | Redux-Saga | XState |
|
|
258
|
+
| ---------------------- | ------------ | ----- | ---------- | ------- |
|
|
259
|
+
| Async orchestration | ✅ | ✅ | ✅ | 🟡 |
|
|
260
|
+
| Debounce / cancel | ✅ | ✅ | 🟡 | 🟡 |
|
|
261
|
+
| Execution-first design | ✅ | ❌ | 🟡 | ❌ |
|
|
262
|
+
| Framework-agnostic | ✅ | ✅ | ❌ | 🟡 |
|
|
263
|
+
| Learning curve | ⭐ easy | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
|
|
205
264
|
|
|
206
265
|
---
|
|
207
266
|
|
|
@@ -218,6 +277,14 @@ If you need **explicit async execution with cancel / debounce / queue** → reac
|
|
|
218
277
|
|
|
219
278
|
---
|
|
220
279
|
|
|
280
|
+
## Flow lifecycle notes
|
|
281
|
+
|
|
282
|
+
- A `Flow` instance is stateful
|
|
283
|
+
- Operators like `tap`, `catch`, `onDone` mutate the instance
|
|
284
|
+
- Prefer: configuring the flow once, handling React state outside the flow
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
221
288
|
## License
|
|
222
289
|
|
|
223
290
|
MIT
|
|
@@ -7,6 +7,7 @@ export type TypedFlow<I, O, Context> = Flow<Context> & {
|
|
|
7
7
|
tap(fn: (v: O, context: Context) => void): TypedFlow<I, O, Context>;
|
|
8
8
|
filter(cond: Condition<O>): TypedFlow<I, O, Context>;
|
|
9
9
|
debounce(ms: number): TypedFlow<I, O, Context>;
|
|
10
|
+
delay(ms: number): TypedFlow<I, O, Context>;
|
|
10
11
|
throttle(ms: number): TypedFlow<I, O, Context>;
|
|
11
12
|
leading(ms: number): TypedFlow<I, O, Context>;
|
|
12
13
|
map<N>(fn: (v: O, context: Context) => N): TypedFlow<I, N, Context>;
|
|
@@ -23,6 +24,7 @@ export type TypedFlow<I, O, Context> = Flow<Context> & {
|
|
|
23
24
|
max?: number;
|
|
24
25
|
}): TypedFlow<I, O, Context>;
|
|
25
26
|
timeout(ms: number): TypedFlow<I, O, Context>;
|
|
27
|
+
tapError(fn: (v: O, context: Context) => void): TypedFlow<I, O, Context>;
|
|
26
28
|
catch(fn: (e: any, context: Context) => O | Promise<O>): TypedFlow<I, O, Context>;
|
|
27
29
|
take(n: number): TypedFlow<I, O, Context>;
|
|
28
30
|
finally(fn: () => void): TypedFlow<I, O, Context>;
|
|
@@ -32,7 +34,7 @@ export declare class Flow<Context = {}> {
|
|
|
32
34
|
static SKIP: symbol;
|
|
33
35
|
private ops;
|
|
34
36
|
private paused?;
|
|
35
|
-
private resume
|
|
37
|
+
private resume?;
|
|
36
38
|
private controller;
|
|
37
39
|
private cancelHandlers;
|
|
38
40
|
private onStartHandlers;
|
|
@@ -56,6 +58,7 @@ export declare class Flow<Context = {}> {
|
|
|
56
58
|
tap(fn: (v: any, context: Context) => void): any;
|
|
57
59
|
filter(cond: Condition<any>): any;
|
|
58
60
|
debounce(ms: number): any;
|
|
61
|
+
delay(ms: number): any;
|
|
59
62
|
leading(ms: number): any;
|
|
60
63
|
throttle(ms: number): any;
|
|
61
64
|
take(n: number): any;
|
|
@@ -73,6 +76,7 @@ export declare class Flow<Context = {}> {
|
|
|
73
76
|
max?: number;
|
|
74
77
|
}): this;
|
|
75
78
|
timeout(ms: number): this;
|
|
79
|
+
tapError(fn: (e: any, context: Context) => void): this;
|
|
76
80
|
catch(fn: (e: any, context: Context) => any): this;
|
|
77
81
|
finally(fn: () => void): this;
|
|
78
82
|
}
|
package/build/index.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var t=require("react");const r=(t,r)=>new Promise((e
|
|
1
|
+
"use strict";var t=require("react");const r=(t,r)=>new Promise((s,e)=>{const n=setTimeout(s,t);r.addEventListener("abort",()=>{clearTimeout(n),e("aborted")})});const s=Symbol("FLOW_HANDLED");class e{constructor(t={}){this.ctx=t,this.ops=[],this.controller=null,this.cancelHandlers=[],this.onStartHandlers=[],this.onDoneHandlers=[],this.onErrorHandlers=[],this.finallyHandlers=[]}static from(t,r){const s=new e(r);return s.initialInput=t,s}async run(t){const r=void 0!==t?t:this.initialInput;if(void 0===r)throw Error("Flow.run: missing input");this.cancel(),this.controller=new AbortController,this.onStartHandlers.forEach(t=>t());let n=r,i=!1;try{for(const t of this.ops){if(this.controller.signal.aborted)return;await this.waitIfPaused(),n=await t(n,this.ctx,this.controller.signal)}return this.onDoneHandlers.forEach(t=>t()),n}catch(t){if(t===e.SKIP)return void(i=!0);throw(null==t?void 0:t[s])||this.onErrorHandlers.forEach(r=>r(t)),t}finally{i||this.finallyHandlers.forEach(t=>t())}}onStart(t){return this.onStartHandlers.push(t),this}onDone(t){return this.onDoneHandlers.push(t),this}onError(t){return this.onErrorHandlers.push(t),this}onCancel(t){return this.cancelHandlers.push(t),this}cancel(){var t;null===(t=this.controller)||void 0===t||t.abort(),this.controller=null,this.resume&&(this.resume(),this.resume=void 0,this.paused=void 0),this.cancelHandlers.forEach(t=>t())}pause(){return this.paused||(this.paused=new Promise(t=>{this.resume=t})),this}resumeFlow(){return this.resume&&(this.resume(),this.resume=void 0,this.paused=void 0),this}async waitIfPaused(){this.paused&&await this.paused}context(){return this.ctx}step(t){return this.ops.push(t),this}tap(t){return this.step((r,s)=>(t(r,s),r))}filter(t){return this.step(r=>{if(!t(r))throw e.SKIP;return r})}debounce(t){return this.step(async(s,e,n)=>(await r(t,n),s))}delay(t){return this.step(async(s,e,n)=>(await r(t,n),s))}leading(t){let r=!1;return this.step(async s=>{if(r)throw e.SKIP;return r=!0,setTimeout(()=>r=!1,t),s})}throttle(t){return this.leading(t)}take(t){let r=0;return this.step(s=>{if(++r>t)throw e.SKIP;return s})}map(t){return this.step((r,s)=>t(r,s))}switchMap(t){let r=null;return this.step(async(s,e,n)=>(null==r||r.abort(),r=new AbortController,n.addEventListener("abort",()=>null==r?void 0:r.abort()),t(s,e,r.signal)))}exhaustMap(t){let r=!1;return this.step(async(s,n,i)=>{if(r)throw e.SKIP;r=!0;try{return await t(s,n,i)}finally{r=!1}})}distinct(t=Object.is){let r,s=!1;return this.step(n=>{if(s&&t(r,n))throw e.SKIP;return s=!0,r=n,n})}retry(t){const s=this.ops.pop();if(!s)return this;const e="number"==typeof t?{times:t}:t,{times:n,delay:i=0,backoff:o}=e;return this.ops.push(async(t,e,a)=>{let u=0;for(;;)try{return await s(t,e,a)}catch(t){if(u++,u>n)throw t;if(i>0){const t="exponential"===o?i*Math.pow(2,u-1):"linear"===o?i*u:i;await r(t,a)}}}),this}poll(t,s){const e=this.ops.pop();if(!e)return this;const{until:n,max:i}=s||{};return this.ops.push(async(s,o,a)=>{let u,l=0;for(;;){if(u=await e(s,o,a),null==n?void 0:n(u))return u;if(i&&++l>=i)throw Error("poll max reached");await r(t,a)}}),this}timeout(t){const s=this.ops.pop();if(!s)return this;return this.ops.push((e,n,i)=>Promise.race([s(e,n,i),r(t,i).then(()=>{throw Error("timeout")})])),this}tapError(t){const r=this.ops.pop();if(!r)return this;return this.ops.push(async(s,e,n)=>{try{return await r(s,e,n)}catch(r){throw t(r,e),r}}),this}catch(t){const r=this.ops.pop();if(!r)return this;return this.ops.push(async(e,n,i)=>{try{return await r(e,n,i)}catch(r){return r[s]=!0,t(r,n)}}),this}finally(t){return this.finallyHandlers.push(t),this}}e.SKIP=Symbol("FLOW_SKIP"),exports.Flow=e,exports.createFlow=function(t){return new e(t)},exports.sleep=r,exports.useFlow=function(r,s,n){const i=t.useRef(null),o=t.useRef(null!=n?n:{});return o.current=null!=n?n:o.current,t.useEffect(()=>{var t;null===(t=i.current)||void 0===t||t.cancel();const n=e.from(r,o.current),a=s(n);return i.current=a,a.run(),()=>a.cancel()},[r,s]),{cancel:t.useCallback(()=>{var t;null===(t=i.current)||void 0===t||t.cancel()},[]),pause:t.useCallback(()=>{var t;null===(t=i.current)||void 0===t||t.pause()},[]),resume:t.useCallback(()=>{var t;null===(t=i.current)||void 0===t||t.resumeFlow()},[]),flow:i.current}};
|
package/build/index.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ export { Flow } from "./flow-system/Flow";
|
|
|
2
2
|
export type { TypedFlow } from "./flow-system/Flow";
|
|
3
3
|
export { useFlow } from "./flow-system/useFlow";
|
|
4
4
|
export type { Step, Condition, CancelHandler } from "./flow-system/types";
|
|
5
|
-
export
|
|
5
|
+
export * from "./flow-system/utils";
|
package/build/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useRef as t,useEffect as r,useCallback as
|
|
1
|
+
import{useRef as t,useEffect as r,useCallback as s}from"react";const e=(t,r)=>new Promise((s,e)=>{const n=setTimeout(s,t);r.addEventListener("abort",()=>{clearTimeout(n),e("aborted")})});function n(t){return new o(t)}const i=Symbol("FLOW_HANDLED");class o{constructor(t={}){this.ctx=t,this.ops=[],this.controller=null,this.cancelHandlers=[],this.onStartHandlers=[],this.onDoneHandlers=[],this.onErrorHandlers=[],this.finallyHandlers=[]}static from(t,r){const s=new o(r);return s.initialInput=t,s}async run(t){const r=void 0!==t?t:this.initialInput;if(void 0===r)throw Error("Flow.run: missing input");this.cancel(),this.controller=new AbortController,this.onStartHandlers.forEach(t=>t());let s=r,e=!1;try{for(const t of this.ops){if(this.controller.signal.aborted)return;await this.waitIfPaused(),s=await t(s,this.ctx,this.controller.signal)}return this.onDoneHandlers.forEach(t=>t()),s}catch(t){if(t===o.SKIP)return void(e=!0);throw(null==t?void 0:t[i])||this.onErrorHandlers.forEach(r=>r(t)),t}finally{e||this.finallyHandlers.forEach(t=>t())}}onStart(t){return this.onStartHandlers.push(t),this}onDone(t){return this.onDoneHandlers.push(t),this}onError(t){return this.onErrorHandlers.push(t),this}onCancel(t){return this.cancelHandlers.push(t),this}cancel(){var t;null===(t=this.controller)||void 0===t||t.abort(),this.controller=null,this.resume&&(this.resume(),this.resume=void 0,this.paused=void 0),this.cancelHandlers.forEach(t=>t())}pause(){return this.paused||(this.paused=new Promise(t=>{this.resume=t})),this}resumeFlow(){return this.resume&&(this.resume(),this.resume=void 0,this.paused=void 0),this}async waitIfPaused(){this.paused&&await this.paused}context(){return this.ctx}step(t){return this.ops.push(t),this}tap(t){return this.step((r,s)=>(t(r,s),r))}filter(t){return this.step(r=>{if(!t(r))throw o.SKIP;return r})}debounce(t){return this.step(async(r,s,n)=>(await e(t,n),r))}delay(t){return this.step(async(r,s,n)=>(await e(t,n),r))}leading(t){let r=!1;return this.step(async s=>{if(r)throw o.SKIP;return r=!0,setTimeout(()=>r=!1,t),s})}throttle(t){return this.leading(t)}take(t){let r=0;return this.step(s=>{if(++r>t)throw o.SKIP;return s})}map(t){return this.step((r,s)=>t(r,s))}switchMap(t){let r=null;return this.step(async(s,e,n)=>(null==r||r.abort(),r=new AbortController,n.addEventListener("abort",()=>null==r?void 0:r.abort()),t(s,e,r.signal)))}exhaustMap(t){let r=!1;return this.step(async(s,e,n)=>{if(r)throw o.SKIP;r=!0;try{return await t(s,e,n)}finally{r=!1}})}distinct(t=Object.is){let r,s=!1;return this.step(e=>{if(s&&t(r,e))throw o.SKIP;return s=!0,r=e,e})}retry(t){const r=this.ops.pop();if(!r)return this;const s="number"==typeof t?{times:t}:t,{times:n,delay:i=0,backoff:o}=s;return this.ops.push(async(t,s,a)=>{let u=0;for(;;)try{return await r(t,s,a)}catch(t){if(u++,u>n)throw t;if(i>0){const t="exponential"===o?i*Math.pow(2,u-1):"linear"===o?i*u:i;await e(t,a)}}}),this}poll(t,r){const s=this.ops.pop();if(!s)return this;const{until:n,max:i}=r||{};return this.ops.push(async(r,o,a)=>{let u,h=0;for(;;){if(u=await s(r,o,a),null==n?void 0:n(u))return u;if(i&&++h>=i)throw Error("poll max reached");await e(t,a)}}),this}timeout(t){const r=this.ops.pop();if(!r)return this;return this.ops.push((s,n,i)=>Promise.race([r(s,n,i),e(t,i).then(()=>{throw Error("timeout")})])),this}tapError(t){const r=this.ops.pop();if(!r)return this;return this.ops.push(async(s,e,n)=>{try{return await r(s,e,n)}catch(r){throw t(r,e),r}}),this}catch(t){const r=this.ops.pop();if(!r)return this;return this.ops.push(async(s,e,n)=>{try{return await r(s,e,n)}catch(r){return r[i]=!0,t(r,e)}}),this}finally(t){return this.finallyHandlers.push(t),this}}function a(e,n,i){const a=t(null),u=t(null!=i?i:{});u.current=null!=i?i:u.current,r(()=>{var t;null===(t=a.current)||void 0===t||t.cancel();const r=o.from(e,u.current),s=n(r);return a.current=s,s.run(),()=>s.cancel()},[e,n]);return{cancel:s(()=>{var t;null===(t=a.current)||void 0===t||t.cancel()},[]),pause:s(()=>{var t;null===(t=a.current)||void 0===t||t.pause()},[]),resume:s(()=>{var t;null===(t=a.current)||void 0===t||t.resumeFlow()},[]),flow:a.current}}o.SKIP=Symbol("FLOW_SKIP");export{o as Flow,n as createFlow,e as sleep,a as useFlow};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-flow-z",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "A lightweight async flow runtime for orchestrating side effects with explicit control over cancellation, debounce, throttling, and execution order.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Delpi.Kye",
|