rest-pipeline-js 1.3.0 → 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.
- package/README.md +215 -599
- package/package.json +1 -1
- package/src/request-executor.ts +45 -4
- package/src/rest-client.ts +79 -4
- package/src/types.ts +48 -0
- package/src/vue-demo/demo.vue +3 -3
- package/tests/rest-client.test.ts +214 -22
package/README.md
CHANGED
|
@@ -22,10 +22,23 @@ import { createRestClient } from "rest-pipeline-js";
|
|
|
22
22
|
const client = createRestClient({
|
|
23
23
|
baseURL: "https://api.example.com",
|
|
24
24
|
timeout: 5000,
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
retry: {
|
|
26
|
+
attempts: 2,
|
|
27
|
+
delayMs: 500,
|
|
28
|
+
backoffMultiplier: 2,
|
|
29
|
+
retriableStatus: [429, 500, 503],
|
|
30
|
+
},
|
|
27
31
|
cache: { enabled: true, ttlMs: 60000 },
|
|
28
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,
|
|
29
42
|
});
|
|
30
43
|
|
|
31
44
|
const res = await client.get("/users/1");
|
|
@@ -35,7 +48,9 @@ console.log(res.data);
|
|
|
35
48
|
await client.patch("/users/1", { name: "Alice" });
|
|
36
49
|
|
|
37
50
|
// Cancellable request
|
|
38
|
-
const req = client.cancellableRequest("my-key", "/search", {
|
|
51
|
+
const req = client.cancellableRequest("my-key", "/search", {
|
|
52
|
+
params: { q: "foo" },
|
|
53
|
+
});
|
|
39
54
|
// Cancel it any time:
|
|
40
55
|
client.cancelRequest("my-key");
|
|
41
56
|
```
|
|
@@ -67,8 +82,10 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
67
82
|
],
|
|
68
83
|
middleware: {
|
|
69
84
|
beforeEach: ({ stage }) => console.log("Starting:", stage.key),
|
|
70
|
-
afterEach: ({ stage, result }) =>
|
|
71
|
-
|
|
85
|
+
afterEach: ({ stage, result }) =>
|
|
86
|
+
console.log("Done:", stage.key, result.data),
|
|
87
|
+
onError: ({ stage, error }) =>
|
|
88
|
+
console.error("Error in", stage.key, error),
|
|
72
89
|
},
|
|
73
90
|
},
|
|
74
91
|
httpConfig: {
|
|
@@ -81,7 +98,12 @@ const orchestrator = new PipelineOrchestrator({
|
|
|
81
98
|
});
|
|
82
99
|
|
|
83
100
|
orchestrator.subscribeProgress((progress) => {
|
|
84
|
-
console.log(
|
|
101
|
+
console.log(
|
|
102
|
+
"Stage:",
|
|
103
|
+
progress.currentStage,
|
|
104
|
+
"Statuses:",
|
|
105
|
+
progress.stageStatuses,
|
|
106
|
+
);
|
|
85
107
|
});
|
|
86
108
|
|
|
87
109
|
orchestrator.on("step:fetchUser:success", (payload) => {
|
|
@@ -103,37 +125,42 @@ Creates a REST client with advanced HTTP features.
|
|
|
103
125
|
|
|
104
126
|
**Available methods:**
|
|
105
127
|
|
|
106
|
-
| Method
|
|
107
|
-
|
|
108
|
-
| `get(url, config?)`
|
|
109
|
-
| `post(url, data?, config?)`
|
|
110
|
-
| `put(url, data?, config?)`
|
|
111
|
-
| `patch(url, data?, config?)`
|
|
112
|
-
| `delete(url, config?)`
|
|
113
|
-
| `request(url, config?)`
|
|
114
|
-
| `cancellableRequest(key, url, config?)` | Request cancellable by key
|
|
115
|
-
| `cancelRequest(key)`
|
|
116
|
-
| `clearCache()`
|
|
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 |
|
|
117
139
|
|
|
118
140
|
**HttpConfig options:**
|
|
119
141
|
|
|
120
|
-
| Option
|
|
121
|
-
|
|
122
|
-
| `baseURL`
|
|
123
|
-
| `timeout`
|
|
124
|
-
| `headers`
|
|
125
|
-
| `withCredentials`
|
|
126
|
-
| `retry.attempts`
|
|
127
|
-
| `retry.delayMs`
|
|
128
|
-
| `retry.backoffMultiplier`
|
|
129
|
-
| `retry.retriableStatus`
|
|
130
|
-
| `cache.enabled`
|
|
131
|
-
| `cache.ttlMs`
|
|
132
|
-
| `rateLimit.maxConcurrent`
|
|
133
|
-
| `rateLimit.maxRequestsPerInterval` | Max requests per time window
|
|
134
|
-
| `rateLimit.intervalMs`
|
|
135
|
-
| `metrics.onRequestStart`
|
|
136
|
-
| `metrics.onRequestEnd`
|
|
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`) |
|
|
137
164
|
|
|
138
165
|
**Per-request cache override:**
|
|
139
166
|
|
|
@@ -147,9 +174,71 @@ const res = await client.get("/data", {
|
|
|
147
174
|
|
|
148
175
|
---
|
|
149
176
|
|
|
177
|
+
### Auth Provider
|
|
178
|
+
|
|
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.
|
|
180
|
+
|
|
181
|
+
```js
|
|
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");
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### Log Sanitization
|
|
203
|
+
|
|
204
|
+
Mask sensitive headers in metrics callbacks (`onRequestStart` / `onRequestEnd`) so they never appear in logs.
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
import { createRestClient, DEFAULT_SENSITIVE_HEADERS } from "rest-pipeline-js";
|
|
208
|
+
|
|
209
|
+
// DEFAULT_SENSITIVE_HEADERS includes: authorization, x-api-key, x-auth-token,
|
|
210
|
+
// cookie, set-cookie, proxy-authorization
|
|
211
|
+
|
|
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);
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
You can also use `sanitizeHeadersMap` directly:
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
import { sanitizeHeadersMap } from "rest-pipeline-js";
|
|
229
|
+
|
|
230
|
+
const safe = sanitizeHeadersMap(
|
|
231
|
+
{ authorization: "Bearer abc", "content-type": "application/json" },
|
|
232
|
+
["x-custom-secret"],
|
|
233
|
+
);
|
|
234
|
+
// { authorization: "REDACTED", "content-type": "application/json" }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
150
239
|
#### RequestExecutor
|
|
151
240
|
|
|
152
|
-
Wrapper for REST requests with retry, timeout (via AbortController), and backoff
|
|
241
|
+
Wrapper for REST requests with retry, timeout (via AbortController), Retry-After header support, and backoff.
|
|
153
242
|
|
|
154
243
|
```js
|
|
155
244
|
import { RequestExecutor } from "rest-pipeline-js";
|
|
@@ -160,7 +249,8 @@ const executor = new RequestExecutor({
|
|
|
160
249
|
attempts: 3,
|
|
161
250
|
delayMs: 500,
|
|
162
251
|
backoffMultiplier: 2,
|
|
163
|
-
retriableStatus: [500, 502, 503],
|
|
252
|
+
retriableStatus: [429, 500, 502, 503],
|
|
253
|
+
maxRetryAfterMs: 30000, // cap Retry-After at 30 s
|
|
164
254
|
},
|
|
165
255
|
});
|
|
166
256
|
|
|
@@ -168,7 +258,7 @@ const executor = new RequestExecutor({
|
|
|
168
258
|
const res = await executor.execute("/data", undefined, 3, 5000, signal);
|
|
169
259
|
```
|
|
170
260
|
|
|
171
|
-
Timeout is enforced via `AbortController` — the actual HTTP request is cancelled, not just the promise.
|
|
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.
|
|
172
262
|
|
|
173
263
|
---
|
|
174
264
|
|
|
@@ -189,40 +279,40 @@ new PipelineOrchestrator({
|
|
|
189
279
|
|
|
190
280
|
##### Key methods
|
|
191
281
|
|
|
192
|
-
| Method
|
|
193
|
-
|
|
194
|
-
| `run(onStepPause?, externalSignal?)`
|
|
195
|
-
| `rerunStep(stepKey, options?)`
|
|
196
|
-
| `abort()`
|
|
197
|
-
| `isAborted()`
|
|
198
|
-
| `pause()`
|
|
199
|
-
| `resume()`
|
|
200
|
-
| `isPaused()`
|
|
201
|
-
| `exportState()`
|
|
202
|
-
| `importState(state)`
|
|
203
|
-
| `subscribeProgress(listener)`
|
|
204
|
-
| `subscribeStageResults(listener)`
|
|
205
|
-
| `subscribeStepProgress(stepKey, listener)` | Subscribe to a specific stage's progress
|
|
206
|
-
| `on(eventName, handler)`
|
|
207
|
-
| `onStepStart/Finish/Error(handler)`
|
|
208
|
-
| `getProgress()`
|
|
209
|
-
| `getLogs()`
|
|
210
|
-
| `clearStageResults()`
|
|
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 |
|
|
211
301
|
|
|
212
302
|
##### Stage parameters (PipelineStageConfig)
|
|
213
303
|
|
|
214
|
-
| Parameter
|
|
215
|
-
|
|
216
|
-
| `key`
|
|
217
|
-
| `request({ prev, allResults, sharedData })`
|
|
218
|
-
| `condition({ prev, allResults, sharedData })` | If returns `false`, stage is skipped with status `"skipped"`
|
|
219
|
-
| `before({ prev, allResults, sharedData })`
|
|
220
|
-
| `after({ result, allResults, sharedData })`
|
|
221
|
-
| `errorHandler({ error, key, sharedData })`
|
|
222
|
-
| `retryCount`
|
|
223
|
-
| `timeoutMs`
|
|
224
|
-
| `pauseBefore`
|
|
225
|
-
| `pauseAfter`
|
|
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` |
|
|
226
316
|
|
|
227
317
|
##### Stage execution flow
|
|
228
318
|
|
|
@@ -293,7 +383,9 @@ Apply hooks to every stage without modifying individual stage configs:
|
|
|
293
383
|
```js
|
|
294
384
|
const orchestrator = new PipelineOrchestrator({
|
|
295
385
|
config: {
|
|
296
|
-
stages: [
|
|
386
|
+
stages: [
|
|
387
|
+
/* ... */
|
|
388
|
+
],
|
|
297
389
|
middleware: {
|
|
298
390
|
beforeEach: async ({ stage, index, sharedData }) => {
|
|
299
391
|
console.log(`[${index}] Starting: ${stage.key}`);
|
|
@@ -357,8 +449,8 @@ const saved = JSON.parse(localStorage.getItem("pipelineState"));
|
|
|
357
449
|
const orchestrator2 = new PipelineOrchestrator({ config });
|
|
358
450
|
orchestrator2.importState(saved);
|
|
359
451
|
|
|
360
|
-
console.log(orchestrator2.getProgress());
|
|
361
|
-
console.log(orchestrator2.getLogs());
|
|
452
|
+
console.log(orchestrator2.getProgress()); // restored progress
|
|
453
|
+
console.log(orchestrator2.getLogs()); // restored logs (timestamps as Date objects)
|
|
362
454
|
```
|
|
363
455
|
|
|
364
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`.
|
|
@@ -371,11 +463,22 @@ console.log(orchestrator2.getLogs()); // restored logs (timestamps
|
|
|
371
463
|
|
|
372
464
|
```vue
|
|
373
465
|
<script setup>
|
|
374
|
-
import {
|
|
466
|
+
import {
|
|
467
|
+
PipelineOrchestrator,
|
|
468
|
+
usePipelineProgressVue,
|
|
469
|
+
usePipelineRunVue,
|
|
470
|
+
} from "rest-pipeline-js/vue";
|
|
375
471
|
|
|
376
|
-
const orchestrator = new PipelineOrchestrator({
|
|
472
|
+
const orchestrator = new PipelineOrchestrator({
|
|
473
|
+
config: {
|
|
474
|
+
stages: [
|
|
475
|
+
/* ... */
|
|
476
|
+
],
|
|
477
|
+
},
|
|
478
|
+
});
|
|
377
479
|
const progress = usePipelineProgressVue(orchestrator);
|
|
378
|
-
const { run, running, result, error, abort, pause, resume, rerunStep } =
|
|
480
|
+
const { run, running, result, error, abort, pause, resume, rerunStep } =
|
|
481
|
+
usePipelineRunVue(orchestrator);
|
|
379
482
|
</script>
|
|
380
483
|
|
|
381
484
|
<template>
|
|
@@ -393,14 +496,14 @@ const { run, running, result, error, abort, pause, resume, rerunStep } = usePipe
|
|
|
393
496
|
|
|
394
497
|
Composition functions (import from `rest-pipeline-js/vue`):
|
|
395
498
|
|
|
396
|
-
| Function
|
|
397
|
-
|
|
398
|
-
| `usePipelineProgressVue(orchestrator)`
|
|
399
|
-
| `usePipelineRunVue(orchestrator)`
|
|
400
|
-
| `usePipelineStepEventVue(orchestrator, stepKey, eventType)` | `Ref<any>`
|
|
401
|
-
| `usePipelineLogsVue(orchestrator)`
|
|
402
|
-
| `useRerunPipelineStepVue(orchestrator)`
|
|
403
|
-
| `useRestClientVue(config)`
|
|
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 |
|
|
404
507
|
|
|
405
508
|
---
|
|
406
509
|
|
|
@@ -416,7 +519,13 @@ import {
|
|
|
416
519
|
usePipelineRunReact,
|
|
417
520
|
} from "rest-pipeline-js/react";
|
|
418
521
|
|
|
419
|
-
const orchestrator = new PipelineOrchestrator({
|
|
522
|
+
const orchestrator = new PipelineOrchestrator({
|
|
523
|
+
config: {
|
|
524
|
+
stages: [
|
|
525
|
+
/* ... */
|
|
526
|
+
],
|
|
527
|
+
},
|
|
528
|
+
});
|
|
420
529
|
|
|
421
530
|
export function PipelineComponent() {
|
|
422
531
|
const progress = usePipelineProgressReact(orchestrator);
|
|
@@ -426,8 +535,12 @@ export function PipelineComponent() {
|
|
|
426
535
|
return (
|
|
427
536
|
<div>
|
|
428
537
|
<div>Current stage: {progress.currentStage}</div>
|
|
429
|
-
<button onClick={() => run()} disabled={running}>
|
|
430
|
-
|
|
538
|
+
<button onClick={() => run()} disabled={running}>
|
|
539
|
+
Start
|
|
540
|
+
</button>
|
|
541
|
+
<button onClick={() => abort()} disabled={!running}>
|
|
542
|
+
Abort
|
|
543
|
+
</button>
|
|
431
544
|
<button onClick={() => pause()}>Pause</button>
|
|
432
545
|
<button onClick={() => resume()}>Resume</button>
|
|
433
546
|
{result && <div>Done: {JSON.stringify(result)}</div>}
|
|
@@ -439,24 +552,24 @@ export function PipelineComponent() {
|
|
|
439
552
|
|
|
440
553
|
Hooks (import from `rest-pipeline-js/react`):
|
|
441
554
|
|
|
442
|
-
| Hook
|
|
443
|
-
|
|
444
|
-
| `usePipelineProgressReact(orchestrator)`
|
|
445
|
-
| `usePipelineRunReact(orchestrator)`
|
|
446
|
-
| `usePipelineStepEventReact(orchestrator, stepKey, eventType)` | `any`
|
|
447
|
-
| `usePipelineLogsReact(orchestrator)`
|
|
448
|
-
| `useRerunPipelineStepReact(orchestrator)`
|
|
449
|
-
| `useRestClientReact(config)`
|
|
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 |
|
|
450
563
|
|
|
451
564
|
---
|
|
452
565
|
|
|
453
566
|
## Entry points
|
|
454
567
|
|
|
455
|
-
| Entry point
|
|
456
|
-
|
|
457
|
-
| `rest-pipeline-js`
|
|
458
|
-
| `rest-pipeline-js/vue`
|
|
459
|
-
| `rest-pipeline-js/react` | React projects | Core + React hooks
|
|
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 |
|
|
460
573
|
|
|
461
574
|
```js
|
|
462
575
|
// Core only
|
|
@@ -466,7 +579,10 @@ import { createRestClient, PipelineOrchestrator } from "rest-pipeline-js";
|
|
|
466
579
|
import { PipelineOrchestrator, usePipelineRunVue } from "rest-pipeline-js/vue";
|
|
467
580
|
|
|
468
581
|
// React
|
|
469
|
-
import {
|
|
582
|
+
import {
|
|
583
|
+
PipelineOrchestrator,
|
|
584
|
+
usePipelineRunReact,
|
|
585
|
+
} from "rest-pipeline-js/react";
|
|
470
586
|
```
|
|
471
587
|
|
|
472
588
|
`sideEffects: false` — unused entry points are tree-shaken. `react`/`react-dom` are `peerDependencies`.
|
|
@@ -504,503 +620,3 @@ Danil Lisin Vladimirovich aka Macrulez
|
|
|
504
620
|
GitHub: [macrulezru](https://github.com/macrulezru) · Website: [macrulez.ru](https://macrulez.ru/)
|
|
505
621
|
|
|
506
622
|
Questions and bugs — [issues](https://github.com/macrulezru/pipeline-js/issues)
|
|
507
|
-
|
|
508
|
-
---
|
|
509
|
-
---
|
|
510
|
-
|
|
511
|
-
## Установка
|
|
512
|
-
|
|
513
|
-
```sh
|
|
514
|
-
npm i rest-pipeline-js
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
## Возможности и API
|
|
518
|
-
|
|
519
|
-
### Базовый модуль (rest-pipeline-js)
|
|
520
|
-
|
|
521
|
-
#### Пример: создание REST клиента и выполнение запросов
|
|
522
|
-
|
|
523
|
-
```js
|
|
524
|
-
import { createRestClient } from "rest-pipeline-js";
|
|
525
|
-
|
|
526
|
-
const client = createRestClient({
|
|
527
|
-
baseURL: "https://api.example.com",
|
|
528
|
-
timeout: 5000,
|
|
529
|
-
headers: { Authorization: "Bearer TOKEN" },
|
|
530
|
-
retry: { attempts: 2, delayMs: 500, backoffMultiplier: 2, retriableStatus: [429, 500, 503] },
|
|
531
|
-
cache: { enabled: true, ttlMs: 60000 },
|
|
532
|
-
rateLimit: { maxConcurrent: 3, maxRequestsPerInterval: 10, intervalMs: 1000 },
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
const res = await client.get("/users/1");
|
|
536
|
-
console.log(res.data);
|
|
537
|
-
|
|
538
|
-
// PATCH-запрос
|
|
539
|
-
await client.patch("/users/1", { name: "Alice" });
|
|
540
|
-
|
|
541
|
-
// Отменяемый запрос
|
|
542
|
-
client.cancellableRequest("my-key", "/search", { params: { q: "foo" } });
|
|
543
|
-
client.cancelRequest("my-key"); // отмена
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
---
|
|
547
|
-
|
|
548
|
-
#### Пример: запуск pipeline, middleware, паузы, общий пул данных
|
|
549
|
-
|
|
550
|
-
```js
|
|
551
|
-
import { PipelineOrchestrator } from "rest-pipeline-js";
|
|
552
|
-
|
|
553
|
-
const orchestrator = new PipelineOrchestrator({
|
|
554
|
-
config: {
|
|
555
|
-
stages: [
|
|
556
|
-
{
|
|
557
|
-
key: "fetchUser",
|
|
558
|
-
request: async ({ sharedData }) => {
|
|
559
|
-
const res = await fetch(`/api/users/${sharedData.userId}`);
|
|
560
|
-
return res.json();
|
|
561
|
-
},
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
key: "processData",
|
|
565
|
-
condition: ({ prev }) => prev !== null,
|
|
566
|
-
before: ({ prev }) => ({ ...prev, processed: true }),
|
|
567
|
-
request: async ({ prev }) => prev,
|
|
568
|
-
after: ({ result }) => ({ ...result, finishedAt: Date.now() }),
|
|
569
|
-
},
|
|
570
|
-
],
|
|
571
|
-
middleware: {
|
|
572
|
-
beforeEach: ({ stage }) => console.log("Старт:", stage.key),
|
|
573
|
-
afterEach: ({ stage, result }) => console.log("Готово:", stage.key, result.data),
|
|
574
|
-
onError: ({ stage, error }) => console.error("Ошибка в", stage.key, error),
|
|
575
|
-
},
|
|
576
|
-
},
|
|
577
|
-
httpConfig: {
|
|
578
|
-
baseURL: "https://api.example.com",
|
|
579
|
-
retry: { attempts: 2, delayMs: 1000, backoffMultiplier: 2 },
|
|
580
|
-
cache: { enabled: true, ttlMs: 60000 },
|
|
581
|
-
},
|
|
582
|
-
sharedData: { userId: 42 },
|
|
583
|
-
options: { autoReset: true },
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
orchestrator.subscribeProgress((progress) => {
|
|
587
|
-
console.log("Шаг:", progress.currentStage, "Статусы:", progress.stageStatuses);
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
const result = await orchestrator.run();
|
|
591
|
-
console.log("Pipeline завершён:", result.success);
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
---
|
|
595
|
-
|
|
596
|
-
### Основные классы и функции
|
|
597
|
-
|
|
598
|
-
#### createRestClient(config: HttpConfig): RestClient
|
|
599
|
-
|
|
600
|
-
Создаёт REST-клиент с поддержкой кэширования, rate limiting, retry и метрик.
|
|
601
|
-
|
|
602
|
-
**Методы клиента:**
|
|
603
|
-
|
|
604
|
-
| Метод | Описание |
|
|
605
|
-
|-------|----------|
|
|
606
|
-
| `get(url, config?)` | GET-запрос |
|
|
607
|
-
| `post(url, data?, config?)` | POST-запрос |
|
|
608
|
-
| `put(url, data?, config?)` | PUT-запрос |
|
|
609
|
-
| `patch(url, data?, config?)` | PATCH-запрос |
|
|
610
|
-
| `delete(url, config?)` | DELETE-запрос |
|
|
611
|
-
| `request(url, config?)` | Произвольный запрос |
|
|
612
|
-
| `cancellableRequest(key, url, config?)` | Запрос, отменяемый по ключу |
|
|
613
|
-
| `cancelRequest(key)` | Отменить запрос по ключу |
|
|
614
|
-
| `clearCache()` | Очистить кэш ответов этого клиента |
|
|
615
|
-
|
|
616
|
-
**Параметры HttpConfig:**
|
|
617
|
-
|
|
618
|
-
| Параметр | Описание |
|
|
619
|
-
|----------|----------|
|
|
620
|
-
| `baseURL` | Базовый URL для всех запросов |
|
|
621
|
-
| `timeout` | Таймаут запроса в мс |
|
|
622
|
-
| `headers` | Заголовки по умолчанию |
|
|
623
|
-
| `withCredentials` | Включить cookies |
|
|
624
|
-
| `retry.attempts` | Количество повторных попыток |
|
|
625
|
-
| `retry.delayMs` | Базовая задержка между попытками в мс |
|
|
626
|
-
| `retry.backoffMultiplier` | Множитель экспоненциального backoff |
|
|
627
|
-
| `retry.retriableStatus` | HTTP-статусы для повтора (например, `[429, 500, 503]`) |
|
|
628
|
-
| `cache.enabled` | Кэшировать GET-ответы |
|
|
629
|
-
| `cache.ttlMs` | Время жизни кэша в мс |
|
|
630
|
-
| `rateLimit.maxConcurrent` | Максимум одновременных запросов |
|
|
631
|
-
| `rateLimit.maxRequestsPerInterval` | Максимум запросов за окно времени |
|
|
632
|
-
| `rateLimit.intervalMs` | Размер временного окна в мс |
|
|
633
|
-
| `metrics.onRequestStart` | Callback при старте запроса |
|
|
634
|
-
| `metrics.onRequestEnd` | Callback при завершении (включает duration и bytes) |
|
|
635
|
-
|
|
636
|
-
**Переопределение кэша на уровне запроса:**
|
|
637
|
-
|
|
638
|
-
```js
|
|
639
|
-
const res = await client.get("/data", {
|
|
640
|
-
useCache: true,
|
|
641
|
-
cacheTtlMs: 30000,
|
|
642
|
-
cacheKey: "my-key",
|
|
643
|
-
});
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
---
|
|
647
|
-
|
|
648
|
-
#### RequestExecutor
|
|
649
|
-
|
|
650
|
-
Обёртка для выполнения запросов с поддержкой retry, таймаута и AbortSignal.
|
|
651
|
-
|
|
652
|
-
```js
|
|
653
|
-
import { RequestExecutor } from "rest-pipeline-js";
|
|
654
|
-
|
|
655
|
-
const executor = new RequestExecutor({
|
|
656
|
-
baseURL: "https://api.example.com",
|
|
657
|
-
retry: {
|
|
658
|
-
attempts: 3,
|
|
659
|
-
delayMs: 500,
|
|
660
|
-
backoffMultiplier: 2,
|
|
661
|
-
retriableStatus: [500, 502, 503],
|
|
662
|
-
},
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
// 5-й аргумент — внешний AbortSignal (например от orchestrator.abort())
|
|
666
|
-
const res = await executor.execute("/data", undefined, 3, 5000, signal);
|
|
667
|
-
```
|
|
668
|
-
|
|
669
|
-
Таймаут реализован через `AbortController` — HTTP-запрос реально отменяется, а не просто отклоняется промис.
|
|
670
|
-
|
|
671
|
-
---
|
|
672
|
-
|
|
673
|
-
#### PipelineOrchestrator
|
|
674
|
-
|
|
675
|
-
Основной класс для управления конвейером последовательных и параллельных шагов.
|
|
676
|
-
|
|
677
|
-
##### Конструктор
|
|
678
|
-
|
|
679
|
-
```js
|
|
680
|
-
new PipelineOrchestrator({
|
|
681
|
-
config, // PipelineConfig — шаги и опциональный middleware
|
|
682
|
-
httpConfig?, // HttpConfig — настройки HTTP-клиента
|
|
683
|
-
sharedData?, // Record<string, any> — общий пул данных для всех шагов
|
|
684
|
-
options?, // { autoReset?: boolean }
|
|
685
|
-
})
|
|
686
|
-
```
|
|
687
|
-
|
|
688
|
-
##### Основные методы
|
|
689
|
-
|
|
690
|
-
| Метод | Описание |
|
|
691
|
-
|-------|----------|
|
|
692
|
-
| `run(onStepPause?, externalSignal?)` | Запустить все шаги. Возвращает `{ stageResults, success }` |
|
|
693
|
-
| `rerunStep(stepKey, options?)` | Повторно выполнить один шаг (учитывает condition, before, after, middleware) |
|
|
694
|
-
| `abort()` | Отменить выполнение (реально отменяет текущий HTTP-запрос через AbortSignal) |
|
|
695
|
-
| `isAborted()` | Проверить, был ли pipeline отменён |
|
|
696
|
-
| `pause()` | Приостановить после завершения текущего шага |
|
|
697
|
-
| `resume()` | Продолжить выполнение после паузы |
|
|
698
|
-
| `isPaused()` | Проверить, приостановлен ли pipeline |
|
|
699
|
-
| `exportState()` | Экспортировать stageResults и логи в простой объект |
|
|
700
|
-
| `importState(state)` | Восстановить состояние из снимка |
|
|
701
|
-
| `subscribeProgress(listener)` | Подписаться на изменения прогресса |
|
|
702
|
-
| `subscribeStageResults(listener)` | Подписаться на изменения результатов шагов |
|
|
703
|
-
| `subscribeStepProgress(stepKey, listener)` | Подписаться на прогресс конкретного шага |
|
|
704
|
-
| `on(eventName, handler)` | Подписаться на события (`step:<key>:start\|success\|error\|skipped\|progress`, `log`) |
|
|
705
|
-
| `onStepStart/Finish/Error(handler)` | Подписка на жизненный цикл шага |
|
|
706
|
-
| `getProgress()` | Получить снимок текущего прогресса |
|
|
707
|
-
| `getLogs()` | Получить все логи pipeline |
|
|
708
|
-
| `clearStageResults()` | Сбросить результаты и прогресс |
|
|
709
|
-
|
|
710
|
-
##### Параметры шага (PipelineStageConfig)
|
|
711
|
-
|
|
712
|
-
| Параметр | Описание |
|
|
713
|
-
|----------|----------|
|
|
714
|
-
| `key` | Уникальный идентификатор шага |
|
|
715
|
-
| `request({ prev, allResults, sharedData })` | Основная функция шага — возвращаемое значение становится результатом |
|
|
716
|
-
| `condition({ prev, allResults, sharedData })` | Если возвращает `false` — шаг пропускается со статусом `"skipped"` |
|
|
717
|
-
| `before({ prev, allResults, sharedData })` | Pre-processing хук — возвращаемое значение заменяет `prev` в `request` |
|
|
718
|
-
| `after({ result, allResults, sharedData })` | Post-processing хук — возвращаемое значение заменяет результат шага |
|
|
719
|
-
| `errorHandler({ error, key, sharedData })` | Обработчик ошибок шага |
|
|
720
|
-
| `retryCount` | Переопределение количества retry для этого шага |
|
|
721
|
-
| `timeoutMs` | Переопределение таймаута для этого шага |
|
|
722
|
-
| `pauseBefore` | Пауза в мс перед выполнением `request` |
|
|
723
|
-
| `pauseAfter` | Пауза в мс после выполнения `request` |
|
|
724
|
-
|
|
725
|
-
##### Диаграмма выполнения шага
|
|
726
|
-
|
|
727
|
-
```
|
|
728
|
-
condition? → false → [статус: skipped] → следующий шаг
|
|
729
|
-
↓ true
|
|
730
|
-
middleware.beforeEach
|
|
731
|
-
↓
|
|
732
|
-
pauseBefore
|
|
733
|
-
↓
|
|
734
|
-
before() хук
|
|
735
|
-
↓
|
|
736
|
-
request()
|
|
737
|
-
↓
|
|
738
|
-
after() хук
|
|
739
|
-
↓
|
|
740
|
-
pauseAfter
|
|
741
|
-
↓
|
|
742
|
-
middleware.afterEach
|
|
743
|
-
↓
|
|
744
|
-
[статус: success] → следующий шаг
|
|
745
|
-
|
|
746
|
-
При ошибке на любом этапе:
|
|
747
|
-
└─► stage.errorHandler (если задан) → middleware.onError → [статус: error] → стоп
|
|
748
|
-
```
|
|
749
|
-
|
|
750
|
-
---
|
|
751
|
-
|
|
752
|
-
### Параллельные шаги
|
|
753
|
-
|
|
754
|
-
Группируйте шаги для параллельного выполнения с помощью `parallel`:
|
|
755
|
-
|
|
756
|
-
```js
|
|
757
|
-
const orchestrator = new PipelineOrchestrator({
|
|
758
|
-
config: {
|
|
759
|
-
stages: [
|
|
760
|
-
// Обычный последовательный шаг
|
|
761
|
-
{ key: "auth", request: async () => getToken() },
|
|
762
|
-
|
|
763
|
-
// Параллельная группа — все шаги выполняются одновременно
|
|
764
|
-
{
|
|
765
|
-
key: "load-data",
|
|
766
|
-
parallel: [
|
|
767
|
-
{ key: "loadUsers", request: async () => fetchUsers() },
|
|
768
|
-
{ key: "loadProducts", request: async () => fetchProducts() },
|
|
769
|
-
{ key: "loadSettings", request: async () => fetchSettings() },
|
|
770
|
-
],
|
|
771
|
-
},
|
|
772
|
-
|
|
773
|
-
// Последовательный шаг после группы
|
|
774
|
-
{
|
|
775
|
-
key: "render",
|
|
776
|
-
request: async ({ allResults }) => render(allResults),
|
|
777
|
-
},
|
|
778
|
-
],
|
|
779
|
-
},
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
const result = await orchestrator.run();
|
|
783
|
-
console.log(result.stageResults.loadUsers.data); // результат параллельного шага
|
|
784
|
-
console.log(result.stageResults.loadProducts.data);
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
- Все шаги группы выполняются одновременно через `Promise.all`.
|
|
788
|
-
- Если **хотя бы один** шаг группы завершился с ошибкой — pipeline останавливается, `success: false`.
|
|
789
|
-
- Каждый параллельный шаг имеет собственный ключ и запись в `stageResults`.
|
|
790
|
-
- `rerunStep(key)` работает в том числе для шагов внутри параллельных групп.
|
|
791
|
-
|
|
792
|
-
---
|
|
793
|
-
|
|
794
|
-
### Global middleware
|
|
795
|
-
|
|
796
|
-
Применяйте хуки ко всем шагам без изменения их конфигураций:
|
|
797
|
-
|
|
798
|
-
```js
|
|
799
|
-
const orchestrator = new PipelineOrchestrator({
|
|
800
|
-
config: {
|
|
801
|
-
stages: [ /* ... */ ],
|
|
802
|
-
middleware: {
|
|
803
|
-
beforeEach: async ({ stage, index, sharedData }) => {
|
|
804
|
-
console.log(`[${index}] Старт: ${stage.key}`);
|
|
805
|
-
sharedData.startedAt = Date.now();
|
|
806
|
-
},
|
|
807
|
-
afterEach: async ({ stage, index, result, sharedData }) => {
|
|
808
|
-
const ms = Date.now() - sharedData.startedAt;
|
|
809
|
-
console.log(`[${index}] Готово: ${stage.key} за ${ms}мс`, result.data);
|
|
810
|
-
},
|
|
811
|
-
onError: async ({ stage, error, sharedData }) => {
|
|
812
|
-
await reportError({ stage: stage.key, error, context: sharedData });
|
|
813
|
-
},
|
|
814
|
-
},
|
|
815
|
-
},
|
|
816
|
-
});
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
Middleware вызывается дополнительно к per-stage `errorHandler`, а не вместо него.
|
|
820
|
-
|
|
821
|
-
---
|
|
822
|
-
|
|
823
|
-
### Пауза и возобновление
|
|
824
|
-
|
|
825
|
-
Останавливайте pipeline после шага и продолжайте по команде:
|
|
826
|
-
|
|
827
|
-
```js
|
|
828
|
-
const orchestrator = new PipelineOrchestrator({ config });
|
|
829
|
-
|
|
830
|
-
// Пауза после завершения step1
|
|
831
|
-
orchestrator.on("step:step1:success", () => orchestrator.pause());
|
|
832
|
-
|
|
833
|
-
const runPromise = orchestrator.run();
|
|
834
|
-
|
|
835
|
-
// В любой момент позже (например, после подтверждения пользователем):
|
|
836
|
-
await showConfirmDialog();
|
|
837
|
-
orchestrator.resume();
|
|
838
|
-
|
|
839
|
-
await runPromise;
|
|
840
|
-
```
|
|
841
|
-
|
|
842
|
-
- `pause()` — pipeline ждёт после завершения текущего шага (включая все события).
|
|
843
|
-
- `resume()` — продолжает выполнение со следующего шага.
|
|
844
|
-
- `abort()` во время паузы — разблокирует pipeline и завершает его.
|
|
845
|
-
|
|
846
|
-
---
|
|
847
|
-
|
|
848
|
-
### Экспорт и восстановление состояния
|
|
849
|
-
|
|
850
|
-
Сохраняйте и восстанавливайте состояние pipeline между перезагрузками или сессиями:
|
|
851
|
-
|
|
852
|
-
```js
|
|
853
|
-
const orchestrator = new PipelineOrchestrator({ config });
|
|
854
|
-
await orchestrator.run();
|
|
855
|
-
|
|
856
|
-
// Сохраняем снимок состояния
|
|
857
|
-
const snapshot = orchestrator.exportState();
|
|
858
|
-
localStorage.setItem("pipelineState", JSON.stringify(snapshot));
|
|
859
|
-
|
|
860
|
-
// Позже — восстанавливаем
|
|
861
|
-
const saved = JSON.parse(localStorage.getItem("pipelineState"));
|
|
862
|
-
const orchestrator2 = new PipelineOrchestrator({ config });
|
|
863
|
-
orchestrator2.importState(saved);
|
|
864
|
-
|
|
865
|
-
console.log(orchestrator2.getProgress()); // восстановленный прогресс
|
|
866
|
-
console.log(orchestrator2.getLogs()); // восстановленные логи (timestamps — объекты Date)
|
|
867
|
-
```
|
|
868
|
-
|
|
869
|
-
`exportState()` возвращает `{ stageResults, logs }` — обычный JSON-сериализуемый объект. Временны́е метки в логах хранятся как ISO-строки и восстанавливаются как объекты `Date` при `importState`.
|
|
870
|
-
|
|
871
|
-
---
|
|
872
|
-
|
|
873
|
-
### Интеграция с Vue
|
|
874
|
-
|
|
875
|
-
```vue
|
|
876
|
-
<script setup>
|
|
877
|
-
import { PipelineOrchestrator, usePipelineProgressVue, usePipelineRunVue } from "rest-pipeline-js/vue";
|
|
878
|
-
|
|
879
|
-
const orchestrator = new PipelineOrchestrator({ config: { stages: [/* ... */] } });
|
|
880
|
-
const progress = usePipelineProgressVue(orchestrator);
|
|
881
|
-
const { run, running, result, error, abort, pause, resume, rerunStep } = usePipelineRunVue(orchestrator);
|
|
882
|
-
</script>
|
|
883
|
-
|
|
884
|
-
<template>
|
|
885
|
-
<div>
|
|
886
|
-
<div>Текущий шаг: {{ progress.currentStage }}</div>
|
|
887
|
-
<button @click="run()" :disabled="running">Старт</button>
|
|
888
|
-
<button @click="abort()" :disabled="!running">Отмена</button>
|
|
889
|
-
<button @click="pause()">Пауза</button>
|
|
890
|
-
<button @click="resume()">Продолжить</button>
|
|
891
|
-
<div v-if="result">Готово: {{ result }}</div>
|
|
892
|
-
<div v-if="error">Ошибка: {{ error.message }}</div>
|
|
893
|
-
</div>
|
|
894
|
-
</template>
|
|
895
|
-
```
|
|
896
|
-
|
|
897
|
-
Composition-функции (импорт из `rest-pipeline-js/vue`):
|
|
898
|
-
|
|
899
|
-
| Функция | Возвращает | Описание |
|
|
900
|
-
|---------|-----------|----------|
|
|
901
|
-
| `usePipelineProgressVue(orchestrator)` | `Ref<PipelineProgress>` | Реактивный прогресс |
|
|
902
|
-
| `usePipelineRunVue(orchestrator)` | `{ run, running, result, error, stageResults, abort, pause, resume, rerunStep, clearStageResults }` | Запуск и реактивное состояние |
|
|
903
|
-
| `usePipelineStepEventVue(orchestrator, stepKey, eventType)` | `Ref<any>` | Последний payload события шага |
|
|
904
|
-
| `usePipelineLogsVue(orchestrator)` | `Ref<log[]>` | Реактивные логи |
|
|
905
|
-
| `useRerunPipelineStepVue(orchestrator)` | `function` | Привязанный `rerunStep` |
|
|
906
|
-
| `useRestClientVue(config)` | `ComputedRef<RestClient>` | Реактивный REST-клиент |
|
|
907
|
-
|
|
908
|
-
---
|
|
909
|
-
|
|
910
|
-
### Интеграция с React
|
|
911
|
-
|
|
912
|
-
```jsx
|
|
913
|
-
import {
|
|
914
|
-
PipelineOrchestrator,
|
|
915
|
-
usePipelineProgressReact,
|
|
916
|
-
usePipelineRunReact,
|
|
917
|
-
} from "rest-pipeline-js/react";
|
|
918
|
-
|
|
919
|
-
const orchestrator = new PipelineOrchestrator({ config: { stages: [/* ... */] } });
|
|
920
|
-
|
|
921
|
-
export function PipelineComponent() {
|
|
922
|
-
const progress = usePipelineProgressReact(orchestrator);
|
|
923
|
-
const [run, { running, result, error, abort, pause, resume, rerunStep }] =
|
|
924
|
-
usePipelineRunReact(orchestrator);
|
|
925
|
-
|
|
926
|
-
return (
|
|
927
|
-
<div>
|
|
928
|
-
<div>Текущий шаг: {progress.currentStage}</div>
|
|
929
|
-
<button onClick={() => run()} disabled={running}>Старт</button>
|
|
930
|
-
<button onClick={() => abort()} disabled={!running}>Отмена</button>
|
|
931
|
-
<button onClick={() => pause()}>Пауза</button>
|
|
932
|
-
<button onClick={() => resume()}>Продолжить</button>
|
|
933
|
-
{result && <div>Готово: {JSON.stringify(result)}</div>}
|
|
934
|
-
{error && <div>Ошибка: {error.message}</div>}
|
|
935
|
-
</div>
|
|
936
|
-
);
|
|
937
|
-
}
|
|
938
|
-
```
|
|
939
|
-
|
|
940
|
-
Хуки (импорт из `rest-pipeline-js/react`):
|
|
941
|
-
|
|
942
|
-
| Хук | Возвращает | Описание |
|
|
943
|
-
|-----|-----------|----------|
|
|
944
|
-
| `usePipelineProgressReact(orchestrator)` | `PipelineProgress` | Реактивный прогресс |
|
|
945
|
-
| `usePipelineRunReact(orchestrator)` | `[run, { running, result, error, stageResults, abort, pause, resume, rerunStep }]` | Запуск и состояние |
|
|
946
|
-
| `usePipelineStepEventReact(orchestrator, stepKey, eventType)` | `any` | Последний payload события шага |
|
|
947
|
-
| `usePipelineLogsReact(orchestrator)` | `log[]` | Реактивные логи |
|
|
948
|
-
| `useRerunPipelineStepReact(orchestrator)` | `function` | Привязанный `rerunStep` |
|
|
949
|
-
| `useRestClientReact(config)` | `RestClient` | Мемоизированный REST-клиент |
|
|
950
|
-
|
|
951
|
-
---
|
|
952
|
-
|
|
953
|
-
## Точки входа
|
|
954
|
-
|
|
955
|
-
| Точка входа | Назначение | Содержимое |
|
|
956
|
-
|-------------|------------|------------|
|
|
957
|
-
| `rest-pipeline-js` | Только ядро | `PipelineOrchestrator`, `createRestClient`, типы. Без Vue/React. |
|
|
958
|
-
| `rest-pipeline-js/vue` | Vue-проекты | Ядро + Vue composition-функции |
|
|
959
|
-
| `rest-pipeline-js/react` | React-проекты | Ядро + React хуки |
|
|
960
|
-
|
|
961
|
-
```js
|
|
962
|
-
// Только ядро
|
|
963
|
-
import { createRestClient, PipelineOrchestrator } from "rest-pipeline-js";
|
|
964
|
-
|
|
965
|
-
// Vue
|
|
966
|
-
import { PipelineOrchestrator, usePipelineRunVue } from "rest-pipeline-js/vue";
|
|
967
|
-
|
|
968
|
-
// React
|
|
969
|
-
import { PipelineOrchestrator, usePipelineRunReact } from "rest-pipeline-js/react";
|
|
970
|
-
```
|
|
971
|
-
|
|
972
|
-
`sideEffects: false` — неиспользуемые точки входа удаляются tree-shaking'ом. `react`/`react-dom` — `peerDependencies`.
|
|
973
|
-
|
|
974
|
-
---
|
|
975
|
-
|
|
976
|
-
## Требования
|
|
977
|
-
|
|
978
|
-
- Node.js >= 14.0.0
|
|
979
|
-
- Современный браузер с поддержкой ES2019+
|
|
980
|
-
|
|
981
|
-
---
|
|
982
|
-
|
|
983
|
-
## Разработка
|
|
984
|
-
|
|
985
|
-
```bash
|
|
986
|
-
git clone https://github.com/macrulezru/pipeline-js.git
|
|
987
|
-
cd pipeline-js
|
|
988
|
-
npm install
|
|
989
|
-
npm test
|
|
990
|
-
```
|
|
991
|
-
|
|
992
|
-
---
|
|
993
|
-
|
|
994
|
-
## Лицензия
|
|
995
|
-
|
|
996
|
-
MIT
|
|
997
|
-
|
|
998
|
-
---
|
|
999
|
-
|
|
1000
|
-
## Автор
|
|
1001
|
-
|
|
1002
|
-
Данил Лисин Владимирович aka Macrulez
|
|
1003
|
-
|
|
1004
|
-
GitHub: [macrulezru](https://github.com/macrulezru) · Сайт: [macrulez.ru](https://macrulez.ru/)
|
|
1005
|
-
|
|
1006
|
-
Вопросы и баги — через [issue](https://github.com/macrulezru/pipeline-js/issues)
|