rest-pipeline-js 1.3.12 → 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 +211 -156
- package/package.json +1 -1
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
|
/>
|
|
@@ -95,12 +95,12 @@ npm run demo:vue
|
|
|
95
95
|
|
|
96
96
|
Opens at `http://localhost:3000`. The demo app lives in the `demo/` directory.
|
|
97
97
|
|
|
98
|
-
| Demo
|
|
99
|
-
|
|
100
|
-
| ✈️ **Flight Pipeline**
|
|
101
|
-
| 🔀 **Parallel Loading**
|
|
102
|
-
| 🛡️ **Retry & Recovery**
|
|
103
|
-
| ⚡ **Cache & Rate Limit** | `createRestClient()` with cache TTL — see server vs cache timing; rate limiter burst visualization
|
|
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 |
|
|
104
104
|
|
|
105
105
|
---
|
|
106
106
|
|
|
@@ -116,7 +116,9 @@ const client = createRestClient({
|
|
|
116
116
|
cache: { enabled: true, ttlMs: 60000 },
|
|
117
117
|
auth: {
|
|
118
118
|
getToken: async () => localStorage.getItem("token") ?? "",
|
|
119
|
-
onUnauthorized: async () => {
|
|
119
|
+
onUnauthorized: async () => {
|
|
120
|
+
/* refresh token */
|
|
121
|
+
},
|
|
120
122
|
},
|
|
121
123
|
});
|
|
122
124
|
|
|
@@ -126,8 +128,15 @@ const res = await client.get("/users/1");
|
|
|
126
128
|
const orchestrator = new PipelineOrchestrator({
|
|
127
129
|
config: {
|
|
128
130
|
stages: [
|
|
129
|
-
{
|
|
130
|
-
|
|
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
|
+
},
|
|
131
140
|
],
|
|
132
141
|
},
|
|
133
142
|
sharedData: { userId: 42 },
|
|
@@ -149,43 +158,43 @@ Creates a REST client with advanced HTTP features.
|
|
|
149
158
|
|
|
150
159
|
### Methods
|
|
151
160
|
|
|
152
|
-
| Method
|
|
153
|
-
|
|
154
|
-
| `get(url, config?)`
|
|
155
|
-
| `post(url, data?, config?)`
|
|
156
|
-
| `put(url, data?, config?)`
|
|
157
|
-
| `patch(url, data?, config?)`
|
|
158
|
-
| `delete(url, config?)`
|
|
159
|
-
| `request(url, config?)`
|
|
160
|
-
| `cancellableRequest(key, url, config?)` | Request cancellable by key
|
|
161
|
-
| `cancelRequest(key)`
|
|
162
|
-
| `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 |
|
|
163
172
|
|
|
164
173
|
### HttpConfig options
|
|
165
174
|
|
|
166
|
-
| Option
|
|
167
|
-
|
|
168
|
-
| `baseURL`
|
|
169
|
-
| `timeout`
|
|
170
|
-
| `headers`
|
|
171
|
-
| `withCredentials`
|
|
172
|
-
| `retry.attempts`
|
|
173
|
-
| `retry.delayMs`
|
|
174
|
-
| `retry.backoffMultiplier`
|
|
175
|
-
| `retry.retriableStatus`
|
|
176
|
-
| `retry.maxRetryAfterMs`
|
|
177
|
-
| `cache.enabled`
|
|
178
|
-
| `cache.ttlMs`
|
|
179
|
-
| `rateLimit.maxConcurrent`
|
|
180
|
-
| `rateLimit.maxRequestsPerInterval` | Max requests per time window
|
|
181
|
-
| `rateLimit.intervalMs`
|
|
182
|
-
| `metrics.onRequestStart`
|
|
183
|
-
| `metrics.onRequestEnd`
|
|
184
|
-
| `auth.getToken`
|
|
185
|
-
| `auth.onUnauthorized`
|
|
186
|
-
| `sanitizeHeaders`
|
|
187
|
-
| `sensitiveHeaders`
|
|
188
|
-
| `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 |
|
|
189
198
|
|
|
190
199
|
### Per-request cache override
|
|
191
200
|
|
|
@@ -215,7 +224,9 @@ const client = createRestClient({
|
|
|
215
224
|
rateLimit: { maxConcurrent: 3, maxRequestsPerInterval: 10, intervalMs: 1000 },
|
|
216
225
|
auth: {
|
|
217
226
|
getToken: async () => localStorage.getItem("token") ?? "",
|
|
218
|
-
onUnauthorized: async () => {
|
|
227
|
+
onUnauthorized: async () => {
|
|
228
|
+
/* refresh token here */
|
|
229
|
+
},
|
|
219
230
|
},
|
|
220
231
|
sanitizeHeaders: true,
|
|
221
232
|
});
|
|
@@ -227,7 +238,9 @@ console.log(res.data);
|
|
|
227
238
|
await client.patch("/users/1", { name: "Alice" });
|
|
228
239
|
|
|
229
240
|
// Cancellable request
|
|
230
|
-
const req = client.cancellableRequest("my-key", "/search", {
|
|
241
|
+
const req = client.cancellableRequest("my-key", "/search", {
|
|
242
|
+
params: { q: "foo" },
|
|
243
|
+
});
|
|
231
244
|
// Cancel it any time:
|
|
232
245
|
client.cancelRequest("my-key");
|
|
233
246
|
```
|
|
@@ -270,8 +283,8 @@ import { createRestClient, DEFAULT_SENSITIVE_HEADERS } from "rest-pipeline-js";
|
|
|
270
283
|
|
|
271
284
|
const client = createRestClient({
|
|
272
285
|
baseURL: "https://api.example.com",
|
|
273
|
-
sanitizeHeaders: true,
|
|
274
|
-
sensitiveHeaders: ["x-internal-secret"],
|
|
286
|
+
sanitizeHeaders: true, // opt-in — disabled by default
|
|
287
|
+
sensitiveHeaders: ["x-internal-secret"], // extend the default list
|
|
275
288
|
metrics: {
|
|
276
289
|
onRequestStart: (info) => {
|
|
277
290
|
// info.requestHeaders — sensitive values replaced with "REDACTED"
|
|
@@ -309,7 +322,7 @@ const executor = new RequestExecutor({
|
|
|
309
322
|
delayMs: 500,
|
|
310
323
|
backoffMultiplier: 2,
|
|
311
324
|
retriableStatus: [429, 500, 502, 503],
|
|
312
|
-
maxRetryAfterMs: 30000,
|
|
325
|
+
maxRetryAfterMs: 30000, // cap Retry-After at 30 s
|
|
313
326
|
},
|
|
314
327
|
});
|
|
315
328
|
|
|
@@ -338,42 +351,42 @@ new PipelineOrchestrator({
|
|
|
338
351
|
|
|
339
352
|
### Methods
|
|
340
353
|
|
|
341
|
-
| Method
|
|
342
|
-
|
|
343
|
-
| `run(onStepPause?, externalSignal?)`
|
|
344
|
-
| `rerunStep(stepKey, options?)`
|
|
345
|
-
| `abort()`
|
|
346
|
-
| `isAborted()`
|
|
347
|
-
| `pause()`
|
|
348
|
-
| `resume()`
|
|
349
|
-
| `isPaused()`
|
|
350
|
-
| `exportState()`
|
|
351
|
-
| `importState(state)`
|
|
352
|
-
| `getStageResults()`
|
|
353
|
-
| `destroy()`
|
|
354
|
-
| `subscribeProgress(listener)`
|
|
355
|
-
| `subscribeStageResults(listener)`
|
|
356
|
-
| `subscribeStepProgress(stepKey, listener)` | Subscribe to a specific stage's progress
|
|
357
|
-
| `on(eventName, handler)`
|
|
358
|
-
| `onStepStart/Finish/Error(handler)`
|
|
359
|
-
| `getProgress()`
|
|
360
|
-
| `getLogs()`
|
|
361
|
-
| `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 |
|
|
362
375
|
|
|
363
376
|
### Stage parameters (PipelineStageConfig)
|
|
364
377
|
|
|
365
|
-
| Parameter
|
|
366
|
-
|
|
367
|
-
| `key`
|
|
368
|
-
| `request({ prev, allResults, sharedData })`
|
|
369
|
-
| `condition({ prev, allResults, sharedData })` | If returns `false`, stage is skipped with status `"skipped"`
|
|
370
|
-
| `before({ prev, allResults, sharedData })`
|
|
371
|
-
| `after({ result, allResults, sharedData })`
|
|
372
|
-
| `errorHandler({ error, key, sharedData })`
|
|
373
|
-
| `retryCount`
|
|
374
|
-
| `timeoutMs`
|
|
375
|
-
| `pauseBefore`
|
|
376
|
-
| `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` |
|
|
377
390
|
|
|
378
391
|
### Stage execution flow
|
|
379
392
|
|
|
@@ -425,8 +438,10 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
425
438
|
],
|
|
426
439
|
middleware: {
|
|
427
440
|
beforeEach: ({ stage }) => console.log("Starting:", stage.key),
|
|
428
|
-
afterEach:
|
|
429
|
-
|
|
441
|
+
afterEach: ({ stage, result }) =>
|
|
442
|
+
console.log("Done:", stage.key, result.data),
|
|
443
|
+
onError: ({ stage, error }) =>
|
|
444
|
+
console.error("Error in", stage.key, error),
|
|
430
445
|
},
|
|
431
446
|
},
|
|
432
447
|
httpConfig: {
|
|
@@ -439,7 +454,12 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
439
454
|
});
|
|
440
455
|
|
|
441
456
|
orchestrator.subscribeProgress((progress) => {
|
|
442
|
-
console.log(
|
|
457
|
+
console.log(
|
|
458
|
+
"Stage:",
|
|
459
|
+
progress.currentStage,
|
|
460
|
+
"Statuses:",
|
|
461
|
+
progress.stageStatuses,
|
|
462
|
+
);
|
|
443
463
|
});
|
|
444
464
|
|
|
445
465
|
orchestrator.on("step:fetchUser:success", (payload) => {
|
|
@@ -468,7 +488,7 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
468
488
|
{
|
|
469
489
|
key: "load-data",
|
|
470
490
|
parallel: [
|
|
471
|
-
{ key: "loadUsers",
|
|
491
|
+
{ key: "loadUsers", request: async () => fetchUsers() },
|
|
472
492
|
{ key: "loadProducts", request: async () => fetchProducts() },
|
|
473
493
|
{ key: "loadSettings", request: async () => fetchSettings() },
|
|
474
494
|
],
|
|
@@ -495,7 +515,9 @@ Apply hooks to every stage without modifying individual stage configs:
|
|
|
495
515
|
```js
|
|
496
516
|
const orchestrator = new PipelineOrchestrator({
|
|
497
517
|
config: {
|
|
498
|
-
stages: [
|
|
518
|
+
stages: [
|
|
519
|
+
/* ... */
|
|
520
|
+
],
|
|
499
521
|
middleware: {
|
|
500
522
|
beforeEach: async ({ stage, index, sharedData }) => {
|
|
501
523
|
console.log(`[${index}] Starting: ${stage.key}`);
|
|
@@ -560,7 +582,7 @@ const orchestrator2 = new PipelineOrchestrator({ config });
|
|
|
560
582
|
orchestrator2.importState(saved);
|
|
561
583
|
|
|
562
584
|
console.log(orchestrator2.getProgress()); // restored progress
|
|
563
|
-
console.log(orchestrator2.getLogs());
|
|
585
|
+
console.log(orchestrator2.getLogs()); // restored logs (timestamps as Date objects)
|
|
564
586
|
```
|
|
565
587
|
|
|
566
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`.
|
|
@@ -574,7 +596,9 @@ Observe pipeline execution without modifying stage logic:
|
|
|
574
596
|
```js
|
|
575
597
|
const orchestrator = new PipelineOrchestrator({
|
|
576
598
|
config: {
|
|
577
|
-
stages: [
|
|
599
|
+
stages: [
|
|
600
|
+
/* ... */
|
|
601
|
+
],
|
|
578
602
|
metrics: {
|
|
579
603
|
onPipelineStart: ({ timestamp }) => {
|
|
580
604
|
console.log("Pipeline started at", new Date(timestamp).toISOString());
|
|
@@ -590,11 +614,11 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
590
614
|
});
|
|
591
615
|
```
|
|
592
616
|
|
|
593
|
-
| Callback
|
|
594
|
-
|
|
595
|
-
| `onPipelineStart` | `{ timestamp }`
|
|
596
|
-
| `onPipelineEnd`
|
|
597
|
-
| `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 |
|
|
598
622
|
|
|
599
623
|
---
|
|
600
624
|
|
|
@@ -608,14 +632,15 @@ import { createPipeline } from "rest-pipeline-js";
|
|
|
608
632
|
const orchestrator = createPipeline(
|
|
609
633
|
[
|
|
610
634
|
{ key: "fetchUser", request: async () => fetchUser() },
|
|
611
|
-
{ key: "process",
|
|
635
|
+
{ key: "process", request: async ({ prev }) => process(prev) },
|
|
612
636
|
],
|
|
613
637
|
{
|
|
614
|
-
httpConfig:
|
|
615
|
-
sharedData:
|
|
638
|
+
httpConfig: { baseURL: "https://api.example.com" },
|
|
639
|
+
sharedData: { userId: 42 },
|
|
616
640
|
pipelineOptions: { continueOnError: false },
|
|
617
641
|
metrics: {
|
|
618
|
-
onStepDuration: ({ stepKey, durationMs }) =>
|
|
642
|
+
onStepDuration: ({ stepKey, durationMs }) =>
|
|
643
|
+
console.log(stepKey, durationMs),
|
|
619
644
|
},
|
|
620
645
|
},
|
|
621
646
|
);
|
|
@@ -627,28 +652,30 @@ const orchestrator = createPipeline(
|
|
|
627
652
|
import { pipe } from "rest-pipeline-js";
|
|
628
653
|
|
|
629
654
|
const orchestrator = pipe()
|
|
630
|
-
.step({ key: "auth",
|
|
655
|
+
.step({ key: "auth", request: async () => getToken() })
|
|
631
656
|
.step({ key: "fetchUser", request: async ({ prev }) => fetchUser(prev) })
|
|
632
657
|
.parallel([
|
|
633
|
-
{ key: "loadPosts",
|
|
658
|
+
{ key: "loadPosts", request: async () => fetchPosts() },
|
|
634
659
|
{ key: "loadNotifs", request: async () => fetchNotifications() },
|
|
635
660
|
])
|
|
636
661
|
.stream({
|
|
637
662
|
key: "liveUpdates",
|
|
638
|
-
stream: async function* () {
|
|
663
|
+
stream: async function* () {
|
|
664
|
+
yield* subscribe("/events");
|
|
665
|
+
},
|
|
639
666
|
onChunk: (chunk) => updateUI(chunk),
|
|
640
667
|
})
|
|
641
668
|
.build({ httpConfig: { baseURL: "https://api.example.com" } });
|
|
642
669
|
```
|
|
643
670
|
|
|
644
|
-
| Builder method
|
|
645
|
-
|
|
646
|
-
| `.step(stage)`
|
|
647
|
-
| `.parallel(stages, options?)` | Add a parallel group (`key` auto-generated if omitted)
|
|
648
|
-
| `.subPipeline(item)`
|
|
649
|
-
| `.stream(stage)`
|
|
650
|
-
| `.build(options?)`
|
|
651
|
-
| `.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 |
|
|
652
679
|
|
|
653
680
|
---
|
|
654
681
|
|
|
@@ -662,8 +689,8 @@ import { validatePipelineConfig } from "rest-pipeline-js";
|
|
|
662
689
|
const { valid, errors } = validatePipelineConfig({
|
|
663
690
|
stages: [
|
|
664
691
|
{ key: "step1", request: async () => data },
|
|
665
|
-
{ key: "step1", request: async () => other },
|
|
666
|
-
{ key: "",
|
|
692
|
+
{ key: "step1", request: async () => other }, // duplicate!
|
|
693
|
+
{ key: "", request: async () => other }, // empty key!
|
|
667
694
|
],
|
|
668
695
|
});
|
|
669
696
|
|
|
@@ -685,15 +712,18 @@ const loggingPlugin = {
|
|
|
685
712
|
install(orchestrator) {
|
|
686
713
|
const off = orchestrator.on("log", (event) => {
|
|
687
714
|
if (event.type === "step:success") console.log("✓", event.stepKey);
|
|
688
|
-
if (event.type === "step:error")
|
|
715
|
+
if (event.type === "step:error")
|
|
716
|
+
console.error("✗", event.stepKey, event.error);
|
|
689
717
|
});
|
|
690
|
-
return () => off();
|
|
718
|
+
return () => off(); // cleanup on orchestrator.destroy()
|
|
691
719
|
},
|
|
692
720
|
};
|
|
693
721
|
|
|
694
722
|
const orchestrator = new PipelineOrchestrator({
|
|
695
723
|
config: {
|
|
696
|
-
stages: [
|
|
724
|
+
stages: [
|
|
725
|
+
/* ... */
|
|
726
|
+
],
|
|
697
727
|
options: { plugins: [loggingPlugin, analyticsPlugin] },
|
|
698
728
|
},
|
|
699
729
|
});
|
|
@@ -722,7 +752,9 @@ const localStorageAdapter = {
|
|
|
722
752
|
|
|
723
753
|
const orchestrator = new PipelineOrchestrator({
|
|
724
754
|
config: {
|
|
725
|
-
stages: [
|
|
755
|
+
stages: [
|
|
756
|
+
/* ... */
|
|
757
|
+
],
|
|
726
758
|
options: { persistAdapter: localStorageAdapter },
|
|
727
759
|
},
|
|
728
760
|
});
|
|
@@ -783,16 +815,20 @@ Replace the built-in axios client with any HTTP implementation:
|
|
|
783
815
|
```js
|
|
784
816
|
const fetchAdapter = {
|
|
785
817
|
async request(config) {
|
|
786
|
-
const url
|
|
787
|
-
const res
|
|
788
|
-
method:
|
|
789
|
-
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,
|
|
790
822
|
headers: { "Content-Type": "application/json", ...config.headers },
|
|
791
|
-
signal:
|
|
823
|
+
signal: config.signal,
|
|
792
824
|
});
|
|
793
825
|
const data = await res.json();
|
|
794
|
-
return {
|
|
795
|
-
|
|
826
|
+
return {
|
|
827
|
+
data,
|
|
828
|
+
status: res.status,
|
|
829
|
+
statusText: res.statusText,
|
|
830
|
+
headers: Object.fromEntries(res.headers.entries()),
|
|
831
|
+
};
|
|
796
832
|
},
|
|
797
833
|
};
|
|
798
834
|
|
|
@@ -824,7 +860,13 @@ import {
|
|
|
824
860
|
usePipelineRunVue,
|
|
825
861
|
} from "rest-pipeline-js/vue";
|
|
826
862
|
|
|
827
|
-
const orchestrator = new PipelineOrchestrator({
|
|
863
|
+
const orchestrator = new PipelineOrchestrator({
|
|
864
|
+
config: {
|
|
865
|
+
stages: [
|
|
866
|
+
/* ... */
|
|
867
|
+
],
|
|
868
|
+
},
|
|
869
|
+
});
|
|
828
870
|
const progress = usePipelineProgressVue(orchestrator);
|
|
829
871
|
const { run, running, result, error, abort, pause, resume, rerunStep } =
|
|
830
872
|
usePipelineRunVue(orchestrator);
|
|
@@ -845,15 +887,15 @@ const { run, running, result, error, abort, pause, resume, rerunStep } =
|
|
|
845
887
|
|
|
846
888
|
Composables (import from `rest-pipeline-js/vue`):
|
|
847
889
|
|
|
848
|
-
| Composable
|
|
849
|
-
|
|
850
|
-
| `usePipelineProgressVue(orchestrator)`
|
|
851
|
-
| `usePipelineRunVue(orchestrator)`
|
|
852
|
-
| `usePipelineStepEventVue(orchestrator, stepKey, eventType)` | `Ref<any>`
|
|
853
|
-
| `usePipelineLogsVue(orchestrator)`
|
|
854
|
-
| `useRerunPipelineStepVue(orchestrator)`
|
|
855
|
-
| `useRestClientVue(config)`
|
|
856
|
-
| `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 |
|
|
857
899
|
|
|
858
900
|
---
|
|
859
901
|
|
|
@@ -867,7 +909,13 @@ import {
|
|
|
867
909
|
usePipelineRunReact,
|
|
868
910
|
} from "rest-pipeline-js/react";
|
|
869
911
|
|
|
870
|
-
const orchestrator = new PipelineOrchestrator({
|
|
912
|
+
const orchestrator = new PipelineOrchestrator({
|
|
913
|
+
config: {
|
|
914
|
+
stages: [
|
|
915
|
+
/* ... */
|
|
916
|
+
],
|
|
917
|
+
},
|
|
918
|
+
});
|
|
871
919
|
|
|
872
920
|
export function PipelineComponent() {
|
|
873
921
|
const progress = usePipelineProgressReact(orchestrator);
|
|
@@ -877,12 +925,16 @@ export function PipelineComponent() {
|
|
|
877
925
|
return (
|
|
878
926
|
<div>
|
|
879
927
|
<div>Current stage: {progress.currentStage}</div>
|
|
880
|
-
<button onClick={() => run()} disabled={running}>
|
|
881
|
-
|
|
928
|
+
<button onClick={() => run()} disabled={running}>
|
|
929
|
+
Start
|
|
930
|
+
</button>
|
|
931
|
+
<button onClick={() => abort()} disabled={!running}>
|
|
932
|
+
Abort
|
|
933
|
+
</button>
|
|
882
934
|
<button onClick={() => pause()}>Pause</button>
|
|
883
935
|
<button onClick={() => resume()}>Resume</button>
|
|
884
936
|
{result && <div>Done: {JSON.stringify(result)}</div>}
|
|
885
|
-
{error
|
|
937
|
+
{error && <div>Error: {error.message}</div>}
|
|
886
938
|
</div>
|
|
887
939
|
);
|
|
888
940
|
}
|
|
@@ -890,25 +942,25 @@ export function PipelineComponent() {
|
|
|
890
942
|
|
|
891
943
|
Hooks (import from `rest-pipeline-js/react`):
|
|
892
944
|
|
|
893
|
-
| Hook
|
|
894
|
-
|
|
895
|
-
| `usePipelineProgressReact(orchestrator)`
|
|
896
|
-
| `usePipelineRunReact(orchestrator)`
|
|
897
|
-
| `usePipelineStepEventReact(orchestrator, stepKey, eventType)` | `any`
|
|
898
|
-
| `usePipelineLogsReact(orchestrator)`
|
|
899
|
-
| `useRerunPipelineStepReact(orchestrator)`
|
|
900
|
-
| `useRestClientReact(config)`
|
|
901
|
-
| `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 |
|
|
902
954
|
|
|
903
955
|
---
|
|
904
956
|
|
|
905
957
|
## Entry points
|
|
906
958
|
|
|
907
|
-
| Entry point
|
|
908
|
-
|
|
909
|
-
| `rest-pipeline-js`
|
|
910
|
-
| `rest-pipeline-js/vue`
|
|
911
|
-
| `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 |
|
|
912
964
|
|
|
913
965
|
```js
|
|
914
966
|
// Core only
|
|
@@ -918,7 +970,10 @@ import { createRestClient, PipelineOrchestrator } from "rest-pipeline-js";
|
|
|
918
970
|
import { PipelineOrchestrator, usePipelineRunVue } from "rest-pipeline-js/vue";
|
|
919
971
|
|
|
920
972
|
// React
|
|
921
|
-
import {
|
|
973
|
+
import {
|
|
974
|
+
PipelineOrchestrator,
|
|
975
|
+
usePipelineRunReact,
|
|
976
|
+
} from "rest-pipeline-js/react";
|
|
922
977
|
```
|
|
923
978
|
|
|
924
979
|
`sideEffects: false` — unused entry points are tree-shaken. `react` / `react-dom` are `peerDependencies`.
|
|
@@ -972,11 +1027,11 @@ rest-pipeline-js
|
|
|
972
1027
|
|
|
973
1028
|
## Bundle size & peer dependencies
|
|
974
1029
|
|
|
975
|
-
| Entry point
|
|
976
|
-
|
|
977
|
-
| `rest-pipeline-js`
|
|
978
|
-
| `rest-pipeline-js/vue`
|
|
979
|
-
| `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 |
|
|
980
1035
|
|
|
981
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.
|
|
982
1037
|
|