rest-pipeline-js 1.2.6 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +412 -747
  2. package/dist/cjs/cache.js +48 -0
  3. package/dist/cjs/pipeline-orchestrator.js +407 -441
  4. package/dist/cjs/progress-tracker.js +3 -3
  5. package/dist/cjs/rate-limiter.js +68 -0
  6. package/dist/cjs/request-executor.js +88 -10
  7. package/dist/cjs/rest-client.js +84 -71
  8. package/dist/cjs/usePipelineRun-react.js +12 -5
  9. package/dist/cjs/usePipelineRun-vue.js +17 -8
  10. package/dist/esm/cache.d.ts +14 -0
  11. package/dist/esm/cache.js +44 -0
  12. package/dist/esm/pipeline-orchestrator.d.ts +51 -94
  13. package/dist/esm/pipeline-orchestrator.js +411 -445
  14. package/dist/esm/progress-tracker.d.ts +7 -3
  15. package/dist/esm/progress-tracker.js +3 -3
  16. package/dist/esm/rate-limiter.d.ts +19 -0
  17. package/dist/esm/rate-limiter.js +64 -0
  18. package/dist/esm/request-executor.d.ts +8 -2
  19. package/dist/esm/request-executor.js +88 -10
  20. package/dist/esm/rest-client.d.ts +10 -5
  21. package/dist/esm/rest-client.js +83 -38
  22. package/dist/esm/types.d.ts +76 -5
  23. package/dist/esm/usePipelineRun-react.d.ts +8 -4
  24. package/dist/esm/usePipelineRun-react.js +13 -6
  25. package/dist/esm/usePipelineRun-vue.d.ts +8 -5
  26. package/dist/esm/usePipelineRun-vue.js +18 -9
  27. package/dist/esm/useRestClient-react.d.ts +7 -5
  28. package/dist/esm/useRestClient-vue.d.ts +7 -5
  29. package/package.json +1 -1
  30. package/src/cache.ts +47 -0
  31. package/src/index.ts +8 -8
  32. package/src/pipeline-orchestrator.ts +534 -519
  33. package/src/progress-tracker.ts +3 -3
  34. package/src/rate-limiter.ts +76 -0
  35. package/src/request-executor.ts +144 -16
  36. package/src/rest-client.ts +184 -40
  37. package/src/types.ts +132 -5
  38. package/src/usePipelineRun-react.ts +52 -36
  39. package/src/usePipelineRun-vue.ts +63 -47
  40. package/src/vue-demo/demo.vue +3 -3
  41. package/tests/pipeline-orchestrator.test.ts +717 -126
  42. package/tests/request-executor.test.ts +39 -9
  43. package/tests/rest-client.test.ts +259 -9
  44. package/tests/types.test.ts +105 -20
  45. package/dist/error-handler.d.ts +0 -7
  46. package/dist/error-handler.js +0 -6
  47. package/dist/index.d.ts +0 -6
  48. package/dist/index.js +0 -7
  49. package/dist/pipeline-orchestrator.d.ts +0 -136
  50. package/dist/pipeline-orchestrator.js +0 -561
  51. package/dist/progress-tracker.d.ts +0 -22
  52. package/dist/progress-tracker.js +0 -43
  53. package/dist/react.d.ts +0 -5
  54. package/dist/react.js +0 -6
  55. package/dist/request-executor.d.ts +0 -9
  56. package/dist/request-executor.js +0 -29
  57. package/dist/rest-client.d.ts +0 -14
  58. package/dist/rest-client.js +0 -160
  59. package/dist/types.d.ts +0 -164
  60. package/dist/types.js +0 -1
  61. package/dist/usePipelineProgress-react.d.ts +0 -8
  62. package/dist/usePipelineProgress-react.js +0 -14
  63. package/dist/usePipelineProgress-vue.d.ts +0 -16
  64. package/dist/usePipelineProgress-vue.js +0 -14
  65. package/dist/usePipelineRun-react.d.ts +0 -12
  66. package/dist/usePipelineRun-react.js +0 -30
  67. package/dist/usePipelineRun-vue.d.ts +0 -21
  68. package/dist/usePipelineRun-vue.js +0 -42
  69. package/dist/usePipelineStepEvents-react.d.ts +0 -29
  70. package/dist/usePipelineStepEvents-react.js +0 -41
  71. package/dist/usePipelineStepEvents-vue.d.ts +0 -39
  72. package/dist/usePipelineStepEvents-vue.js +0 -40
  73. package/dist/useRestClient-react.d.ts +0 -15
  74. package/dist/useRestClient-react.js +0 -10
  75. package/dist/useRestClient-vue.d.ts +0 -15
  76. package/dist/useRestClient-vue.js +0 -10
  77. package/dist/vue.d.ts +0 -5
  78. package/dist/vue.js +0 -6
package/README.md CHANGED
@@ -22,936 +22,601 @@ import { createRestClient } from "rest-pipeline-js";
22
22
  const client = createRestClient({
23
23
  baseURL: "https://api.example.com",
24
24
  timeout: 5000,
25
- headers: { Authorization: "Bearer TOKEN" },
25
+ retry: {
26
+ attempts: 2,
27
+ delayMs: 500,
28
+ backoffMultiplier: 2,
29
+ retriableStatus: [429, 500, 503],
30
+ },
31
+ cache: { enabled: true, ttlMs: 60000 },
32
+ rateLimit: { maxConcurrent: 3, maxRequestsPerInterval: 10, intervalMs: 1000 },
33
+ // Auth Provider — token injected automatically, 401 triggers refresh + one retry
34
+ auth: {
35
+ getToken: async () => localStorage.getItem("token") ?? "",
36
+ onUnauthorized: async () => {
37
+ /* refresh token here */
38
+ },
39
+ },
40
+ // Mask sensitive headers in metrics (authorization, x-api-key, cookie, …)
41
+ sanitizeHeaders: true,
26
42
  });
27
43
 
28
- async function fetchUser(id) {
29
- const res = await client.request(`/users/${id}`);
30
- if (res.error) {
31
- console.error(res.error);
32
- } else {
33
- console.log(res.data);
34
- }
35
- }
44
+ const res = await client.get("/users/1");
45
+ console.log(res.data);
46
+
47
+ // PATCH support
48
+ await client.patch("/users/1", { name: "Alice" });
49
+
50
+ // Cancellable request
51
+ const req = client.cancellableRequest("my-key", "/search", {
52
+ params: { q: "foo" },
53
+ });
54
+ // Cancel it any time:
55
+ client.cancelRequest("my-key");
36
56
  ```
37
57
 
58
+ ---
59
+
38
60
  #### Example: Run a pipeline, handle errors, track progress, use shared data
39
61
 
40
62
  ```js
41
63
  import { PipelineOrchestrator } from "rest-pipeline-js";
42
64
 
