react-pipeline-runner 0.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/LICENSE +7 -0
- package/README.md +179 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tonylus
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# react-pipeline-runner
|
|
2
|
+
|
|
3
|
+
A lightweight React hook for running async actions sequentially with abort support, error handling, and resume capability.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install react-pipeline-runner
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Sequential execution of sync/async actions
|
|
14
|
+
- AbortController support for cancellation
|
|
15
|
+
- Resume from failed step
|
|
16
|
+
- TypeScript-first with intelligent ID inference
|
|
17
|
+
- Discriminated union for type-safe state handling
|
|
18
|
+
- Automatic cleanup on unmount
|
|
19
|
+
- Zero dependencies (except React 18+)
|
|
20
|
+
|
|
21
|
+
## Basic Usage
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { usePipeline } from 'react-pipeline-runner'
|
|
25
|
+
|
|
26
|
+
function MyComponent() {
|
|
27
|
+
const pipeline = usePipeline([
|
|
28
|
+
async (signal) => {
|
|
29
|
+
await fetch('/api/step1', { signal })
|
|
30
|
+
},
|
|
31
|
+
async (signal) => {
|
|
32
|
+
await fetch('/api/step2', { signal })
|
|
33
|
+
},
|
|
34
|
+
async () => {
|
|
35
|
+
console.log('Step 3 - no signal needed')
|
|
36
|
+
},
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div>
|
|
41
|
+
<p>State: {pipeline.state}</p>
|
|
42
|
+
|
|
43
|
+
{pipeline.state === 'idle' && (
|
|
44
|
+
<button onClick={pipeline.start}>Start</button>
|
|
45
|
+
)}
|
|
46
|
+
|
|
47
|
+
{pipeline.state === 'running' && (
|
|
48
|
+
<>
|
|
49
|
+
<p>Running step {pipeline.current.index + 1}...</p>
|
|
50
|
+
<button onClick={pipeline.stop}>Cancel</button>
|
|
51
|
+
</>
|
|
52
|
+
)}
|
|
53
|
+
|
|
54
|
+
{pipeline.state === 'failed' && (
|
|
55
|
+
<>
|
|
56
|
+
<p>Error at step {pipeline.current.index + 1}: {String(pipeline.current.error)}</p>
|
|
57
|
+
<button onClick={pipeline.resume}>Retry</button>
|
|
58
|
+
<button onClick={pipeline.stop}>Abandon</button>
|
|
59
|
+
</>
|
|
60
|
+
)}
|
|
61
|
+
|
|
62
|
+
{pipeline.state === 'completed' && (
|
|
63
|
+
<p>Done!</p>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Steps with IDs
|
|
71
|
+
|
|
72
|
+
You can assign IDs to steps for better tracking. TypeScript will automatically infer the ID types.
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
const pipeline = usePipeline([
|
|
76
|
+
{ id: 'fetch-user', action: async () => fetchUser() },
|
|
77
|
+
{ id: 'validate', action: () => validateData() },
|
|
78
|
+
async () => doSomethingWithoutId(),
|
|
79
|
+
{ id: 'save', action: async () => saveData() },
|
|
80
|
+
])
|
|
81
|
+
|
|
82
|
+
if (pipeline.state === 'running') {
|
|
83
|
+
console.log('Current step:', pipeline.current.id)
|
|
84
|
+
// TypeScript knows: id is 'fetch-user' | 'validate' | 'save' | undefined
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Auto-run
|
|
89
|
+
|
|
90
|
+
Start the pipeline automatically when the component mounts:
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
const pipeline = usePipeline(
|
|
94
|
+
[step1, step2, step3],
|
|
95
|
+
{ autorun: true }
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## API
|
|
100
|
+
|
|
101
|
+
### `usePipeline(steps, options?)`
|
|
102
|
+
|
|
103
|
+
#### Parameters
|
|
104
|
+
|
|
105
|
+
- `steps` - Array of actions. Each action can be:
|
|
106
|
+
- A function: `(signal?: AbortSignal) => Promise<unknown> | unknown`
|
|
107
|
+
- An object: `{ id: string, action: (signal?: AbortSignal) => Promise<unknown> | unknown }`
|
|
108
|
+
- `options` - Optional configuration:
|
|
109
|
+
- `autorun?: boolean` - Start pipeline on mount (default: `false`)
|
|
110
|
+
|
|
111
|
+
#### Returns (Discriminated Union)
|
|
112
|
+
|
|
113
|
+
The hook returns a discriminated union based on `state`:
|
|
114
|
+
|
|
115
|
+
| State | `current` | Description |
|
|
116
|
+
|-------|-----------|-------------|
|
|
117
|
+
| `'idle'` | `undefined` | Not started or stopped |
|
|
118
|
+
| `'running'` | `CurrentStatus` | Executing steps |
|
|
119
|
+
| `'failed'` | `CurrentStatus` | Stopped on error |
|
|
120
|
+
| `'completed'` | `undefined` | All steps done |
|
|
121
|
+
|
|
122
|
+
**Methods:**
|
|
123
|
+
|
|
124
|
+
- `start()` - Start from beginning. Returns `true` if started, `false` if running or failed.
|
|
125
|
+
- `stop()` - Cancel and reset to idle. Returns `true` if was running or failed, `false` otherwise.
|
|
126
|
+
- `resume()` - Retry failed step. Returns `true` if was failed, `false` otherwise.
|
|
127
|
+
|
|
128
|
+
**State transitions:**
|
|
129
|
+
|
|
130
|
+
| State | `start()` | `stop()` | `resume()` |
|
|
131
|
+
|-------|-----------|----------|------------|
|
|
132
|
+
| `idle` | ✅ starts | ❌ false | ❌ false |
|
|
133
|
+
| `running` | ❌ false | ✅ → idle | ❌ false |
|
|
134
|
+
| `failed` | ❌ false | ✅ → idle | ✅ retries |
|
|
135
|
+
| `completed` | ✅ restarts | ❌ false | ❌ false |
|
|
136
|
+
|
|
137
|
+
**CurrentStatus:**
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
{
|
|
141
|
+
index: number // Step index (0-based)
|
|
142
|
+
id: string | undefined // Step ID if provided
|
|
143
|
+
state: 'running' | 'failed'
|
|
144
|
+
error: unknown | undefined // Error if failed
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Type Safety
|
|
149
|
+
|
|
150
|
+
Thanks to discriminated unions, TypeScript narrows the `current` type based on `state`:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
if (pipeline.state === 'failed') {
|
|
154
|
+
// TypeScript knows current is defined!
|
|
155
|
+
console.log(pipeline.current.error) // ✅ No need for && pipeline.current
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (pipeline.state === 'idle') {
|
|
159
|
+
pipeline.current.index // ❌ Compile error - current is undefined
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## AbortController Support
|
|
164
|
+
|
|
165
|
+
Each action receives an optional `AbortSignal`. Use it to make your actions cancellable:
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
async (signal) => {
|
|
169
|
+
// Fetch automatically aborts when signal fires
|
|
170
|
+
const response = await fetch('/api/data', { signal })
|
|
171
|
+
return response.json()
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
When `stop()` is called or the component unmounts, the signal is aborted automatically.
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
ISC
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type PipelineAction = (signal?: AbortSignal) => Promise<unknown> | unknown;
|
|
2
|
+
interface PipelineStepWithId {
|
|
3
|
+
id: string;
|
|
4
|
+
action: PipelineAction;
|
|
5
|
+
}
|
|
6
|
+
type PipelineStep = PipelineAction | PipelineStepWithId;
|
|
7
|
+
interface PipelineOptions {
|
|
8
|
+
autorun?: boolean;
|
|
9
|
+
}
|
|
10
|
+
type PipelineState = 'idle' | 'running' | 'failed' | 'completed';
|
|
11
|
+
type StepState = 'running' | 'failed';
|
|
12
|
+
interface CurrentStatus<TIds extends string | undefined> {
|
|
13
|
+
index: number;
|
|
14
|
+
id: TIds;
|
|
15
|
+
state: StepState;
|
|
16
|
+
error: unknown | undefined;
|
|
17
|
+
}
|
|
18
|
+
interface PipelineMethods {
|
|
19
|
+
start: () => boolean;
|
|
20
|
+
stop: () => boolean;
|
|
21
|
+
resume: () => boolean;
|
|
22
|
+
}
|
|
23
|
+
type PipelineIdle = PipelineMethods & {
|
|
24
|
+
state: 'idle';
|
|
25
|
+
current: undefined;
|
|
26
|
+
};
|
|
27
|
+
type PipelineRunning<TIds extends string | undefined> = PipelineMethods & {
|
|
28
|
+
state: 'running';
|
|
29
|
+
current: CurrentStatus<TIds>;
|
|
30
|
+
};
|
|
31
|
+
type PipelineFailed<TIds extends string | undefined> = PipelineMethods & {
|
|
32
|
+
state: 'failed';
|
|
33
|
+
current: CurrentStatus<TIds>;
|
|
34
|
+
};
|
|
35
|
+
type PipelineCompleted = PipelineMethods & {
|
|
36
|
+
state: 'completed';
|
|
37
|
+
current: undefined;
|
|
38
|
+
};
|
|
39
|
+
type PipelineResult<TIds extends string | undefined> = PipelineIdle | PipelineRunning<TIds> | PipelineFailed<TIds> | PipelineCompleted;
|
|
40
|
+
type ExtractStepId<T> = T extends {
|
|
41
|
+
id: infer Id extends string;
|
|
42
|
+
} ? Id : undefined;
|
|
43
|
+
type ExtractAllIds<T extends readonly PipelineStep[]> = ExtractStepId<T[number]>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Runs a list of actions sequentially with support for abort, resume, and error handling.
|
|
47
|
+
*/
|
|
48
|
+
declare function usePipeline<const T extends readonly PipelineStep[]>(steps: T, options?: PipelineOptions): PipelineResult<ExtractAllIds<T>>;
|
|
49
|
+
|
|
50
|
+
export { type CurrentStatus, type PipelineAction, type PipelineOptions, type PipelineResult, type PipelineState, type PipelineStep, type PipelineStepWithId, type StepState, usePipeline };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import*as t from'react';function R(e){let[p,f]=t.useState(e),n=t.useRef(e),r=t.useCallback(a=>{n.current=a,f(a);},[]);return [p,n,r]}function P(e){return typeof e=="object"&&e!==null&&"action"in e}function A(e){return P(e)?e.action:e}function E(e){if(P(e))return e.id}function h(e,p){let[f,n,r]=R("idle"),[a,c]=t.useState(void 0),d=t.useRef(0),u=t.useRef(null),o=t.useCallback(async x=>{r("running"),u.current=new AbortController;let l=u.current.signal;for(let i=x;i<e.length;i++){if(l.aborted)return;let y=e[i],I=A(y),S=E(y);d.current=i,c({index:i,id:S,state:"running",error:void 0});try{await I(l);}catch(C){if(l.aborted)return;r("failed"),c({index:i,id:S,state:"failed",error:C});return}}l.aborted||(r("completed"),c(void 0));},[e]),T=t.useCallback(()=>n.current==="running"||n.current==="failed"?false:(o(0),true),[o]),b=t.useCallback(()=>n.current!=="running"&&n.current!=="failed"?false:(u.current?.abort(),u.current=null,r("idle"),c(void 0),true),[]),m=t.useCallback(()=>n.current!=="failed"?false:(o(d.current),true),[o]);return t.useEffect(()=>(p?.autorun&&T(),()=>{u.current?.abort();}),[]),{state:f,current:a,start:T,stop:b,resume:m}}export{h as usePipeline};//# sourceMappingURL=index.js.map
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/usePipeline.ts"],"names":["useStateRef","initialValue","state","setState","s","ref","setValue","value","isStepWithId","step","getStepAction","getStepId","usePipeline","steps","options","stateRef","current","setCurrent","currentIndexRef","abortControllerRef","runFromIndex","startIndex","signal","action","id","error","start","stop","resume"],"mappings":"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","file":"index.js","sourcesContent":["import * as React from 'react'\n\n//\n\n/**\n * Combines useState and useRef - state for React reactivity, ref for immediate access in async callbacks.\n */\nexport function useStateRef<T>(initialValue: T) {\n const [state, setState] = React.useState(initialValue);\n const ref = React.useRef(initialValue);\n\n const setValue = React.useCallback((value: T) => {\n ref.current = value;\n setState(value);\n }, []);\n\n return [state, ref, setValue] as const;\n}\n","import * as React from 'react'\n\nimport type * as Types from './types'\nimport * as Utils from './utils'\n\n//\n\nfunction isStepWithId(step: Types.PipelineStep): step is Types.PipelineStepWithId {\n return typeof step === 'object' && step !== null && 'action' in step;\n}\n\nfunction getStepAction(step: Types.PipelineStep): Types.PipelineAction {\n if (isStepWithId(step))\n return step.action;\n\n return step;\n}\n\nfunction getStepId<T extends Types.PipelineStep>(step: T): Types.ExtractStepId<T> {\n if (isStepWithId(step))\n return step.id as Types.ExtractStepId<T>;\n\n return undefined as Types.ExtractStepId<T>;\n}\n\n//\n\n/**\n * Runs a list of actions sequentially with support for abort, resume, and error handling.\n */\nexport function usePipeline<const T extends readonly Types.PipelineStep[]>(\n steps: T,\n options?: Types.PipelineOptions\n): Types.PipelineResult<Types.ExtractAllIds<T>> {\n type TIds = Types.ExtractAllIds<T>\n\n const [state, stateRef, setState] = Utils.useStateRef<Types.PipelineState>('idle');\n const [current, setCurrent] = React.useState<Types.CurrentStatus<TIds> | undefined>(undefined);\n\n const currentIndexRef = React.useRef<number>(0);\n const abortControllerRef = React.useRef<AbortController | null>(null);\n\n //\n\n const runFromIndex = React.useCallback(async (startIndex: number) => {\n setState('running');\n\n abortControllerRef.current = new AbortController();\n const signal = abortControllerRef.current.signal;\n\n for (let i = startIndex; i < steps.length; i++) {\n if (signal.aborted)\n return;\n\n const step = steps[i];\n const action = getStepAction(step);\n const id = getStepId(step) as TIds;\n\n currentIndexRef.current = i;\n setCurrent({ index: i, id, state: 'running', error: undefined });\n\n try {\n await action(signal);\n }\n catch (error) {\n if (signal.aborted)\n return;\n\n setState('failed');\n setCurrent({ index: i, id, state: 'failed', error });\n return;\n }\n }\n\n if (signal.aborted)\n return;\n\n setState('completed');\n setCurrent(undefined);\n }, [steps]);\n\n //\n\n const start = React.useCallback(() => {\n if (stateRef.current === 'running' || stateRef.current === 'failed')\n return false;\n\n runFromIndex(0);\n return true;\n }, [runFromIndex]);\n\n const stop = React.useCallback(() => {\n if (stateRef.current !== 'running' && stateRef.current !== 'failed')\n return false;\n\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n\n setState('idle');\n setCurrent(undefined);\n\n return true;\n }, []);\n\n const resume = React.useCallback(() => {\n if (stateRef.current !== 'failed')\n return false;\n\n runFromIndex(currentIndexRef.current);\n return true;\n }, [runFromIndex]);\n\n //\n\n React.useEffect(() => {\n if (options?.autorun)\n start();\n\n return () => {\n abortControllerRef.current?.abort();\n };\n }, []);\n\n //\n\n return { state, current, start, stop, resume } as Types.PipelineResult<TIds>;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-pipeline-runner",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight React hook for running async actions sequentially with abort support and resume capability",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"hook",
|
|
8
|
+
"pipeline",
|
|
9
|
+
"async",
|
|
10
|
+
"sequential",
|
|
11
|
+
"queue",
|
|
12
|
+
"runner"
|
|
13
|
+
],
|
|
14
|
+
"author": "tonylus",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"repository": "https://github.com/TonylusMark1/react-pipeline-runner",
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"module": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"prepublishOnly": "npm run build"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@testing-library/react": "^16.3.2",
|
|
41
|
+
"@types/node": "^25.3.2",
|
|
42
|
+
"@types/react": "^19.0.0",
|
|
43
|
+
"happy-dom": "^20.8.4",
|
|
44
|
+
"react": "^19.2.4",
|
|
45
|
+
"tsup": "^8.5.1",
|
|
46
|
+
"typescript": "^5.5.3",
|
|
47
|
+
"vitest": "^4.1.0"
|
|
48
|
+
}
|
|
49
|
+
}
|