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 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-js.webp"
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 live interactive demo of the pipeline running against a real flight-search API 4 sequential stages: airport lookup, availability, ancillary services, and seat map.
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` (or the next available port). Click **Run Pipeline** to execute all stages and watch results appear in real time. A boarding pass is rendered when all stages succeed.
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 () => { /* refresh token */ },
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
- { key: "fetchUser", request: async ({ sharedData }) => client.get(`/users/${sharedData.userId}`) },
123
- { key: "processData", request: async ({ prev }) => ({ ...prev.data, processed: true }) },
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 | Description |
146
- |---|---|
147
- | `get(url, config?)` | GET request |
148
- | `post(url, data?, config?)` | POST request |
149
- | `put(url, data?, config?)` | PUT request |
150
- | `patch(url, data?, config?)` | PATCH request |
151
- | `delete(url, config?)` | DELETE request |
152
- | `request(url, config?)` | Generic request |
153
- | `cancellableRequest(key, url, config?)` | Request cancellable by key |
154
- | `cancelRequest(key)` | Cancel request by key |
155
- | `clearCache()` | Clear this client's response cache |
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 | Description |
160
- |---|---|
161
- | `baseURL` | Base URL for all requests |
162
- | `timeout` | Request timeout in ms |
163
- | `headers` | Default headers |
164
- | `withCredentials` | Include cookies |
165
- | `retry.attempts` | Number of retry attempts |
166
- | `retry.delayMs` | Base delay between retries in ms |
167
- | `retry.backoffMultiplier` | Exponential backoff multiplier |
168
- | `retry.retriableStatus` | HTTP status codes eligible for retry (e.g. `[429, 500, 503]`) |
169
- | `retry.maxRetryAfterMs` | Max wait from `Retry-After` header in ms (default: `60000`) |
170
- | `cache.enabled` | Enable response caching for GET requests |
171
- | `cache.ttlMs` | Cache TTL in ms |
172
- | `rateLimit.maxConcurrent` | Max simultaneous requests |
173
- | `rateLimit.maxRequestsPerInterval` | Max requests per time window |
174
- | `rateLimit.intervalMs` | Time window size in ms |
175
- | `metrics.onRequestStart` | Callback on request start |
176
- | `metrics.onRequestEnd` | Callback on request end (includes duration and bytes) |
177
- | `auth.getToken` | Async function returning a Bearer token (called before every request) |
178
- | `auth.onUnauthorized` | Optional async callback on 401 — refresh the token here; request is retried once |
179
- | `sanitizeHeaders` | Mask sensitive headers in metrics callbacks (default: `false`) |
180
- | `sensitiveHeaders` | Additional headers to mask (extends `DEFAULT_SENSITIVE_HEADERS`) |
181
- | `adapter` | Custom HTTP adapter (e.g. native `fetch`) — replaces built-in axios |
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 () => { /* refresh token here */ },
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", { params: { q: "foo" } });
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, // opt-in — disabled by default
267
- sensitiveHeaders: ["x-internal-secret"], // extend the default list
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, // cap Retry-After at 30 s
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 | Description |
335
- |---|---|
336
- | `run(onStepPause?, externalSignal?)` | Execute all stages. Returns `{ stageResults, success }` |
337
- | `rerunStep(stepKey, options?)` | Re-execute a single stage (respects condition, before, after, middleware) |
338
- | `abort()` | Abort pipeline execution (cancels the current HTTP request via AbortSignal) |
339
- | `isAborted()` | Check if pipeline was aborted |
340
- | `pause()` | Pause after the current stage completes |
341
- | `resume()` | Resume a paused pipeline |
342
- | `isPaused()` | Check if pipeline is paused |
343
- | `exportState()` | Serialize stageResults and logs to a plain object |
344
- | `importState(state)` | Restore stageResults and logs from a snapshot |
345
- | `getStageResults()` | Synchronous snapshot of all stage results |
346
- | `destroy()` | Run cleanup callbacks from all installed plugins |
347
- | `subscribeProgress(listener)` | Subscribe to progress updates |
348
- | `subscribeStageResults(listener)` | Subscribe to stageResults changes |
349
- | `subscribeStepProgress(stepKey, listener)` | Subscribe to a specific stage's progress |
350
- | `on(eventName, handler)` | Subscribe to any event (`step:<key>:start\|success\|error\|skipped\|progress`, `log`) |
351
- | `onStepStart/Finish/Error(handler)` | Subscribe to stage lifecycle events |
352
- | `getProgress()` | Get current progress snapshot |
353
- | `getLogs()` | Get all pipeline logs |
354
- | `clearStageResults()` | Reset results and progress |
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 | Description |
359
- |---|---|
360
- | `key` | Unique stage identifier |
361
- | `request({ prev, allResults, sharedData })` | Main stage function — return value becomes the stage result |
362
- | `condition({ prev, allResults, sharedData })` | If returns `false`, stage is skipped with status `"skipped"` |
363
- | `before({ prev, allResults, sharedData })` | Pre-processing hook — returned value replaces `prev` passed to `request` |
364
- | `after({ result, allResults, sharedData })` | Post-processing hook — returned value replaces the stage result |
365
- | `errorHandler({ error, key, sharedData })` | Per-stage error handler |
366
- | `retryCount` | Override retry count for this stage |
367
- | `timeoutMs` | Override timeout for this stage |
368
- | `pauseBefore` | Delay in ms before executing `request` |
369
- | `pauseAfter` | Delay in ms after executing `request` |
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: ({ stage, result }) => console.log("Done:", stage.key, result.data),
422
- onError: ({ stage, error }) => console.error("Error in", stage.key, error),
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("Stage:", progress.currentStage, "Statuses:", progress.stageStatuses);
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", request: async () => fetchUsers() },
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()); // restored logs (timestamps as Date objects)
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 | Receives | Description |
587
- |---|---|---|
588
- | `onPipelineStart` | `{ timestamp }` | Fires at the beginning of `run()` |
589
- | `onPipelineEnd` | `{ durationMs, success, stageResults }` | Fires when `run()` completes |
590
- | `onStepDuration` | `{ stepKey, durationMs, status }` | Fires after every executed step |
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", request: async ({ prev }) => process(prev) },
635
+ { key: "process", request: async ({ prev }) => process(prev) },
605
636
  ],
606
637
  {
607
- httpConfig: { baseURL: "https://api.example.com" },
608
- sharedData: { userId: 42 },
638
+ httpConfig: { baseURL: "https://api.example.com" },
639
+ sharedData: { userId: 42 },
609
640
  pipelineOptions: { continueOnError: false },
610
641
  metrics: {
611
- onStepDuration: ({ stepKey, durationMs }) => console.log(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", request: async () => getToken() })
655
+ .step({ key: "auth", request: async () => getToken() })
624
656
  .step({ key: "fetchUser", request: async ({ prev }) => fetchUser(prev) })
625
657
  .parallel([
626
- { key: "loadPosts", request: async () => fetchPosts() },
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* () { yield* subscribe("/events"); },
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 | Description |
638
- |---|---|
639
- | `.step(stage)` | Add a sequential stage |
640
- | `.parallel(stages, options?)` | Add a parallel group (`key` auto-generated if omitted) |
641
- | `.subPipeline(item)` | Embed a sub-pipeline as a stage |
642
- | `.stream(stage)` | Add a stream stage (AsyncIterable) |
643
- | `.build(options?)` | Create and return a `PipelineOrchestrator` |
644
- | `.toConfig(options?)` | Return `PipelineConfig` without creating an orchestrator |
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 }, // duplicate!
659
- { key: "", request: async () => other }, // empty 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") console.error("✗", event.stepKey, event.error);
715
+ if (event.type === "step:error")
716
+ console.error("✗", event.stepKey, event.error);
682
717
  });
683
- return () => off(); // cleanup on orchestrator.destroy()
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 = `${config.baseURL ?? ""}${config.url ?? ""}`;
780
- const res = await fetch(url, {
781
- method: config.method ?? "GET",
782
- body: config.data ? JSON.stringify(config.data) : undefined,
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: config.signal,
823
+ signal: config.signal,
785
824
  });
786
825
  const data = await res.json();
787
- return { data, status: res.status, statusText: res.statusText,
788
- headers: Object.fromEntries(res.headers.entries()) };
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({ config: { stages: [ /* ... */ ] } });
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 | Returns | Description |
842
- |---|---|---|
843
- | `usePipelineProgressVue(orchestrator)` | `Ref<PipelineProgress>` | Reactive progress |
844
- | `usePipelineRunVue(orchestrator)` | `{ run, running, result, error, stageResults, abort, pause, resume, rerunStep, clearStageResults }` | Run pipeline and get reactive state |
845
- | `usePipelineStepEventVue(orchestrator, stepKey, eventType)` | `Ref<any>` | Last payload for a specific step event |
846
- | `usePipelineLogsVue(orchestrator)` | `Ref<log[]>` | Reactive logs |
847
- | `useRerunPipelineStepVue(orchestrator)` | `function` | Bound `rerunStep` |
848
- | `useRestClientVue(config)` | `ComputedRef<RestClient>` | Reactive REST client |
849
- | `usePipelineStageResultVue(orchestrator, stepKey)` | `Ref<PipelineStepResult \| null>` | Reactive result of a single stage |
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({ config: { stages: [ /* ... */ ] } });
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}>Start</button>
874
- <button onClick={() => abort()} disabled={!running}>Abort</button>
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 && <div>Error: {error.message}</div>}
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 | Returns | Description |
887
- |---|---|---|
888
- | `usePipelineProgressReact(orchestrator)` | `PipelineProgress` | Reactive progress |
889
- | `usePipelineRunReact(orchestrator)` | `[run, { running, result, error, stageResults, abort, pause, resume, rerunStep }]` | Run pipeline and get state |
890
- | `usePipelineStepEventReact(orchestrator, stepKey, eventType)` | `any` | Last payload for a specific step event |
891
- | `usePipelineLogsReact(orchestrator)` | `log[]` | Reactive logs |
892
- | `useRerunPipelineStepReact(orchestrator)` | `function` | Bound `rerunStep` |
893
- | `useRestClientReact(config)` | `RestClient` | Memoized REST client |
894
- | `usePipelineStageResultReact(orchestrator, stepKey)` | `PipelineStepResult \| null` | Result of a single stage |
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 | Use for | Contents |
901
- |---|---|---|
902
- | `rest-pipeline-js` | Core only | `PipelineOrchestrator`, `createRestClient`, types, utilities. No Vue/React. |
903
- | `rest-pipeline-js/vue` | Vue projects | Core + Vue composables |
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 { PipelineOrchestrator, usePipelineRunReact } from "rest-pipeline-js/react";
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 | Peer deps | Notes |
969
- |---|---|---|
970
- | `rest-pipeline-js` | — | Core — orchestrator, HTTP client, utilities. Depends on `axios`. |
971
- | `rest-pipeline-js/vue` | `vue ^3.3` | Core + Vue composables |
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