43
- const pipelineConfig = {
44
- stages: [
45
- { key: "step1", command: "/api/step1", method: "POST" },
46
- {
47
- key: "step2",
48
- command: "/api/step2",
49
- method: "POST",
50
- dependsOn: ["step1"],
65
+ const orchestrator = new PipelineOrchestrator({
66
+ config: {
67
+ stages: [
68
+ {
69
+ key: "fetchUser",
70
+ request: async ({ sharedData }) => {
71
+ const res = await fetch(`/api/users/${sharedData.userId}`);
72
+ return res.json();
73
+ },
74
+ },
75
+ {
76
+ key: "processData",
77
+ condition: ({ prev }) => prev !== null,
78
+ before: ({ prev }) => ({ ...prev, processed: true }),
79
+ request: async ({ prev }) => prev,
80
+ after: ({ result }) => ({ ...result, finishedAt: Date.now() }),
81
+ },
82
+ ],
83
+ middleware: {
84
+ beforeEach: ({ stage }) => console.log("Starting:", stage.key),
85
+ afterEach: ({ stage, result }) =>
86
+ console.log("Done:", stage.key, result.data),
87
+ onError: ({ stage, error }) =>
88
+ console.error("Error in", stage.key, error),
51
89
  },
52
- ],
53
- };
54
- const httpConfig = {
55
- baseURL: "https://api.example.com",
56
- timeout: 7000,
57
- headers: { Authorization: "Bearer TOKEN" },
58
- retry: { attempts: 2, delayMs: 1000 },
59
- cache: { enabled: true, ttlMs: 60000 },
60
- rateLimit: { maxConcurrent: 2 },
61
- metrics: {
62
- onRequestStart: (info) => console.log("Start:", info),
63
- onRequestEnd: (info) => console.log("End:", info),
64
90
  },
65
- };
66
- const sharedData = { sessionId: "abc123" };
67
- const orchestrator = new PipelineOrchestrator(
68
- pipelineConfig,
69
- httpConfig,
70
- sharedData,
71
- { autoReset: true },
72
- );
91
+ httpConfig: {
92
+ baseURL: "https://api.example.com",
93
+ retry: { attempts: 2, delayMs: 1000, backoffMultiplier: 2 },
94
+ cache: { enabled: true, ttlMs: 60000 },
95
+ },
96
+ sharedData: { userId: 42 },
97
+ options: { autoReset: true },
98
+ });
73
99
 
74
100
  orchestrator.subscribeProgress((progress) => {
75
101
  console.log(
76
- "Current stage:",
102
+ "Stage:",
77
103
  progress.currentStage,
78
104
  "Statuses:",
79
105
  progress.stageStatuses,
80
106
  );
81
107
  });
82
- orchestrator.on("step:step1:success", (payload) => {
83
- console.log("Step 1 success:", payload.data);
84
- });
85
- orchestrator.on("step:step2:error", (payload) => {
86
- console.error("Step 2 error:", payload.error);
87
- });
88
- orchestrator.on("log", () => {
89
- console.log("Logs:", orchestrator.getLogs());
108
+
109
+ orchestrator.on("step:fetchUser:success", (payload) => {
110
+ console.log("fetchUser done:", payload.data);
90
111
  });
91
- orchestrator
92
- .run({ foo: "bar" })
93
- .then((result) => {
94
- console.log("Pipeline finished:", result);
95
- console.log("Stage results:", result.stageResults);
96
- })
97
- .catch((err) => {
98
- console.error("Pipeline error:", err);
99
- });
100
- // orchestrator.rerunStep('step2');
112
+
113
+ const result = await orchestrator.run();
114
+ console.log("Pipeline finished:", result.success);
115
+ console.log("Stage results:", result.stageResults);
101
116
  ```
102
117
 
103
118
  ---
104
119
 
105
- The module provides a universal mechanism for building and managing REST API pipelines with progress tracking, error handling, event subscriptions, and extensibility.
106
-
107
120
  ### Main classes and functions
108
121
 
109
122
  #### createRestClient(config: HttpConfig): RestClient
110
123
 
111
- Creates a REST client with advanced HTTP API features.
112
-
113
- #### Example
124
+ Creates a REST client with advanced HTTP features.
125
+
126
+ **Available methods:**
127
+
128
+ | Method | Description |
129
+ | --------------------------------------- | ---------------------------------- |
130
+ | `get(url, config?)` | GET request |
131
+ | `post(url, data?, config?)` | POST request |
132
+ | `put(url, data?, config?)` | PUT request |
133
+ | `patch(url, data?, config?)` | PATCH request |
134
+ | `delete(url, config?)` | DELETE request |
135
+ | `request(url, config?)` | Generic request |
136
+ | `cancellableRequest(key, url, config?)` | Request cancellable by key |
137
+ | `cancelRequest(key)` | Cancel request by key |
138
+ | `clearCache()` | Clear this client's response cache |
139
+
140
+ **HttpConfig options:**
141
+
142
+ | Option | Description |
143
+ | ---------------------------------- | -------------------------------------------------------------------------------- |
144
+ | `baseURL` | Base URL for all requests |
145
+ | `timeout` | Request timeout in ms |
146
+ | `headers` | Default headers |
147
+ | `withCredentials` | Include cookies |
148
+ | `retry.attempts` | Number of retry attempts |
149
+ | `retry.delayMs` | Base delay between retries in ms |
150
+ | `retry.backoffMultiplier` | Exponential backoff multiplier |
151
+ | `retry.retriableStatus` | HTTP status codes eligible for retry (e.g. `[429, 500, 503]`) |
152
+ | `cache.enabled` | Enable response caching for GET requests |
153
+ | `cache.ttlMs` | Cache TTL in ms |
154
+ | `rateLimit.maxConcurrent` | Max simultaneous requests |
155
+ | `rateLimit.maxRequestsPerInterval` | Max requests per time window |
156
+ | `rateLimit.intervalMs` | Time window size in ms |
157
+ | `metrics.onRequestStart` | Callback on request start |
158
+ | `metrics.onRequestEnd` | Callback on request end (includes duration and bytes) |
159
+ | `auth.getToken` | Async function returning a Bearer token (called before every request) |
160
+ | `auth.onUnauthorized` | Optional async callback on 401 — refresh the token here; request is retried once |
161
+ | `sanitizeHeaders` | Mask sensitive headers in metrics callbacks (default: `false`) |
162
+ | `sensitiveHeaders` | Additional headers to mask (extends `DEFAULT_SENSITIVE_HEADERS`) |
163
+ | `retry.maxRetryAfterMs` | Max wait from `Retry-After` header in ms (default: `60000`) |
164
+
165
+ **Per-request cache override:**
114
166
 
115
167
  ```js
116
- import { createRestClient } from "rest-pipeline-js";
117
- const client = createRestClient({
118
- baseURL: "https://api.example.com",
119
- timeout: 5000,
120
- headers: { Authorization: "Bearer TOKEN" },
121
- retry: { attempts: 2 },
122
- cache: { enabled: true, ttlMs: 60000 },
168
+ const res = await client.get("/data", {
169
+ useCache: true,
170
+ cacheTtlMs: 30000,
171
+ cacheKey: "my-custom-key",
123
172
  });
124
- async function getUser(id) {
125
- const res = await client.request(`/users/${id}`);
126
- if (res.error) {
127
- console.error("Error:", res.error);
128
- } else {
129
- console.log("User:", res.data);
130
- }
131
- }
132
173
  ```
133
174
 
134
175
  ---
135
176
 
136
- #### RequestExecutor
137
-
138
- Wrapper for REST requests with retry and timeout support.
177
+ ### Auth Provider
139
178
 
140
- #### Example
179
+ Automatically inject an `Authorization: Bearer <token>` header before every request. On a `401` response, `onUnauthorized` is called (e.g. to refresh the token) and the request is retried **once** — preventing infinite loops.
141
180
 
142
181
  ```js
143
- import { RequestExecutor } from "rest-pipeline-js";
144
- const executor = new RequestExecutor({ baseURL: "https://api.example.com" });
145
- async function fetchData() {
146
- try {
147
- const res = await executor.execute("/data", { method: "GET" }, 3, 3000);
148
- if (res.error) {
149
- console.error("Error:", res.error);
150
- } else {
151
- console.log("Data:", res.data);
152
- }
153
- } catch (err) {
154
- console.error("Critical error:", err);
155
- }
156
- }
182
+ const client = createRestClient({
183
+ baseURL: "https://api.example.com",
184
+ auth: {
185
+ getToken: async () => {
186
+ return localStorage.getItem("access_token") ?? "";
187
+ },
188
+ onUnauthorized: async () => {
189
+ // refresh token, update storage
190
+ const newToken = await refreshAccessToken();
191
+ localStorage.setItem("access_token", newToken);
192
+ },
193
+ },
194
+ });
195
+
196
+ // Authorization: Bearer <token> is added automatically to every request
197
+ const res = await client.get("/profile");
157
198
  ```
158
199
 
159
200
  ---
160
201
 
161
- #### PipelineOrchestrator
202
+ ### Log Sanitization
162
203
 
163
- Main class for building and managing a pipeline of sequential stages.
164
-
165
- ##### Key methods and parameters
166
-
167
- - **constructor(pipelineConfig, httpConfig, sharedData?, options?)**
168
- - `pipelineConfig` — array of stages, their params, conditions, handlers
169
- - `httpConfig` — HTTP client config
170
- - `sharedData` — shared data pool between stages
171
- - `options.autoReset` — whether to reset state after finish
172
- - **run(onStepPause?, externalSignal?)** — run the pipeline
173
- - `onStepPause(stepIndex, stepResult, stageResults)` — callback for pause/confirmation/modification between stages
174
- - `externalSignal` — external AbortSignal
175
- - Returns: `{ stageResults, success }`
176
- - **rerunStep(stepKey, options?)** — rerun a single stage
177
- - **subscribeProgress(listener)** — subscribe to progress updates
178
- - **subscribeStageResults(listener)** — subscribe to stage results
179
- - **subscribeStepProgress(stepKey, listener)** — subscribe to a specific stage's progress
180
- - **on(eventName, handler)** — universal event subscription
181
- - **onStepStart/Finish/Error(handler)** — subscribe to stage events
182
- - **getProgress()** — get current progress snapshot
183
- - **getProgressRef()** — get progress object (for reactivity)
184
- - **getLogs()** — get pipeline logs
185
- - **abort()** — abort pipeline
186
- - **isAborted()** — check if pipeline was aborted
187
-
188
- ##### Stage parameters
189
-
190
- - `key` — unique stage key
191
- - `command` — endpoint/command for request
192
- - `method` — HTTP method
193
- - `dependsOn` — array of stage keys this depends on
194
- - `condition({ prev, allResults, sharedData })` — condition function
195
- - `before({ prev, allResults, sharedData })` — pre-processing hook (called before request; can modify input)
196
- - `request({ prev, allResults, sharedData })` — custom request function. If before returns a value, it will be passed to request as prev.
197
- - `after({ result, allResults, sharedData })` — post-processing hook (called after request, before next stage; can modify result)
198
- - `pauseBefore` — optional pause (in ms) before request execution
199
- - `pauseAfter` — optional pause (in ms) after request execution
200
- - `retryCount`, `timeoutMs` — per-stage retry/timeout
201
- - `errorHandler({ error, key, sharedData })` — custom error handler
202
-
203
- ##### Step execution flow diagram
204
+ Mask sensitive headers in metrics callbacks (`onRequestStart` / `onRequestEnd`) so they never appear in logs.
204
205
 
205
- ```
206
- ┌────────────┐
207
- │ before │
208
- │ (optional) │
209
- └─────┬──────┘
210
-
211
-
212
- ┌────────────┐
213
- │ request │
214
- └─────┬──────┘
215
-
216
-
217
- ┌────────────┐
218
- │ after │
219
- │ (optional) │
220
- └─────┬──────┘
221
-
222
-
223
- ┌────────────┐
224
- │ next step │
225
- └────────────┘
226
-
227
- If an error occurs at any stage:
228
- └─► errorHandler (if defined) → error result
229
- ```
206
+ ```js
207
+ import { createRestClient, DEFAULT_SENSITIVE_HEADERS } from "rest-pipeline-js";
230
208
 
231
- #### Example
209
+ // DEFAULT_SENSITIVE_HEADERS includes: authorization, x-api-key, x-auth-token,
210
+ // cookie, set-cookie, proxy-authorization
232
211
 
233
- ```js
234
- import { PipelineOrchestrator } from "rest-pipeline-js";
235
- const pipelineConfig = {
236
- stages: [
237
- { key: "first", command: "/api/first", method: "POST" },
238
- {
239
- key: "second",
240
- command: "/api/second",
241
- method: "POST",
242
- dependsOn: ["first"],
212
+ const client = createRestClient({
213
+ baseURL: "https://api.example.com",
214
+ sanitizeHeaders: true, // opt-in — disabled by default
215
+ sensitiveHeaders: ["x-internal-secret"], // extend the default list
216
+ metrics: {
217
+ onRequestStart: (info) => {
218
+ // info.requestHeaders — sensitive values replaced with "REDACTED"
219
+ console.log(info.requestHeaders);
243
220
  },
244
- ],
245
- };
246
- const httpConfig = { baseURL: "https://api.example.com" };
247
- const sharedData = { sessionId: "abc" };
248
- const orchestrator = new PipelineOrchestrator(
249
- pipelineConfig,
250
- httpConfig,
251
- sharedData,
252
- );
253
- orchestrator.subscribeProgress((progress) => {
254
- console.log("Progress:", progress);
255
- });
256
- orchestrator.on("step:first:success", (payload) => {
257
- console.log("First stage done:", payload.data);
221
+ },
258
222
  });
259
- orchestrator
260
- .run(async (i, result) => {
261
- await new Promise((r) => setTimeout(r, 1000));
262
- return result;
263
- })
264
- .then((result) => console.log("Pipeline finished:", result))
265
- .catch((err) => console.error("Pipeline error:", err));
266
223
  ```
267
224
 
268
- ---
269
-
270
- #### ProgressTracker
271
-
272
- Internal class for tracking pipeline progress.
273
-
274
- #### Example
225
+ You can also use `sanitizeHeadersMap` directly:
275
226
 
276
227
  ```js
277
- import { ProgressTracker } from "rest-pipeline-js";
278
- const tracker = new ProgressTracker(3);
279
- tracker.subscribe((progress) => {
280
- console.log("Current progress:", progress);
281
- });
282
- tracker.updateStage(1, "success");
283
- console.log(tracker.getProgress());
284
- ```
285
-
286
- ---
287
-
288
- #### ErrorHandler
289
-
290
- Class for handling pipeline stage errors.
291
-
292
- #### Example
228
+ import { sanitizeHeadersMap } from "rest-pipeline-js";
293
229
 
294
- ```js
295
- import { ErrorHandler } from "rest-pipeline-js";
296
- const handler = new ErrorHandler();
297
- const error = handler.handle(new Error("fail"), "step1");
298
- console.log(error); // { type: 'unknown', error: [Error], stageKey: 'step1' }
230
+ const safe = sanitizeHeadersMap(
231
+ { authorization: "Bearer abc", "content-type": "application/json" },
232
+ ["x-custom-secret"],
233
+ );
234
+ // { authorization: "REDACTED", "content-type": "application/json" }
299
235
  ```
300
236
 
301
- #### Types and interfaces
302
-
303
- - **HttpConfig** — REST client config (baseURL, timeout, headers, retry, cache, rateLimit, metrics)
304
- - **ApiError** — API error description
305
- - **ApiResponse<T>** — API response (data, error, status)
306
- - **PipelineConfig, PipelineResult, PipelineStepEvent, PipelineStepStatus** — pipeline and stage types
307
-
308
237
  ---
309
238
 
310
- ### Vue integration
239
+ #### RequestExecutor
311
240
 
312
- #### Example: use in Vue component
241
+ Wrapper for REST requests with retry, timeout (via AbortController), Retry-After header support, and backoff.
313
242
 
314
243
  ```js
315
- <script setup>
316
- import { ref } from 'vue';
317
- import { PipelineOrchestrator, usePipelineProgressVue, usePipelineRunVue } from 'rest-pipeline-js/vue';
318
- const pipelineConfig = { stages: [/* ... */] };
319
- const httpConfig = { baseURL: 'https://api.example.com' };
320
- const orchestrator = new PipelineOrchestrator(pipelineConfig, httpConfig);
321
- const progress = usePipelineProgressVue(orchestrator);
322
- const { run, running, result, error } = usePipelineRunVue(orchestrator);
323
- </script>
324
- <template>
325
- <div>
326
- <div>Current stage: {{ progress.value.currentStage }}</div>
327
- <button @click="run()" :disabled="running">Start</button>
328
- <div v-if="result">Done: {{ result }}</div>
329
- <div v-if="error">Error: {{ error.message }}</div>
330
- </div>
331
- </template>
332
- ```
333
-
334
- ---
335
-
336
- Composition functions for Vue 3 (import from `rest-pipeline-js/vue`):
337
-
338
- - **usePipelineProgressVue(orchestrator)** — reactive pipeline progress (Ref<PipelineProgress>)
339
- - **usePipelineRunVue(orchestrator)** — run pipeline and get reactive status (run, running, result, error)
340
- - **usePipelineStepEventVue(orchestrator, stepKey, eventType)** — subscribe to stage events (success, error, progress)
341
- - **usePipelineLogsVue(orchestrator)** — reactive pipeline logs
342
- - **useRerunPipelineStepVue(orchestrator)** — rerun a stage
343
- - **useRestClientVue(config)** — reactive REST client (computed)
344
-
345
- ---
346
-
347
- ### React integration
244
+ import { RequestExecutor } from "rest-pipeline-js";
348
245
 
349
- #### Example: use in React component
246
+ const executor = new RequestExecutor({
247
+ baseURL: "https://api.example.com",
248
+ retry: {
249
+ attempts: 3,
250
+ delayMs: 500,
251
+ backoffMultiplier: 2,
252
+ retriableStatus: [429, 500, 502, 503],
253
+ maxRetryAfterMs: 30000, // cap Retry-After at 30 s
254
+ },
255
+ });
350
256
 
351
- ```jsx
352
- import React from "react";
353
- import {
354
- PipelineOrchestrator,
355
- usePipelineProgressReact,
356
- usePipelineRunReact,
357
- } from "rest-pipeline-js/react";
358
- const pipelineConfig = {
359
- stages: [
360
- /* ... */
361
- ],
362
- };
363
- const httpConfig = { baseURL: "https://api.example.com" };
364
- const orchestrator = new PipelineOrchestrator(pipelineConfig, httpConfig);
365
- export function PipelineComponent() {
366
- const progress = usePipelineProgressReact(orchestrator);
367
- const [run, { running, result, error }] = usePipelineRunReact(orchestrator);
368
- return (
369
- <div>
370
- <div>Current stage: {progress.currentStage}</div>
371
- <button onClick={() => run()} disabled={running}>
372
- Start
373
- </button>
374
- {result && <div>Done: {JSON.stringify(result)}</div>}
375
- {error && <div>Error: {error.message}</div>}
376
- </div>
377
- );
378
- }
257
+ // 5th arg: external AbortSignal (e.g. from orchestrator.abort())
258
+ const res = await executor.execute("/data", undefined, 3, 5000, signal);
379
259
  ```
380
260
 
381
- ---
382
-
383
- Hooks for React (import from `rest-pipeline-js/react`):
384
-
385
- - **usePipelineProgressReact(orchestrator)** — subscribe to pipeline progress (PipelineProgress)
386
- - **usePipelineRunReact(orchestrator)** — run pipeline and get status ([run, { running, result, error }])
387
- - **usePipelineStepEventReact(orchestrator, stepKey, eventType)** — subscribe to stage events (success/error/progress)
388
- - **usePipelineLogsReact(orchestrator)** — subscribe to pipeline logs
389
- - **useRerunPipelineStepReact(orchestrator)** — rerun a stage
390
- - **useRestClientReact(config)** — memoized REST client
391
-
392
- ---
393
-
394
- ## Requirements
395
-
396
- - Node.js >= 14.0.0
397
- - Modern browser with ES2020 support
261
+ When the server returns a `Retry-After` header (numeric seconds or HTTP-date), that delay takes priority over the backoff formula. Values exceeding `maxRetryAfterMs` are clamped to the cap. Timeout is enforced via `AbortController` — the actual HTTP request is cancelled, not just the promise.
398
262
 
399
263
  ---
400
264
 
401
- ## Entry points and imports
402
-
403
- The package has three entry points so that bundlers do not pull in React when you only use core or Vue.
404
-
405
- | Entry point | Use for | Contents |
406
- |-------------|---------|----------|
407
- | `rest-pipeline-js` | Core only | `PipelineOrchestrator`, `createRestClient`, types, `rest-client`, `request-executor`, `error-handler`, `progress-tracker`. No Vue/React. |
408
- | `rest-pipeline-js/vue` | Vue projects | Everything from core + Vue hooks: `usePipelineProgressVue`, `usePipelineRunVue`, `useRestClientVue`, `usePipelineStepEventVue`, `usePipelineLogsVue`, `useRerunPipelineStepVue`. |
409
- | `rest-pipeline-js/react` | React projects | Everything from core + React hooks: `usePipelineProgressReact`, `usePipelineRunReact`, `useRestClientReact`, `usePipelineStepEventReact`, `usePipelineLogsReact`, `useRerunPipelineStepReact`. |
410
-
411
- **Recommended imports:**
412
-
413
- - **Core only** (no framework):
414
-
415
- ```js
416
- import { createRestClient, PipelineOrchestrator } from "rest-pipeline-js";
417
- ```
418
-
419
- - **Vue** (core + Vue hooks):
420
-
421
- ```js
422
- import { PipelineOrchestrator, usePipelineRunVue } from "rest-pipeline-js/vue";
423
- ```
424
-
425
- - **React** (core + React hooks):
426
-
427
- ```js
428
- import { PipelineOrchestrator, usePipelineRunReact } from "rest-pipeline-js/react";
429
- ```
430
-
431
- If you use only `rest-pipeline-js` or `rest-pipeline-js/vue`, the bundler will not resolve or include `react`. The package sets `sideEffects: false` and declares `react`/`react-dom` as `peerDependencies` for the React entry point.
265
+ #### PipelineOrchestrator
432
266
 
433
- If you want, I can prepare a release (bump version and build) with these changes.
267
+ Main class for building and managing a pipeline of sequential (and parallel) stages.
434
268
 
435
- ## Development & Contribution
269
+ ##### Constructor
436
270
 
437
- ```bash
438
- # Clone repository
439
- git clone https://github.com/macrulezru/pipeline-js.git
440
- cd pipeline-js
441
- npm install
442
- npm test
443
- npm run lint
271
+ ```js
272
+ new PipelineOrchestrator({
273
+ config, // PipelineConfig — stages and optional middleware
274
+ httpConfig?, // HttpConfig — HTTP client settings
275
+ sharedData?, // Record<string, any> — shared pool across all stages
276
+ options?, // { autoReset?: boolean }
277
+ })
444
278
  ```
445
279
 
446
- ---
447
-
448
- ## License
280
+ ##### Key methods
281
+
282
+ | Method | Description |
283
+ | ------------------------------------------ | ------------------------------------------------------------------------------------- |
284
+ | `run(onStepPause?, externalSignal?)` | Execute all stages. Returns `{ stageResults, success }` |
285
+ | `rerunStep(stepKey, options?)` | Re-execute a single stage (respects condition, before, after, middleware) |
286
+ | `abort()` | Abort pipeline execution (cancels the current HTTP request via AbortSignal) |
287
+ | `isAborted()` | Check if pipeline was aborted |
288
+ | `pause()` | Pause after the current stage completes |
289
+ | `resume()` | Resume a paused pipeline |
290
+ | `isPaused()` | Check if pipeline is paused |
291
+ | `exportState()` | Serialize stageResults and logs to a plain object |
292
+ | `importState(state)` | Restore stageResults and logs from a snapshot |
293
+ | `subscribeProgress(listener)` | Subscribe to progress updates |
294
+ | `subscribeStageResults(listener)` | Subscribe to stageResults changes |
295
+ | `subscribeStepProgress(stepKey, listener)` | Subscribe to a specific stage's progress |
296
+ | `on(eventName, handler)` | Subscribe to any event (`step:<key>:start\|success\|error\|skipped\|progress`, `log`) |
297
+ | `onStepStart/Finish/Error(handler)` | Subscribe to stage lifecycle events |
298
+ | `getProgress()` | Get current progress snapshot |
299
+ | `getLogs()` | Get all pipeline logs |
300
+ | `clearStageResults()` | Reset results and progress |
301
+
302
+ ##### Stage parameters (PipelineStageConfig)
303
+
304
+ | Parameter | Description |
305
+ | --------------------------------------------- | ------------------------------------------------------------------------ |
306
+ | `key` | Unique stage identifier |
307
+ | `request({ prev, allResults, sharedData })` | Main stage function — return value becomes the stage result |
308
+ | `condition({ prev, allResults, sharedData })` | If returns `false`, stage is skipped with status `"skipped"` |
309
+ | `before({ prev, allResults, sharedData })` | Pre-processing hook — returned value replaces `prev` passed to `request` |
310
+ | `after({ result, allResults, sharedData })` | Post-processing hook — returned value replaces the stage result |
311
+ | `errorHandler({ error, key, sharedData })` | Per-stage error handler |
312
+ | `retryCount` | Override retry count for this stage |
313
+ | `timeoutMs` | Override timeout for this stage |
314
+ | `pauseBefore` | Delay in ms before executing `request` |
315
+ | `pauseAfter` | Delay in ms after executing `request` |
316
+
317
+ ##### Stage execution flow
449
318
 
450
- MIT
451
-
452
- ---
453
-
454
- ## Author
455
-
456
- Danil Lisin Vladimirovich aka Macrulez
457
-
458
- GitHub: [macrulezru](https://github.com/macrulezru)
459
-
460
- Website: [macrulez.ru](https://macrulez.ru/)
461
-
462
- ---
463
-
464
- ## Support
465
-
466
- Questions and bugs — via [issue](https://github.com/macrulezru/pipeline-js/issues)
467
-
468
- ## Установка
469
-
470
- ```sh
471
- npm i rest-pipeline-js
472
319
  ```
473
-
474
- ## Возможности и API
475
-
476
- ### Базовый модуль (rest-pipeline-js)
477
-
478
- #### Пример: создание REST клиента и выполнение запроса
479
-
480
- ```js
481
- import { createRestClient } from "rest-pipeline-js";
482
-
483
- const client = createRestClient({
484
- baseURL: "https://api.example.com",
485
- timeout: 5000,
486
- headers: { Authorization: "Bearer TOKEN" },
487
- });
488
-
489
- async function fetchUser(id) {
490
- const res = await client.request(`/users/${id}`);
491
- if (res.error) {
492
- console.error(res.error);
493
- } else {
494
- console.log(res.data);
495
- }
496
- }
320
+ condition? → false → [status: skipped] → next stage
321
+ true
322
+ middleware.beforeEach
323
+
324
+ pauseBefore
325
+
326
+ before() hook
327
+
328
+ request()
329
+
330
+ after() hook
331
+
332
+ pauseAfter
333
+
334
+ middleware.afterEach
335
+
336
+ [status: success] next stage
337
+
338
+ On error at any point:
339
+ └─► stage.errorHandler (if set) → middleware.onError → [status: error] → stop
497
340
  ```
498
341
 
499
- #### Пример: запуск pipeline, обработка ошибок, отслеживание выполнения и использование общего пула данных
342
+ ---
500
343
 
501
- ```js
502
- import { PipelineOrchestrator } from "rest-pipeline-js";
344
+ ### Parallel stages
503
345
 
504
- const pipelineConfig = {
505
- stages: [
506
- {
507
- key: "step1",
508
- command: "/api/step1",
509
- method: "POST",
510
- // Можно добавить кастомные параметры шага
511
- },
512
- {
513
- key: "step2",
514
- command: "/api/step2",
515
- method: "POST",
516
- dependsOn: ["step1"], // step2 выполнится только после step1
517
- },
518
- ],
519
- };
346
+ Group stages for concurrent execution using `parallel`:
520
347
 
521
- const httpConfig = {
522
- baseURL: "https://api.example.com",
523
- timeout: 7000,
524
- headers: { Authorization: "Bearer TOKEN" },
525
- retry: { attempts: 2, delayMs: 1000 },
526
- cache: { enabled: true, ttlMs: 60000 },
527
- rateLimit: { maxConcurrent: 2 },
528
- metrics: {
529
- onRequestStart: (info) => console.log("Start:", info),
530
- onRequestEnd: (info) => console.log("End:", info),
348
+ ```js
349
+ const orchestrator = new PipelineOrchestrator({
350
+ config: {
351
+ stages: [
352
+ // Sequential stage
353
+ { key: "auth", request: async () => getToken() },
354
+
355
+ // Parallel group — all run concurrently
356
+ {
357
+ key: "load-data",
358
+ parallel: [
359
+ { key: "loadUsers", request: async () => fetchUsers() },
360
+ { key: "loadProducts", request: async () => fetchProducts() },
361
+ { key: "loadSettings", request: async () => fetchSettings() },
362
+ ],
363
+ },
364
+
365
+ // Sequential stage after the group
366
+ { key: "render", request: async ({ allResults }) => render(allResults) },
367
+ ],
531
368
  },
532
- };
533
-
534
- // Общий пул данных между шагами
535
- const sharedData = { sessionId: "abc123" };
536
-
537
- const orchestrator = new PipelineOrchestrator(
538
- pipelineConfig,
539
- httpConfig,
540
- sharedData,
541
- { autoReset: true },
542
- );
543
-
544
- // Отслеживание прогресса
545
- orchestrator.subscribeProgress((progress) => {
546
- console.log(
547
- "Текущий шаг:",
548
- progress.currentStage,
549
- "Статусы:",
550
- progress.stageStatuses,
551
- );
552
- });
553
-
554
- // Подписка на события успеха/ошибки шага
555
- orchestrator.on("step:step1:success", (payload) => {
556
- console.log("Step 1 завершён успешно:", payload.data);
557
- });
558
- orchestrator.on("step:step2:error", (payload) => {
559
- console.error("Ошибка на step2:", payload.error);
560
369
  });
561
-
562
- // Подписка на все логи pipeline
563
- orchestrator.on("log", () => {
564
- console.log("Логи:", orchestrator.getLogs());
565
- });
566
-
567
- // Запуск pipeline с передачей параметров
568
- orchestrator
569
- .run({ foo: "bar" })
570
- .then((result) => {
571
- console.log("Pipeline завершён. Итог:", result);
572
- // Доступ к результатам всех шагов:
573
- console.log("Результаты шагов:", result.stageResults);
574
- })
575
- .catch((err) => {
576
- // Глобальная обработка ошибок pipeline
577
- console.error("Pipeline error:", err);
578
- });
579
-
580
- // Повторный запуск шага (например, после ошибки)
581
- // orchestrator.rerunStep('step2');
582
370
  ```
583
371
 
584
- ---
372
+ - All stages in a `parallel` group run simultaneously via `Promise.all`.
373
+ - If **any** stage in the group fails, the pipeline stops and marks `success: false`.
374
+ - Each parallel stage has its own key and result in `stageResults`.
375
+ - `rerunStep(key)` works for stages inside parallel groups too.
585
376
 
586
377
  ---
587
378
 
588
- Модуль предоставляет универсальный механизм для построения и управления REST API pipeline с поддержкой прогресса, обработки ошибок, подписки на события и расширяемости.
379
+ ### Global middleware
589
380
 
590
- #### Основные классы и функции
591
-
592
- ---
593
-
594
- ### createRestClient(config: HttpConfig): RestClient
595
-
596
- Создаёт REST-клиент с поддержкой расширенных возможностей для работы с HTTP API.
597
-
598
- #### Пример
381
+ Apply hooks to every stage without modifying individual stage configs:
599
382
 
600
383
  ```js
601
- import { createRestClient } from "rest-pipeline-js";
602
-
603
- const client = createRestClient({
604
- baseURL: "https://api.example.com",
605
- timeout: 5000,
606
- headers: { Authorization: "Bearer TOKEN" },
607
- retry: { attempts: 2 },
608
- cache: { enabled: true, ttlMs: 60000 },
384
+ const orchestrator = new PipelineOrchestrator({
385
+ config: {
386
+ stages: [
387
+ /* ... */
388
+ ],
389
+ middleware: {
390
+ beforeEach: async ({ stage, index, sharedData }) => {
391
+ console.log(`[${index}] Starting: ${stage.key}`);
392
+ sharedData.startedAt = Date.now();
393
+ },
394
+ afterEach: async ({ stage, index, result, sharedData }) => {
395
+ const ms = Date.now() - sharedData.startedAt;
396
+ console.log(`[${index}] Done: ${stage.key} in ${ms}ms`, result.data);
397
+ },
398
+ onError: async ({ stage, error, sharedData }) => {
399
+ await reportError({ stage: stage.key, error, context: sharedData });
400
+ },
401
+ },
402
+ },
609
403
  });
610
-
611
- async function getUser(id) {
612
- const res = await client.request(`/users/${id}`);
613
- if (res.error) {
614
- console.error("Ошибка:", res.error);
615
- } else {
616
- console.log("Пользователь:", res.data);
617
- }
618
- }
619
404
  ```
620
405
 
621
- ---
622
-
623
- ### RequestExecutor
624
-
625
- Обёртка для выполнения REST-запросов с поддержкой автоматического retry и таймаута.
626
-
627
- #### Пример
628
-
629
- ```js
630
- import { RequestExecutor } from "rest-pipeline-js";
631
-
632
- const executor = new RequestExecutor({ baseURL: "https://api.example.com" });
633
-
634
- async function fetchData() {
635
- try {
636
- const res = await executor.execute("/data", { method: "GET" }, 3, 3000);
637
- if (res.error) {
638
- console.error("Ошибка:", res.error);
639
- } else {
640
- console.log("Данные:", res.data);
641
- }
642
- } catch (err) {
643
- console.error("Критическая ошибка:", err);
644
- }
645
- }
646
- ```
406
+ Middleware runs in addition to (not instead of) per-stage `errorHandler`.
647
407
 
648
408
  ---
649
409
 
650
- ### PipelineOrchestrator
651
-
652
- Основной класс для построения и управления конвейером (pipeline) из последовательных шагов.
653
-
654
- #### Основные методы и параметры
655
-
656
- - **constructor(pipelineConfig, httpConfig, sharedData?, options?)** — создание экземпляра:
657
- - `pipelineConfig` — массив шагов (stages), их параметры, условия, обработчики
658
- - `httpConfig` — настройки HTTP клиента
659
- - `sharedData` — общий пул данных между шагами
660
- - `options.autoReset` — сбрасывать ли состояние после завершения
661
-
662
- - **run(onStepPause?, externalSignal?)** — запуск конвейера
663
- - `onStepPause(stepIndex, stepResult, stageResults)` — callback для паузы/подтверждения/модификации результата между шагами (можно реализовать задержку, диалог, логику)
664
- - `externalSignal` — внешний AbortSignal для отмены
665
- - Возвращает: `{ stageResults, success }`
666
-
667
- - **rerunStep(stepKey, options?)** — повторно выполнить один шаг
668
- - `onStepPause` и `externalSignal` аналогично run
669
- - Возвращает результат шага
670
-
671
- - **subscribeProgress(listener)** — подписка на прогресс выполнения (listener получает PipelineProgress)
672
- - **subscribeStageResults(listener)** — подписка на изменения результатов всех шагов
673
- - **subscribeStepProgress(stepKey, listener)** — подписка на прогресс конкретного шага
674
- - **on(eventName, handler)** — универсальная подписка на события:
675
- - `step:<stepKey>:start|success|error|progress` — события по шагам
676
- - `log` — новые логи
677
- - любые кастомные события
678
- - **onStepStart/Finish/Error(handler)** — подписка на начало/успех/ошибку шага (PipelineStepEvent)
679
- - **getProgress()** — получить текущий прогресс (snapshot)
680
- - **getProgressRef()** — получить ссылку на объект прогресса (для реактивности)
681
- - **getLogs()** — получить массив логов pipeline
682
- - **abort()** — отменить выполнение пайплайна
683
- - **isAborted()** — проверить, был ли пайплайн отменён
684
-
685
- #### Важные параметры шага (stage):
686
-
687
- - `key` — уникальный ключ шага
688
- - `command` — команда/endpoint для запроса
689
- - `method` — HTTP-метод
690
- - `dependsOn` — массив ключей шагов, от которых зависит этот шаг
691
- - `condition({ prev, allResults, sharedData })` — функция-условие для выполнения шага
692
- - `before({ prev, allResults, sharedData })` — before-хук (вызывается перед запросом; может изменить входные данные)
693
- - `request({ prev, allResults, sharedData })` — кастомная функция запроса (альтернатива command). Если before возвращает значение, оно будет передано в request как prev.
694
- - `after({ result, allResults, sharedData })` — post-processing хук (вызывается после запроса, до перехода к следующему этапу; может модифицировать результат)
695
- - `pauseBefore` — опциональная пауза (в миллисекундах) перед выполнением запроса
696
- - `pauseAfter` — опциональная пауза (в миллисекундах) после выполнения запроса
697
- - `retryCount`, `timeoutMs` — индивидуальные настройки повтора и таймаута
698
- - `errorHandler({ error, key, sharedData })` — обработчик ошибок шага
699
-
700
- ##### Диаграмма выполнения шага
701
-
702
- ```
703
- ┌───────────────┐
704
- │ before │
705
- │ (опционально) │
706
- └─────┬─────────┘
707
-
708
-
709
- ┌────────────┐
710
- │ request │
711
- └─────┬──────┘
712
-
713
-
714
- ┌───────────────┐
715
- │ after │
716
- │ (опционально) │
717
- └─────┬─────────┘
718
-
719
-
720
- ┌────────────┐
721
- │ следующий │
722
- │ шаг │
723
- └────────────┘
724
-
725
- Если возникает ошибка на любом этапе:
726
- └─► errorHandler (если определён) → результат с ошибкой
727
- ```
410
+ ### Pause / Resume
728
411
 
729
- #### Пример
412
+ Pause the pipeline after a stage and resume later:
730
413
 
731
414
  ```js
732
- import { PipelineOrchestrator } from "rest-pipeline-js";
415
+ const orchestrator = new PipelineOrchestrator({ config });
733
416
 
734
- const pipelineConfig = {
735
- stages: [
736
- { key: "first", command: "/api/first", method: "POST" },
737
- {
738
- key: "second",
739
- command: "/api/second",
740
- method: "POST",
741
- dependsOn: ["first"],
742
- },
743
- ],
744
- };
745
- const httpConfig = { baseURL: "https://api.example.com" };
746
- const sharedData = { sessionId: "abc" };
747
- const orchestrator = new PipelineOrchestrator(
748
- pipelineConfig,
749
- httpConfig,
750
- sharedData,
751
- );
417
+ // Pause after step1 completes
418
+ orchestrator.on("step:step1:success", () => orchestrator.pause());
752
419
 
753
- orchestrator.subscribeProgress((progress) => {
754
- console.log("Прогресс:", progress);
755
- });
420
+ const runPromise = orchestrator.run();
756
421
 
757
- orchestrator.on("step:first:success", (payload) => {
758
- console.log("Первый шаг выполнен:", payload.data);
759
- });
422
+ // At some point later (e.g. after user confirmation):
423
+ await showConfirmDialog();
424
+ orchestrator.resume();
760
425
 
761
- // Пауза 1 секунда между шагами
762
- orchestrator
763
- .run(async (i, result) => {
764
- await new Promise((r) => setTimeout(r, 1000));
765
- return result;
766
- })
767
- .then((result) => console.log("Pipeline завершён:", result))
768
- .catch((err) => console.error("Ошибка pipeline:", err));
426
+ await runPromise;
769
427
  ```
770
428
 
771
- ---
429
+ - `pause()` — pipeline waits after the current stage finishes (including events).
430
+ - `resume()` — continues from the next stage.
431
+ - `abort()` while paused unblocks the pipeline and terminates it.
772
432
 
773
- ### ProgressTracker
433
+ ---
774
434
 
775
- Внутренний класс для отслеживания прогресса pipeline.
435
+ ### Export / Import state
776
436
 
777
- #### Пример
437
+ Save and restore the pipeline state across page reloads or sessions:
778
438
 
779
439
  ```js
780
- import { ProgressTracker } from "rest-pipeline-js";
440
+ const orchestrator = new PipelineOrchestrator({ config });
441
+ await orchestrator.run();
781
442
 
782
- const tracker = new ProgressTracker(3); // 3 шага
783
- tracker.subscribe((progress) => {
784
- console.log("Текущий прогресс:", progress);
785
- });
786
- tracker.updateStage(1, "success");
787
- console.log(tracker.getProgress());
788
- ```
789
-
790
- ---
443
+ // Save state
444
+ const snapshot = orchestrator.exportState();
445
+ localStorage.setItem("pipelineState", JSON.stringify(snapshot));
791
446
 
792
- ### ErrorHandler
793
-
794
- Класс для обработки ошибок шагов pipeline.
795
-
796
- #### Пример
797
-
798
- ```js
799
- import { ErrorHandler } from "rest-pipeline-js";
447
+ // Later — restore and inspect without re-running
448
+ const saved = JSON.parse(localStorage.getItem("pipelineState"));
449
+ const orchestrator2 = new PipelineOrchestrator({ config });
450
+ orchestrator2.importState(saved);
800
451
 
801
- const handler = new ErrorHandler();
802
- const error = handler.handle(new Error("fail"), "step1");
803
- console.log(error); // { type: 'unknown', error: [Error], stageKey: 'step1' }
452
+ console.log(orchestrator2.getProgress()); // restored progress
453
+ console.log(orchestrator2.getLogs()); // restored logs (timestamps as Date objects)
804
454
  ```
805
455
 
806
- #### Типы и интерфейсы:
807
-
808
- - **HttpConfig** — конфигурация REST клиента (baseURL, timeout, headers, retry, cache, rateLimit, metrics)
809
- - **ApiError** — описание ошибки API
810
- - **ApiResponse<T>** — ответ API (данные, ошибка, статус)
811
- - **PipelineConfig, PipelineResult, PipelineStepEvent, PipelineStepStatus** — описание pipeline и стадий
456
+ `exportState()` returns `{ stageResults, logs }` — a plain JSON-serializable object. Timestamps in logs are stored as ISO strings and restored as `Date` objects on `importState`.
812
457
 
813
458
  ---
814
459
 
815
- ### Расширение для Vue
460
+ ### Vue integration
816
461
 
817
- #### Пример: использование во Vue компоненте
462
+ #### Example: use in Vue component
818
463
 
819
- ```js
464
+ ```vue
820
465
  <script setup>
821
- import { ref } from 'vue';
822
- import { PipelineOrchestrator, usePipelineProgressVue, usePipelineRunVue } from 'rest-pipeline-js/vue';
823
-
824
- const pipelineConfig = { stages: [/* ... */] };
825
- const httpConfig = { baseURL: 'https://api.example.com' };
826
- const orchestrator = new PipelineOrchestrator(pipelineConfig, httpConfig);
827
-
466
+ import {
467
+ PipelineOrchestrator,
468
+ usePipelineProgressVue,
469
+ usePipelineRunVue,
470
+ } from "rest-pipeline-js/vue";
471
+
472
+ const orchestrator = new PipelineOrchestrator({
473
+ config: {
474
+ stages: [
475
+ /* ... */
476
+ ],
477
+ },
478
+ });
828
479
  const progress = usePipelineProgressVue(orchestrator);
829
- const { run, running, result, error } = usePipelineRunVue(orchestrator);
480
+ const { run, running, result, error, abort, pause, resume, rerunStep } =
481
+ usePipelineRunVue(orchestrator);
830
482
  </script>
831
483
 
832
484
  <template>
833
- <div>
834
- <div>Текущий шаг: {{ progress.value.currentStage }}</div>
835
- <button @click="run()" :disabled="running">Старт</button>
836
- <div v-if="result">Готово: {{ result }}</div>
837
- <div v-if="error">Ошибка: {{ error.message }}</div>
838
- </div>
485
+ <div>
486
+ <div>Current stage: {{ progress.currentStage }}</div>
487
+ <button @click="run()" :disabled="running">Start</button>
488
+ <button @click="abort()" :disabled="!running">Abort</button>
489
+ <button @click="pause()">Pause</button>
490
+ <button @click="resume()">Resume</button>
491
+ <div v-if="result">Done: {{ result }}</div>
492
+ <div v-if="error">Error: {{ error.message }}</div>
493
+ </div>
839
494
  </template>
840
495
  ```
841
496
 
842
- ---
843
-
844
- Экспортируются composition-функции для интеграции rest-pipeline-js с Vue 3 (импортировать из `rest-pipeline-js/vue`):
497
+ Composition functions (import from `rest-pipeline-js/vue`):
845
498
 
846
- - **usePipelineProgressVue(orchestrator)** реактивный прогресс pipeline (Ref<PipelineProgress>)
847
- - **usePipelineRunVue(orchestrator)** запуск pipeline и реактивные статусы (run, running, result, error)
848
- - **usePipelineStepEventVue(orchestrator, stepKey, eventType)** подписка на события шага (успех, ошибка, прогресс)
849
- - **usePipelineLogsVue(orchestrator)** реактивные логи pipeline
850
- - **useRerunPipelineStepVue(orchestrator)** функция для повторного запуска шага
851
- - **useRestClientVue(config)** реактивный REST клиент (computed)
499
+ | Function | Returns | Description |
500
+ | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | -------------------------------------- |
501
+ | `usePipelineProgressVue(orchestrator)` | `Ref<PipelineProgress>` | Reactive progress |
502
+ | `usePipelineRunVue(orchestrator)` | `{ run, running, result, error, stageResults, abort, pause, resume, rerunStep, clearStageResults }` | Run pipeline and get reactive state |
503
+ | `usePipelineStepEventVue(orchestrator, stepKey, eventType)` | `Ref<any>` | Last payload for a specific step event |
504
+ | `usePipelineLogsVue(orchestrator)` | `Ref<log[]>` | Reactive logs |
505
+ | `useRerunPipelineStepVue(orchestrator)` | `function` | Bound `rerunStep` |
506
+ | `useRestClientVue(config)` | `ComputedRef<RestClient>` | Reactive REST client |
852
507
 
853
508
  ---
854
509
 
855
- ### Расширение для React
510
+ ### React integration
856
511
 
857
- #### Пример: использование в React компоненте
512
+ #### Example: use in React component
858
513
 
859
514
  ```jsx
860
- import React from "react";
515
+ import { useRef } from "react";
861
516
  import {
862
517
  PipelineOrchestrator,
863
518
  usePipelineProgressReact,
864
519
  usePipelineRunReact,
865
520
  } from "rest-pipeline-js/react";
866
521
 
867
- const pipelineConfig = {
868
- stages: [
869
- /* ... */
870
- ],
871
- };
872
- const httpConfig = { baseURL: "https://api.example.com" };
873
- const orchestrator = new PipelineOrchestrator(pipelineConfig, httpConfig);
522
+ const orchestrator = new PipelineOrchestrator({
523
+ config: {
524
+ stages: [
525
+ /* ... */
526
+ ],
527
+ },
528
+ });
874
529
 
875
530
  export function PipelineComponent() {
876
531
  const progress = usePipelineProgressReact(orchestrator);
877
- const [run, { running, result, error }] = usePipelineRunReact(orchestrator);
532
+ const [run, { running, result, error, abort, pause, resume, rerunStep }] =
533
+ usePipelineRunReact(orchestrator);
878
534
 
879
535
  return (
880
536
  <div>
881
- <div>Текущий шаг: {progress.currentStage}</div>
537
+ <div>Current stage: {progress.currentStage}</div>
882
538
  <button onClick={() => run()} disabled={running}>
883
- Старт
539
+ Start
884
540
  </button>
885
- {result && <div>Готово: {JSON.stringify(result)}</div>}
886
- {error && <div>Ошибка: {error.message}</div>}
541
+ <button onClick={() => abort()} disabled={!running}>
542
+ Abort
543
+ </button>
544
+ <button onClick={() => pause()}>Pause</button>
545
+ <button onClick={() => resume()}>Resume</button>
546
+ {result && <div>Done: {JSON.stringify(result)}</div>}
547
+ {error && <div>Error: {error.message}</div>}
887
548
  </div>
888
549
  );
889
550
  }
890
551
  ```
891
552
 
892
- ---
553
+ Hooks (import from `rest-pipeline-js/react`):
893
554
 
894
- Экспортируются хуки для интеграции rest-pipeline-js с React (импортировать из `rest-pipeline-js/react`):
895
-
896
- - **usePipelineProgressReact(orchestrator)** подписка на прогресс pipeline (PipelineProgress)
897
- - **usePipelineRunReact(orchestrator)** — запуск pipeline и статусы ([run, { running, result, error }])
898
- - **usePipelineStepEventReact(orchestrator, stepKey, eventType)** подписка на события шага (success/error/progress)
899
- - **usePipelineLogsReact(orchestrator)** подписка на логи pipeline
900
- - **useRerunPipelineStepReact(orchestrator)** функция для повторного запуска шага
901
- - **useRestClientReact(config)** мемоизированный REST клиент
555
+ | Hook | Returns | Description |
556
+ | ------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------- |
557
+ | `usePipelineProgressReact(orchestrator)` | `PipelineProgress` | Reactive progress |
558
+ | `usePipelineRunReact(orchestrator)` | `[run, { running, result, error, stageResults, abort, pause, resume, rerunStep }]` | Run pipeline and get state |
559
+ | `usePipelineStepEventReact(orchestrator, stepKey, eventType)` | `any` | Last payload for a specific step event |
560
+ | `usePipelineLogsReact(orchestrator)` | `log[]` | Reactive logs |
561
+ | `useRerunPipelineStepReact(orchestrator)` | `function` | Bound `rerunStep` |
562
+ | `useRestClientReact(config)` | `RestClient` | Memoized REST client |
902
563
 
903
564
  ---
904
565
 
905
- ## Точки входа и импорты (русский)
566
+ ## Entry points
567
+
568
+ | Entry point | Use for | Contents |
569
+ | ------------------------ | -------------- | --------------------------------------------------------------------------- |
570
+ | `rest-pipeline-js` | Core only | `PipelineOrchestrator`, `createRestClient`, types, utilities. No Vue/React. |
571
+ | `rest-pipeline-js/vue` | Vue projects | Core + Vue composition functions |
572
+ | `rest-pipeline-js/react` | React projects | Core + React hooks |
906
573
 
907
- В пакете три точки входа, чтобы при использовании только ядра или Vue сборщик не подтягивал React.
574
+ ```js
575
+ // Core only
576
+ import { createRestClient, PipelineOrchestrator } from "rest-pipeline-js";
908
577
 
909
- | Точка входа | Назначение | Содержимое |
910
- |-------------|------------|------------|
911
- | `rest-pipeline-js` | Только ядро | `PipelineOrchestrator`, `createRestClient`, типы, rest-client, request-executor, error-handler, progress-tracker. Без Vue/React. |
912
- | `rest-pipeline-js/vue` | Проекты на Vue | Всё из ядра + Vue-хуки: usePipelineProgressVue, usePipelineRunVue, useRestClientVue, usePipelineStepEventVue, usePipelineLogsVue, useRerunPipelineStepVue. |
913
- | `rest-pipeline-js/react` | Проекты на React | Всё из ядра + React-хуки: usePipelineProgressReact, usePipelineRunReact, useRestClientReact, usePipelineStepEventReact, usePipelineLogsReact, useRerunPipelineStepReact. |
578
+ // Vue
579
+ import { PipelineOrchestrator, usePipelineRunVue } from "rest-pipeline-js/vue";
580
+
581
+ // React
582
+ import {
583
+ PipelineOrchestrator,
584
+ usePipelineRunReact,
585
+ } from "rest-pipeline-js/react";
586
+ ```
914
587
 
915
- Рекомендуемые импорты: ядро — `rest-pipeline-js`; Vue — `rest-pipeline-js/vue`; React `rest-pipeline-js/react`. Пакет помечен как `sideEffects: false`; `react`/`react-dom` в `peerDependencies` для входа React.
588
+ `sideEffects: false` — unused entry points are tree-shaken. `react`/`react-dom` are `peerDependencies`.
916
589
 
917
590
  ---
918
591
 
919
- ## Требования
592
+ ## Requirements
920
593
 
921
594
  - Node.js >= 14.0.0
922
- - Современный браузер с поддержкой ES2020
595
+ - Modern browser with ES2019+ support
923
596
 
924
597
  ---
925
598
 
926
- ## Разработка и вклад
599
+ ## Development
927
600
 
928
601
  ```bash
929
- # Клонировать репозиторий
930
602
  git clone https://github.com/macrulezru/pipeline-js.git
931
603
  cd pipeline-js
932
604
  npm install
933
605
  npm test
934
- npm run lint
935
606
  ```
936
607
 
937
608
  ---
938
609
 
939
- ## Лицензия
610
+ ## License
940
611
 
941
612
  MIT
942
613
 
943
614
  ---
944
615
 
945
- ## Автор
946
-
947
- Данил Лисин Владимирович aka Macrulez
948
-
949
- GitHub: [macrulezru](https://github.com/macrulezru)
950
-
951
- Сайт: [macrulez.ru](https://macrulez.ru/)
616
+ ## Author
952
617
 
953
- ---
618
+ Danil Lisin Vladimirovich aka Macrulez
954
619
 
955
- ## Поддержка
620
+ GitHub: [macrulezru](https://github.com/macrulezru) · Website: [macrulez.ru](https://macrulez.ru/)
956
621
 
957
- Вопросы и багичерез [issue](https://github.com/macrulezru/pipeline-js/issues)
622
+ Questions and bugs — [issues](https://github.com/macrulezru/pipeline-js/issues)