rest-pipeline-js 1.3.11 → 1.3.14
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 +214 -152
- package/demo/App.vue +122 -0
- package/demo/index.html +22 -0
- package/demo/main.js +5 -0
- package/demo/style.css +857 -0
- package/demo/views/CacheDemo.vue +599 -0
- package/demo/views/FlightDemo.vue +481 -0
- package/demo/views/ParallelDemo.vue +546 -0
- package/demo/views/RetryDemo.vue +506 -0
- package/package.json +1 -1
- package/tsconfig.cjs.json +1 -0
- package/tsconfig.esm.json +1 -1
- package/tsconfig.json +1 -1
- package/vite.config.js +25 -21
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
rest-pipeline-js
|
|
4
4
|
</h1>
|
|
5
5
|
<img
|
|
6
|
-
src="https://s3.twcstorage.ru/c9a2cc89-780f97fd-311d-4a1a-b86f-c25665c9dc46/images/npm/rest-pipeline-
|
|
6
|
+
src="https://s3.twcstorage.ru/c9a2cc89-780f97fd-311d-4a1a-b86f-c25665c9dc46/images/npm/rest-pipeline-js_v2.webp"
|
|
7
7
|
alt="rest-pipeline-js"
|
|
8
8
|
style="max-width:100%;width:auto;height:300px;border-radius:12px"
|
|
9
9
|
/>
|
|
@@ -84,7 +84,7 @@ npm install react@>=19 react-dom@>=19
|
|
|
84
84
|
|
|
85
85
|
## Demo
|
|
86
86
|
|
|
87
|
-
A
|
|
87
|
+
A multi-scenario interactive demo showcasing the key features of `rest-pipeline-js`. All demos use real public REST APIs.
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
90
|
git clone https://github.com/macrulezru/pipeline-js.git
|
|
@@ -93,7 +93,14 @@ npm install
|
|
|
93
93
|
npm run demo:vue
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
Opens at `http://localhost:3000
|
|
96
|
+
Opens at `http://localhost:3000`. The demo app lives in the `demo/` directory.
|
|
97
|
+
|
|
98
|
+
| Demo | What it shows |
|
|
99
|
+
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
100
|
+
| ✈️ **Flight Pipeline** | 4-stage sequential pipeline with `sharedData`, `pauseBefore`/`pauseAfter`, middleware, boarding pass result |
|
|
101
|
+
| 🔀 **Parallel Loading** | `pipe()` fluent builder with `.parallel([])` — 3 sources queried simultaneously, timing breakdown |
|
|
102
|
+
| 🛡️ **Retry & Recovery** | Configurable flaky stage with exponential backoff, event log, `abort()`, pause/resume between stages |
|
|
103
|
+
| ⚡ **Cache & Rate Limit** | `createRestClient()` with cache TTL — see server vs cache timing; rate limiter burst visualization |
|
|
97
104
|
|
|
98
105
|
---
|
|
99
106
|
|
|
@@ -109,7 +116,9 @@ const client = createRestClient({
|
|
|
109
116
|
cache: { enabled: true, ttlMs: 60000 },
|
|
110
117
|
auth: {
|
|
111
118
|
getToken: async () => localStorage.getItem("token") ?? "",
|
|
112
|
-
onUnauthorized: async () => {
|
|
119
|
+
onUnauthorized: async () => {
|
|
120
|
+
/* refresh token */
|
|
121
|
+
},
|
|
113
122
|
},
|
|
114
123
|
});
|
|
115
124
|
|
|
@@ -119,8 +128,15 @@ const res = await client.get("/users/1");
|
|
|
119
128
|
const orchestrator = new PipelineOrchestrator({
|
|
120
129
|
config: {
|
|
121
130
|
stages: [
|
|
122
|
-
{
|
|
123
|
-
|
|
131
|
+
{
|
|
132
|
+
key: "fetchUser",
|
|
133
|
+
request: async ({ sharedData }) =>
|
|
134
|
+
client.get(`/users/${sharedData.userId}`),
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
key: "processData",
|
|
138
|
+
request: async ({ prev }) => ({ ...prev.data, processed: true }),
|
|
139
|
+
},
|
|
124
140
|
],
|
|
125
141
|
},
|
|
126
142
|
sharedData: { userId: 42 },
|
|
@@ -142,43 +158,43 @@ Creates a REST client with advanced HTTP features.
|
|
|
142
158
|
|
|
143
159
|
### Methods
|
|
144
160
|
|
|
145
|
-
| Method
|
|
146
|
-
|
|
147
|
-
| `get(url, config?)`
|
|
148
|
-
| `post(url, data?, config?)`
|
|
149
|
-
| `put(url, data?, config?)`
|
|
150
|
-
| `patch(url, data?, config?)`
|
|
151
|
-
| `delete(url, config?)`
|
|
152
|
-
| `request(url, config?)`
|
|
153
|
-
| `cancellableRequest(key, url, config?)` | Request cancellable by key
|
|
154
|
-
| `cancelRequest(key)`
|
|
155
|
-
| `clearCache()`
|
|
161
|
+
| Method | Description |
|
|
162
|
+
| --------------------------------------- | ---------------------------------- |
|
|
163
|
+
| `get(url, config?)` | GET request |
|
|
164
|
+
| `post(url, data?, config?)` | POST request |
|
|
165
|
+
| `put(url, data?, config?)` | PUT request |
|
|
166
|
+
| `patch(url, data?, config?)` | PATCH request |
|
|
167
|
+
| `delete(url, config?)` | DELETE request |
|
|
168
|
+
| `request(url, config?)` | Generic request |
|
|
169
|
+
| `cancellableRequest(key, url, config?)` | Request cancellable by key |
|
|
170
|
+
| `cancelRequest(key)` | Cancel request by key |
|
|
171
|
+
| `clearCache()` | Clear this client's response cache |
|
|
156
172
|
|
|
157
173
|
### HttpConfig options
|
|
158
174
|
|
|
159
|
-
| Option
|
|
160
|
-
|
|
161
|
-
| `baseURL`
|
|
162
|
-
| `timeout`
|
|
163
|
-
| `headers`
|
|
164
|
-
| `withCredentials`
|
|
165
|
-
| `retry.attempts`
|
|
166
|
-
| `retry.delayMs`
|
|
167
|
-
| `retry.backoffMultiplier`
|
|
168
|
-
| `retry.retriableStatus`
|
|
169
|
-
| `retry.maxRetryAfterMs`
|
|
170
|
-
| `cache.enabled`
|
|
171
|
-
| `cache.ttlMs`
|
|
172
|
-
| `rateLimit.maxConcurrent`
|
|
173
|
-
| `rateLimit.maxRequestsPerInterval` | Max requests per time window
|
|
174
|
-
| `rateLimit.intervalMs`
|
|
175
|
-
| `metrics.onRequestStart`
|
|
176
|
-
| `metrics.onRequestEnd`
|
|
177
|
-
| `auth.getToken`
|
|
178
|
-
| `auth.onUnauthorized`
|
|
179
|
-
| `sanitizeHeaders`
|
|
180
|
-
| `sensitiveHeaders`
|
|
181
|
-
| `adapter`
|
|
175
|
+
| Option | Description |
|
|
176
|
+
| ---------------------------------- | -------------------------------------------------------------------------------- |
|
|
177
|
+
| `baseURL` | Base URL for all requests |
|
|
178
|
+
| `timeout` | Request timeout in ms |
|
|
179
|
+
| `headers` | Default headers |
|
|
180
|
+
| `withCredentials` | Include cookies |
|
|
181
|
+
| `retry.attempts` | Number of retry attempts |
|
|
182
|
+
| `retry.delayMs` | Base delay between retries in ms |
|
|
183
|
+
| `retry.backoffMultiplier` | Exponential backoff multiplier |
|
|
184
|
+
| `retry.retriableStatus` | HTTP status codes eligible for retry (e.g. `[429, 500, 503]`) |
|
|
185
|
+
| `retry.maxRetryAfterMs` | Max wait from `Retry-After` header in ms (default: `60000`) |
|
|
186
|
+
| `cache.enabled` | Enable response caching for GET requests |
|
|
187
|
+
| `cache.ttlMs` | Cache TTL in ms |
|
|
188
|
+
| `rateLimit.maxConcurrent` | Max simultaneous requests |
|
|
189
|
+
| `rateLimit.maxRequestsPerInterval` | Max requests per time window |
|
|
190
|
+
| `rateLimit.intervalMs` | Time window size in ms |
|
|
191
|
+
| `metrics.onRequestStart` | Callback on request start |
|
|
192
|
+
| `metrics.onRequestEnd` | Callback on request end (includes duration and bytes) |
|
|
193
|
+
| `auth.getToken` | Async function returning a Bearer token (called before every request) |
|
|
194
|
+
| `auth.onUnauthorized` | Optional async callback on 401 — refresh the token here; request is retried once |
|
|
195
|
+
| `sanitizeHeaders` | Mask sensitive headers in metrics callbacks (default: `false`) |
|
|
196
|
+
| `sensitiveHeaders` | Additional headers to mask (extends `DEFAULT_SENSITIVE_HEADERS`) |
|
|
197
|
+
| `adapter` | Custom HTTP adapter (e.g. native `fetch`) — replaces built-in axios |
|
|
182
198
|
|
|
183
199
|
### Per-request cache override
|
|
184
200
|
|
|
@@ -208,7 +224,9 @@ const client = createRestClient({
|
|
|
208
224
|
rateLimit: { maxConcurrent: 3, maxRequestsPerInterval: 10, intervalMs: 1000 },
|
|
209
225
|
auth: {
|
|
210
226
|
getToken: async () => localStorage.getItem("token") ?? "",
|
|
211
|
-
onUnauthorized: async () => {
|
|
227
|
+
onUnauthorized: async () => {
|
|
228
|
+
/* refresh token here */
|
|
229
|
+
},
|
|
212
230
|
},
|
|
213
231
|
sanitizeHeaders: true,
|
|
214
232
|
});
|
|
@@ -220,7 +238,9 @@ console.log(res.data);
|
|
|
220
238
|
await client.patch("/users/1", { name: "Alice" });
|
|
221
239
|
|
|
222
240
|
// Cancellable request
|
|
223
|
-
const req = client.cancellableRequest("my-key", "/search", {
|
|
241
|
+
const req = client.cancellableRequest("my-key", "/search", {
|
|
242
|
+
params: { q: "foo" },
|
|
243
|
+
});
|
|
224
244
|
// Cancel it any time:
|
|
225
245
|
client.cancelRequest("my-key");
|
|
226
246
|
```
|
|
@@ -263,8 +283,8 @@ import { createRestClient, DEFAULT_SENSITIVE_HEADERS } from "rest-pipeline-js";
|
|
|
263
283
|
|
|
264
284
|
const client = createRestClient({
|
|
265
285
|
baseURL: "https://api.example.com",
|
|
266
|
-
sanitizeHeaders: true,
|
|
267
|
-
sensitiveHeaders: ["x-internal-secret"],
|
|
286
|
+
sanitizeHeaders: true, // opt-in — disabled by default
|
|
287
|
+
sensitiveHeaders: ["x-internal-secret"], // extend the default list
|
|
268
288
|
metrics: {
|
|
269
289
|
onRequestStart: (info) => {
|
|
270
290
|
// info.requestHeaders — sensitive values replaced with "REDACTED"
|
|
@@ -302,7 +322,7 @@ const executor = new RequestExecutor({
|
|
|
302
322
|
delayMs: 500,
|
|
303
323
|
backoffMultiplier: 2,
|
|
304
324
|
retriableStatus: [429, 500, 502, 503],
|
|
305
|
-
maxRetryAfterMs: 30000,
|
|
325
|
+
maxRetryAfterMs: 30000, // cap Retry-After at 30 s
|
|
306
326
|
},
|
|
307
327
|
});
|
|
308
328
|
|
|
@@ -331,42 +351,42 @@ new PipelineOrchestrator({
|
|
|
331
351
|
|
|
332
352
|
### Methods
|
|
333
353
|
|
|
334
|
-
| Method
|
|
335
|
-
|
|
336
|
-
| `run(onStepPause?, externalSignal?)`
|
|
337
|
-
| `rerunStep(stepKey, options?)`
|
|
338
|
-
| `abort()`
|
|
339
|
-
| `isAborted()`
|
|
340
|
-
| `pause()`
|
|
341
|
-
| `resume()`
|
|
342
|
-
| `isPaused()`
|
|
343
|
-
| `exportState()`
|
|
344
|
-
| `importState(state)`
|
|
345
|
-
| `getStageResults()`
|
|
346
|
-
| `destroy()`
|
|
347
|
-
| `subscribeProgress(listener)`
|
|
348
|
-
| `subscribeStageResults(listener)`
|
|
349
|
-
| `subscribeStepProgress(stepKey, listener)` | Subscribe to a specific stage's progress
|
|
350
|
-
| `on(eventName, handler)`
|
|
351
|
-
| `onStepStart/Finish/Error(handler)`
|
|
352
|
-
| `getProgress()`
|
|
353
|
-
| `getLogs()`
|
|
354
|
-
| `clearStageResults()`
|
|
354
|
+
| Method | Description |
|
|
355
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------- |
|
|
356
|
+
| `run(onStepPause?, externalSignal?)` | Execute all stages. Returns `{ stageResults, success }` |
|
|
357
|
+
| `rerunStep(stepKey, options?)` | Re-execute a single stage (respects condition, before, after, middleware) |
|
|
358
|
+
| `abort()` | Abort pipeline execution (cancels the current HTTP request via AbortSignal) |
|
|
359
|
+
| `isAborted()` | Check if pipeline was aborted |
|
|
360
|
+
| `pause()` | Pause after the current stage completes |
|
|
361
|
+
| `resume()` | Resume a paused pipeline |
|
|
362
|
+
| `isPaused()` | Check if pipeline is paused |
|
|
363
|
+
| `exportState()` | Serialize stageResults and logs to a plain object |
|
|
364
|
+
| `importState(state)` | Restore stageResults and logs from a snapshot |
|
|
365
|
+
| `getStageResults()` | Synchronous snapshot of all stage results |
|
|
366
|
+
| `destroy()` | Run cleanup callbacks from all installed plugins |
|
|
367
|
+
| `subscribeProgress(listener)` | Subscribe to progress updates |
|
|
368
|
+
| `subscribeStageResults(listener)` | Subscribe to stageResults changes |
|
|
369
|
+
| `subscribeStepProgress(stepKey, listener)` | Subscribe to a specific stage's progress |
|
|
370
|
+
| `on(eventName, handler)` | Subscribe to any event (`step:<key>:start\|success\|error\|skipped\|progress`, `log`) |
|
|
371
|
+
| `onStepStart/Finish/Error(handler)` | Subscribe to stage lifecycle events |
|
|
372
|
+
| `getProgress()` | Get current progress snapshot |
|
|
373
|
+
| `getLogs()` | Get all pipeline logs |
|
|
374
|
+
| `clearStageResults()` | Reset results and progress |
|
|
355
375
|
|
|
356
376
|
### Stage parameters (PipelineStageConfig)
|
|
357
377
|
|
|
358
|
-
| Parameter
|
|
359
|
-
|
|
360
|
-
| `key`
|
|
361
|
-
| `request({ prev, allResults, sharedData })`
|
|
362
|
-
| `condition({ prev, allResults, sharedData })` | If returns `false`, stage is skipped with status `"skipped"`
|
|
363
|
-
| `before({ prev, allResults, sharedData })`
|
|
364
|
-
| `after({ result, allResults, sharedData })`
|
|
365
|
-
| `errorHandler({ error, key, sharedData })`
|
|
366
|
-
| `retryCount`
|
|
367
|
-
| `timeoutMs`
|
|
368
|
-
| `pauseBefore`
|
|
369
|
-
| `pauseAfter`
|
|
378
|
+
| Parameter | Description |
|
|
379
|
+
| --------------------------------------------- | ------------------------------------------------------------------------ |
|
|
380
|
+
| `key` | Unique stage identifier |
|
|
381
|
+
| `request({ prev, allResults, sharedData })` | Main stage function — return value becomes the stage result |
|
|
382
|
+
| `condition({ prev, allResults, sharedData })` | If returns `false`, stage is skipped with status `"skipped"` |
|
|
383
|
+
| `before({ prev, allResults, sharedData })` | Pre-processing hook — returned value replaces `prev` passed to `request` |
|
|
384
|
+
| `after({ result, allResults, sharedData })` | Post-processing hook — returned value replaces the stage result |
|
|
385
|
+
| `errorHandler({ error, key, sharedData })` | Per-stage error handler |
|
|
386
|
+
| `retryCount` | Override retry count for this stage |
|
|
387
|
+
| `timeoutMs` | Override timeout for this stage |
|
|
388
|
+
| `pauseBefore` | Delay in ms before executing `request` |
|
|
389
|
+
| `pauseAfter` | Delay in ms after executing `request` |
|
|
370
390
|
|
|
371
391
|
### Stage execution flow
|
|
372
392
|
|
|
@@ -418,8 +438,10 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
418
438
|
],
|
|
419
439
|
middleware: {
|
|
420
440
|
beforeEach: ({ stage }) => console.log("Starting:", stage.key),
|
|
421
|
-
afterEach:
|
|
422
|
-
|
|
441
|
+
afterEach: ({ stage, result }) =>
|
|
442
|
+
console.log("Done:", stage.key, result.data),
|
|
443
|
+
onError: ({ stage, error }) =>
|
|
444
|
+
console.error("Error in", stage.key, error),
|
|
423
445
|
},
|
|
424
446
|
},
|
|
425
447
|
httpConfig: {
|
|
@@ -432,7 +454,12 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
432
454
|
});
|
|
433
455
|
|
|
434
456
|
orchestrator.subscribeProgress((progress) => {
|
|
435
|
-
console.log(
|
|
457
|
+
console.log(
|
|
458
|
+
"Stage:",
|
|
459
|
+
progress.currentStage,
|
|
460
|
+
"Statuses:",
|
|
461
|
+
progress.stageStatuses,
|
|
462
|
+
);
|
|
436
463
|
});
|
|
437
464
|
|
|
438
465
|
orchestrator.on("step:fetchUser:success", (payload) => {
|
|
@@ -461,7 +488,7 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
461
488
|
{
|
|
462
489
|
key: "load-data",
|
|
463
490
|
parallel: [
|
|
464
|
-
{ key: "loadUsers",
|
|
491
|
+
{ key: "loadUsers", request: async () => fetchUsers() },
|
|
465
492
|
{ key: "loadProducts", request: async () => fetchProducts() },
|
|
466
493
|
{ key: "loadSettings", request: async () => fetchSettings() },
|
|
467
494
|
],
|
|
@@ -488,7 +515,9 @@ Apply hooks to every stage without modifying individual stage configs:
|
|
|
488
515
|
```js
|
|
489
516
|
const orchestrator = new PipelineOrchestrator({
|
|
490
517
|
config: {
|
|
491
|
-
stages: [
|
|
518
|
+
stages: [
|
|
519
|
+
/* ... */
|
|
520
|
+
],
|
|
492
521
|
middleware: {
|
|
493
522
|
beforeEach: async ({ stage, index, sharedData }) => {
|
|
494
523
|
console.log(`[${index}] Starting: ${stage.key}`);
|
|
@@ -553,7 +582,7 @@ const orchestrator2 = new PipelineOrchestrator({ config });
|
|
|
553
582
|
orchestrator2.importState(saved);
|
|
554
583
|
|
|
555
584
|
console.log(orchestrator2.getProgress()); // restored progress
|
|
556
|
-
console.log(orchestrator2.getLogs());
|
|
585
|
+
console.log(orchestrator2.getLogs()); // restored logs (timestamps as Date objects)
|
|
557
586
|
```
|
|
558
587
|
|
|
559
588
|
`exportState()` returns `{ stageResults, logs }` — a plain JSON-serializable object. Timestamps in logs are stored as ISO strings and restored as `Date` objects on `importState`.
|
|
@@ -567,7 +596,9 @@ Observe pipeline execution without modifying stage logic:
|
|
|
567
596
|
```js
|
|
568
597
|
const orchestrator = new PipelineOrchestrator({
|
|
569
598
|
config: {
|
|
570
|
-
stages: [
|
|
599
|
+
stages: [
|
|
600
|
+
/* ... */
|
|
601
|
+
],
|
|
571
602
|
metrics: {
|
|
572
603
|
onPipelineStart: ({ timestamp }) => {
|
|
573
604
|
console.log("Pipeline started at", new Date(timestamp).toISOString());
|
|
@@ -583,11 +614,11 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
583
614
|
});
|
|
584
615
|
```
|
|
585
616
|
|
|
586
|
-
| Callback
|
|
587
|
-
|
|
588
|
-
| `onPipelineStart` | `{ timestamp }`
|
|
589
|
-
| `onPipelineEnd`
|
|
590
|
-
| `onStepDuration`
|
|
617
|
+
| Callback | Receives | Description |
|
|
618
|
+
| ----------------- | --------------------------------------- | --------------------------------- |
|
|
619
|
+
| `onPipelineStart` | `{ timestamp }` | Fires at the beginning of `run()` |
|
|
620
|
+
| `onPipelineEnd` | `{ durationMs, success, stageResults }` | Fires when `run()` completes |
|
|
621
|
+
| `onStepDuration` | `{ stepKey, durationMs, status }` | Fires after every executed step |
|
|
591
622
|
|
|
592
623
|
---
|
|
593
624
|
|
|
@@ -601,14 +632,15 @@ import { createPipeline } from "rest-pipeline-js";
|
|
|
601
632
|
const orchestrator = createPipeline(
|
|
602
633
|
[
|
|
603
634
|
{ key: "fetchUser", request: async () => fetchUser() },
|
|
604
|
-
{ key: "process",
|
|
635
|
+
{ key: "process", request: async ({ prev }) => process(prev) },
|
|
605
636
|
],
|
|
606
637
|
{
|
|
607
|
-
httpConfig:
|
|
608
|
-
sharedData:
|
|
638
|
+
httpConfig: { baseURL: "https://api.example.com" },
|
|
639
|
+
sharedData: { userId: 42 },
|
|
609
640
|
pipelineOptions: { continueOnError: false },
|
|
610
641
|
metrics: {
|
|
611
|
-
onStepDuration: ({ stepKey, durationMs }) =>
|
|
642
|
+
onStepDuration: ({ stepKey, durationMs }) =>
|
|
643
|
+
console.log(stepKey, durationMs),
|
|
612
644
|
},
|
|
613
645
|
},
|
|
614
646
|
);
|
|
@@ -620,28 +652,30 @@ const orchestrator = createPipeline(
|
|
|
620
652
|
import { pipe } from "rest-pipeline-js";
|
|
621
653
|
|
|
622
654
|
const orchestrator = pipe()
|
|
623
|
-
.step({ key: "auth",
|
|
655
|
+
.step({ key: "auth", request: async () => getToken() })
|
|
624
656
|
.step({ key: "fetchUser", request: async ({ prev }) => fetchUser(prev) })
|
|
625
657
|
.parallel([
|
|
626
|
-
{ key: "loadPosts",
|
|
658
|
+
{ key: "loadPosts", request: async () => fetchPosts() },
|
|
627
659
|
{ key: "loadNotifs", request: async () => fetchNotifications() },
|
|
628
660
|
])
|
|
629
661
|
.stream({
|
|
630
662
|
key: "liveUpdates",
|
|
631
|
-
stream: async function* () {
|
|
663
|
+
stream: async function* () {
|
|
664
|
+
yield* subscribe("/events");
|
|
665
|
+
},
|
|
632
666
|
onChunk: (chunk) => updateUI(chunk),
|
|
633
667
|
})
|
|
634
668
|
.build({ httpConfig: { baseURL: "https://api.example.com" } });
|
|
635
669
|
```
|
|
636
670
|
|
|
637
|
-
| Builder method
|
|
638
|
-
|
|
639
|
-
| `.step(stage)`
|
|
640
|
-
| `.parallel(stages, options?)` | Add a parallel group (`key` auto-generated if omitted)
|
|
641
|
-
| `.subPipeline(item)`
|
|
642
|
-
| `.stream(stage)`
|
|
643
|
-
| `.build(options?)`
|
|
644
|
-
| `.toConfig(options?)`
|
|
671
|
+
| Builder method | Description |
|
|
672
|
+
| ----------------------------- | -------------------------------------------------------- |
|
|
673
|
+
| `.step(stage)` | Add a sequential stage |
|
|
674
|
+
| `.parallel(stages, options?)` | Add a parallel group (`key` auto-generated if omitted) |
|
|
675
|
+
| `.subPipeline(item)` | Embed a sub-pipeline as a stage |
|
|
676
|
+
| `.stream(stage)` | Add a stream stage (AsyncIterable) |
|
|
677
|
+
| `.build(options?)` | Create and return a `PipelineOrchestrator` |
|
|
678
|
+
| `.toConfig(options?)` | Return `PipelineConfig` without creating an orchestrator |
|
|
645
679
|
|
|
646
680
|
---
|
|
647
681
|
|
|
@@ -655,8 +689,8 @@ import { validatePipelineConfig } from "rest-pipeline-js";
|
|
|
655
689
|
const { valid, errors } = validatePipelineConfig({
|
|
656
690
|
stages: [
|
|
657
691
|
{ key: "step1", request: async () => data },
|
|
658
|
-
{ key: "step1", request: async () => other },
|
|
659
|
-
{ key: "",
|
|
692
|
+
{ key: "step1", request: async () => other }, // duplicate!
|
|
693
|
+
{ key: "", request: async () => other }, // empty key!
|
|
660
694
|
],
|
|
661
695
|
});
|
|
662
696
|
|
|
@@ -678,15 +712,18 @@ const loggingPlugin = {
|
|
|
678
712
|
install(orchestrator) {
|
|
679
713
|
const off = orchestrator.on("log", (event) => {
|
|
680
714
|
if (event.type === "step:success") console.log("✓", event.stepKey);
|
|
681
|
-
if (event.type === "step:error")
|
|
715
|
+
if (event.type === "step:error")
|
|
716
|
+
console.error("✗", event.stepKey, event.error);
|
|
682
717
|
});
|
|
683
|
-
return () => off();
|
|
718
|
+
return () => off(); // cleanup on orchestrator.destroy()
|
|
684
719
|
},
|
|
685
720
|
};
|
|
686
721
|
|
|
687
722
|
const orchestrator = new PipelineOrchestrator({
|
|
688
723
|
config: {
|
|
689
|
-
stages: [
|
|
724
|
+
stages: [
|
|
725
|
+
/* ... */
|
|
726
|
+
],
|
|
690
727
|
options: { plugins: [loggingPlugin, analyticsPlugin] },
|
|
691
728
|
},
|
|
692
729
|
});
|
|
@@ -715,7 +752,9 @@ const localStorageAdapter = {
|
|
|
715
752
|
|
|
716
753
|
const orchestrator = new PipelineOrchestrator({
|
|
717
754
|
config: {
|
|
718
|
-
stages: [
|
|
755
|
+
stages: [
|
|
756
|
+
/* ... */
|
|
757
|
+
],
|
|
719
758
|
options: { persistAdapter: localStorageAdapter },
|
|
720
759
|
},
|
|
721
760
|
});
|
|
@@ -776,16 +815,20 @@ Replace the built-in axios client with any HTTP implementation:
|
|
|
776
815
|
```js
|
|
777
816
|
const fetchAdapter = {
|
|
778
817
|
async request(config) {
|
|
779
|
-
const url
|
|
780
|
-
const res
|
|
781
|
-
method:
|
|
782
|
-
body:
|
|
818
|
+
const url = `${config.baseURL ?? ""}${config.url ?? ""}`;
|
|
819
|
+
const res = await fetch(url, {
|
|
820
|
+
method: config.method ?? "GET",
|
|
821
|
+
body: config.data ? JSON.stringify(config.data) : undefined,
|
|
783
822
|
headers: { "Content-Type": "application/json", ...config.headers },
|
|
784
|
-
signal:
|
|
823
|
+
signal: config.signal,
|
|
785
824
|
});
|
|
786
825
|
const data = await res.json();
|
|
787
|
-
return {
|
|
788
|
-
|
|
826
|
+
return {
|
|
827
|
+
data,
|
|
828
|
+
status: res.status,
|
|
829
|
+
statusText: res.statusText,
|
|
830
|
+
headers: Object.fromEntries(res.headers.entries()),
|
|
831
|
+
};
|
|
789
832
|
},
|
|
790
833
|
};
|
|
791
834
|
|
|
@@ -817,7 +860,13 @@ import {
|
|
|
817
860
|
usePipelineRunVue,
|
|
818
861
|
} from "rest-pipeline-js/vue";
|
|
819
862
|
|
|
820
|
-
const orchestrator = new PipelineOrchestrator({
|
|
863
|
+
const orchestrator = new PipelineOrchestrator({
|
|
864
|
+
config: {
|
|
865
|
+
stages: [
|
|
866
|
+
/* ... */
|
|
867
|
+
],
|
|
868
|
+
},
|
|
869
|
+
});
|
|
821
870
|
const progress = usePipelineProgressVue(orchestrator);
|
|
822
871
|
const { run, running, result, error, abort, pause, resume, rerunStep } =
|
|
823
872
|
usePipelineRunVue(orchestrator);
|
|
@@ -838,15 +887,15 @@ const { run, running, result, error, abort, pause, resume, rerunStep } =
|
|
|
838
887
|
|
|
839
888
|
Composables (import from `rest-pipeline-js/vue`):
|
|
840
889
|
|
|
841
|
-
| Composable
|
|
842
|
-
|
|
843
|
-
| `usePipelineProgressVue(orchestrator)`
|
|
844
|
-
| `usePipelineRunVue(orchestrator)`
|
|
845
|
-
| `usePipelineStepEventVue(orchestrator, stepKey, eventType)` | `Ref<any>`
|
|
846
|
-
| `usePipelineLogsVue(orchestrator)`
|
|
847
|
-
| `useRerunPipelineStepVue(orchestrator)`
|
|
848
|
-
| `useRestClientVue(config)`
|
|
849
|
-
| `usePipelineStageResultVue(orchestrator, stepKey)`
|
|
890
|
+
| Composable | Returns | Description |
|
|
891
|
+
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | -------------------------------------- |
|
|
892
|
+
| `usePipelineProgressVue(orchestrator)` | `Ref<PipelineProgress>` | Reactive progress |
|
|
893
|
+
| `usePipelineRunVue(orchestrator)` | `{ run, running, result, error, stageResults, abort, pause, resume, rerunStep, clearStageResults }` | Run pipeline and get reactive state |
|
|
894
|
+
| `usePipelineStepEventVue(orchestrator, stepKey, eventType)` | `Ref<any>` | Last payload for a specific step event |
|
|
895
|
+
| `usePipelineLogsVue(orchestrator)` | `Ref<log[]>` | Reactive logs |
|
|
896
|
+
| `useRerunPipelineStepVue(orchestrator)` | `function` | Bound `rerunStep` |
|
|
897
|
+
| `useRestClientVue(config)` | `ComputedRef<RestClient>` | Reactive REST client |
|
|
898
|
+
| `usePipelineStageResultVue(orchestrator, stepKey)` | `Ref<PipelineStepResult \| null>` | Reactive result of a single stage |
|
|
850
899
|
|
|
851
900
|
---
|
|
852
901
|
|
|
@@ -860,7 +909,13 @@ import {
|
|
|
860
909
|
usePipelineRunReact,
|
|
861
910
|
} from "rest-pipeline-js/react";
|
|
862
911
|
|
|
863
|
-
const orchestrator = new PipelineOrchestrator({
|
|
912
|
+
const orchestrator = new PipelineOrchestrator({
|
|
913
|
+
config: {
|
|
914
|
+
stages: [
|
|
915
|
+
/* ... */
|
|
916
|
+
],
|
|
917
|
+
},
|
|
918
|
+
});
|
|
864
919
|
|
|
865
920
|
export function PipelineComponent() {
|
|
866
921
|
const progress = usePipelineProgressReact(orchestrator);
|
|
@@ -870,12 +925,16 @@ export function PipelineComponent() {
|
|
|
870
925
|
return (
|
|
871
926
|
<div>
|
|
872
927
|
<div>Current stage: {progress.currentStage}</div>
|
|
873
|
-
<button onClick={() => run()} disabled={running}>
|
|
874
|
-
|
|
928
|
+
<button onClick={() => run()} disabled={running}>
|
|
929
|
+
Start
|
|
930
|
+
</button>
|
|
931
|
+
<button onClick={() => abort()} disabled={!running}>
|
|
932
|
+
Abort
|
|
933
|
+
</button>
|
|
875
934
|
<button onClick={() => pause()}>Pause</button>
|
|
876
935
|
<button onClick={() => resume()}>Resume</button>
|
|
877
936
|
{result && <div>Done: {JSON.stringify(result)}</div>}
|
|
878
|
-
{error
|
|
937
|
+
{error && <div>Error: {error.message}</div>}
|
|
879
938
|
</div>
|
|
880
939
|
);
|
|
881
940
|
}
|
|
@@ -883,25 +942,25 @@ export function PipelineComponent() {
|
|
|
883
942
|
|
|
884
943
|
Hooks (import from `rest-pipeline-js/react`):
|
|
885
944
|
|
|
886
|
-
| Hook
|
|
887
|
-
|
|
888
|
-
| `usePipelineProgressReact(orchestrator)`
|
|
889
|
-
| `usePipelineRunReact(orchestrator)`
|
|
890
|
-
| `usePipelineStepEventReact(orchestrator, stepKey, eventType)` | `any`
|
|
891
|
-
| `usePipelineLogsReact(orchestrator)`
|
|
892
|
-
| `useRerunPipelineStepReact(orchestrator)`
|
|
893
|
-
| `useRestClientReact(config)`
|
|
894
|
-
| `usePipelineStageResultReact(orchestrator, stepKey)`
|
|
945
|
+
| Hook | Returns | Description |
|
|
946
|
+
| ------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------- |
|
|
947
|
+
| `usePipelineProgressReact(orchestrator)` | `PipelineProgress` | Reactive progress |
|
|
948
|
+
| `usePipelineRunReact(orchestrator)` | `[run, { running, result, error, stageResults, abort, pause, resume, rerunStep }]` | Run pipeline and get state |
|
|
949
|
+
| `usePipelineStepEventReact(orchestrator, stepKey, eventType)` | `any` | Last payload for a specific step event |
|
|
950
|
+
| `usePipelineLogsReact(orchestrator)` | `log[]` | Reactive logs |
|
|
951
|
+
| `useRerunPipelineStepReact(orchestrator)` | `function` | Bound `rerunStep` |
|
|
952
|
+
| `useRestClientReact(config)` | `RestClient` | Memoized REST client |
|
|
953
|
+
| `usePipelineStageResultReact(orchestrator, stepKey)` | `PipelineStepResult \| null` | Result of a single stage |
|
|
895
954
|
|
|
896
955
|
---
|
|
897
956
|
|
|
898
957
|
## Entry points
|
|
899
958
|
|
|
900
|
-
| Entry point
|
|
901
|
-
|
|
902
|
-
| `rest-pipeline-js`
|
|
903
|
-
| `rest-pipeline-js/vue`
|
|
904
|
-
| `rest-pipeline-js/react` | React projects | Core + React hooks
|
|
959
|
+
| Entry point | Use for | Contents |
|
|
960
|
+
| ------------------------ | -------------- | --------------------------------------------------------------------------- |
|
|
961
|
+
| `rest-pipeline-js` | Core only | `PipelineOrchestrator`, `createRestClient`, types, utilities. No Vue/React. |
|
|
962
|
+
| `rest-pipeline-js/vue` | Vue projects | Core + Vue composables |
|
|
963
|
+
| `rest-pipeline-js/react` | React projects | Core + React hooks |
|
|
905
964
|
|
|
906
965
|
```js
|
|
907
966
|
// Core only
|
|
@@ -911,7 +970,10 @@ import { createRestClient, PipelineOrchestrator } from "rest-pipeline-js";
|
|
|
911
970
|
import { PipelineOrchestrator, usePipelineRunVue } from "rest-pipeline-js/vue";
|
|
912
971
|
|
|
913
972
|
// React
|
|
914
|
-
import {
|
|
973
|
+
import {
|
|
974
|
+
PipelineOrchestrator,
|
|
975
|
+
usePipelineRunReact,
|
|
976
|
+
} from "rest-pipeline-js/react";
|
|
915
977
|
```
|
|
916
978
|
|
|
917
979
|
`sideEffects: false` — unused entry points are tree-shaken. `react` / `react-dom` are `peerDependencies`.
|
|
@@ -965,11 +1027,11 @@ rest-pipeline-js
|
|
|
965
1027
|
|
|
966
1028
|
## Bundle size & peer dependencies
|
|
967
1029
|
|
|
968
|
-
| Entry point
|
|
969
|
-
|
|
970
|
-
| `rest-pipeline-js`
|
|
971
|
-
| `rest-pipeline-js/vue`
|
|
972
|
-
| `rest-pipeline-js/react` | `react ^19`, `react-dom ^19` | Core + React hooks
|
|
1030
|
+
| Entry point | Peer deps | Notes |
|
|
1031
|
+
| ------------------------ | ---------------------------- | ---------------------------------------------------------------- |
|
|
1032
|
+
| `rest-pipeline-js` | — | Core — orchestrator, HTTP client, utilities. Depends on `axios`. |
|
|
1033
|
+
| `rest-pipeline-js/vue` | `vue ^3.3` | Core + Vue composables |
|
|
1034
|
+
| `rest-pipeline-js/react` | `react ^19`, `react-dom ^19` | Core + React hooks |
|
|
973
1035
|
|
|
974
1036
|
The package ships as tree-shakeable ESM (`dist/esm/`) and CommonJS (`dist/cjs/`). The `/vue` and `/react` entry points are code-split — importing one does not bundle the other.
|
|
975
1037
|
|