rest-pipeline-js 1.3.12 → 1.4.0

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 (39) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/CHANGELOG.md +50 -0
  3. package/README.md +360 -171
  4. package/dist/cjs/cache.js +15 -0
  5. package/dist/cjs/circuit-breaker.js +84 -0
  6. package/dist/cjs/index.js +1 -0
  7. package/dist/cjs/pipeline-builder.js +22 -3
  8. package/dist/cjs/pipeline-orchestrator.js +159 -97
  9. package/dist/cjs/rest-client.js +97 -20
  10. package/dist/cjs/types.js +13 -0
  11. package/dist/esm/cache.d.ts +4 -0
  12. package/dist/esm/cache.js +15 -0
  13. package/dist/esm/circuit-breaker.d.ts +33 -0
  14. package/dist/esm/circuit-breaker.js +79 -0
  15. package/dist/esm/index.d.ts +1 -0
  16. package/dist/esm/index.js +1 -0
  17. package/dist/esm/pipeline-builder.d.ts +24 -9
  18. package/dist/esm/pipeline-builder.js +22 -3
  19. package/dist/esm/pipeline-orchestrator.d.ts +20 -0
  20. package/dist/esm/pipeline-orchestrator.js +159 -97
  21. package/dist/esm/rest-client.d.ts +10 -0
  22. package/dist/esm/rest-client.js +97 -20
  23. package/dist/esm/types.d.ts +77 -2
  24. package/dist/esm/types.js +11 -0
  25. package/dist/esm/usePipelineStepEvents-react.d.ts +1 -0
  26. package/dist/esm/usePipelineStepEvents-vue.d.ts +3 -0
  27. package/dist/esm/useRestClient-react.d.ts +5 -0
  28. package/dist/esm/useRestClient-vue.d.ts +5 -0
  29. package/package.json +1 -1
  30. package/src/cache.ts +17 -0
  31. package/src/circuit-breaker.ts +90 -0
  32. package/src/index.ts +1 -0
  33. package/src/pipeline-builder.ts +34 -13
  34. package/src/pipeline-orchestrator.ts +203 -105
  35. package/src/rest-client.ts +93 -9
  36. package/src/types.ts +90 -3
  37. package/tests/pipeline-builder.test.ts +112 -0
  38. package/tests/pipeline-orchestrator.test.ts +279 -0
  39. package/tests/rest-client.test.ts +290 -1
@@ -0,0 +1,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(grep -n \"stepIndex,$\\\\|stepIndex: i,\\\\|emitStepStart\\(\\\\|emitStepFinish\\(\\\\|emitStepError\\(\\\\|emitStepSkipped\\(\\\\|stageResults: { \\\\.\\\\.\\\\.this\\\\.stageResults }\" src/pipeline-orchestrator.ts)",
5
+ "Bash(cat > *)"
6
+ ],
7
+ "additionalDirectories": [
8
+ "\\tmp"
9
+ ]
10
+ }
11
+ }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ### Fixed
6
+
7
+ - **Flaky test in `tests/rest-client.test.ts`** ("при повторном 401 после onUnauthorized — не попадает в бесконечный цикл") — the mock error set `err.isAxiosError = true` *after* `Object.setPrototypeOf(err, axios.AxiosError.prototype)`. `AxiosError.prototype.isAxiosError` is defined as non-writable (`Object.defineProperty(..., { value: true })`), so that assignment threw a `TypeError` in strict mode, which masked the actual 401-retry logic being exercised. Fixed by assigning `isAxiosError` before swapping the prototype, matching the (correct) pattern already used by the other axios-error mocks in the same file. No production code changed.
8
+
9
+ ### Added
10
+
11
+ #### Pipeline Orchestrator
12
+
13
+ - **`signal` in stage hooks** — `request`, `condition`, `before`, `after`, `errorHandler`, and `StreamStageConfig.stream` now receive the pipeline's `AbortSignal` in their params object. Pass it down to `fetch`/`axios`/etc. so `abort()` actually cancels custom async work inside stage functions, not just the orchestrator's own bookkeeping.
14
+ - **`recoverStep(data)`** (from `types.ts`, re-exported from the root entry point) — `errorHandler` can return `recoverStep(data)` to recover a failed stage back into a successful one (`status: "success"`, `data`), running the same commit path as a normal success (metrics, `persistAdapter.save()`, `middleware.afterEach`, `step:success` event) instead of stopping/continuing-as-error. Returning anything else keeps the previous behavior (error, transformed via `toApiError`).
15
+
16
+ #### RestClient
17
+
18
+ - **No `axios.create()` when `adapter` is set** — `createRestClient()` no longer constructs the built-in axios instance if a custom `HttpAdapter` is provided, avoiding unnecessary work in edge/serverless environments that only use the adapter.
19
+
20
+ ### Changed
21
+
22
+ - Internal: `PipelineOrchestrator.executeStage()` success/error commit logic was extracted into `_commitStepSuccess()` / `_commitStepError()` so the new `errorHandler` recovery path and the normal success path share identical metrics/persist/middleware/event behavior.
23
+
24
+ #### Pipeline Orchestrator
25
+
26
+ - **`ParallelStageGroup.concurrency`** — caps how many stages of a parallel group run at once instead of always starting all of them via `Promise.all`. Useful for fan-out over many items (e.g. paginated fetches) without opening hundreds of requests at the same time. Results are still returned/stored in the same shape and order as an unlimited group. Supported by the `pipe()` builder via `.parallel(stages, { concurrency })`.
27
+
28
+ #### RestClient
29
+
30
+ - **`AuthProvider.tokenTtlMs`** — caches `getToken()`'s result for the given duration instead of calling it before every request. The cache is invalidated automatically on a `401` (before `onUnauthorized` runs), so the retried request always fetches a fresh token. Without `tokenTtlMs`, behavior is unchanged (`getToken()` called every request).
31
+ - **`invalidateCache(matcher)`** — new method on the client returned by `createRestClient()`. Removes only the response-cache entries whose URL matches `matcher` (substring, `RegExp`, or `(info: { method, url }) => boolean`) instead of clearing the whole cache like `clearCache()`. Returns the number of entries removed.
32
+ - `TtlCache` gained `keys()` and `deleteWhere(predicate)` to support the above.
33
+
34
+ ### Added (continued)
35
+
36
+ #### RestClient — Circuit breaker
37
+
38
+ - **`HttpConfig.circuitBreaker`** (new `CircuitBreakerConfig`: `{ failureThreshold, openMs, successThreshold?, isFailure? }`) — after `failureThreshold` consecutive failures the client rejects requests immediately with `CircuitOpenError` (`code: "CIRCUIT_OPEN"`) for `openMs`, without making a network call. After `openMs` it probes with real requests in a `half-open` state: success (×`successThreshold`, default 1) closes the circuit, failure re-opens it. `isFailure(error)` can exclude certain errors (e.g. 4xx) from counting as failures. Cancelled/aborted requests never count as failures. New module `src/circuit-breaker.ts` exports `CircuitBreaker`, `CircuitOpenError`, and the `CircuitBreakerState` type.
39
+ - **`client.getCircuitBreakerState()`** — returns `"closed" | "open" | "half-open"`, or `null` if `circuitBreaker` isn't configured.
40
+ - Not set by default — without `circuitBreaker`, behavior is unchanged.
41
+
42
+ #### Pipeline Orchestrator — run correlation
43
+
44
+ - **`runId`** — every `run()` call generates a fresh ID (via `crypto.randomUUID()`, falling back to a timestamp-based string), shared by `PipelineMetrics.onPipelineStart/onPipelineEnd/onStepDuration`, every `PipelineStepEvent` (`.runId`), and every entry returned by `getLogs()`/`exportState()`. All attempts within one `run()` (including `pipelineRetry` retries) share the same `runId`. `rerunStep()` generates its own separate `runId`. New `orchestrator.getRunId()` reads the current/last one. `PipelineMetrics`' three callback `info` objects and `PipelineStepEvent` gained a `runId` field (required on the former, optional on the latter for backward compatibility).
45
+
46
+ #### DX utilities — typed `pipe()` builder
47
+
48
+ - **`PipelineBuilder<TPrev>`** — the fluent builder is now generic: `.step()` infers and threads the previous step's output type into the next step's `prev`, so TypeScript catches type mismatches across a chain and provides autocomplete. The first step's `prev` is typed `undefined`, matching actual runtime behavior. `.parallel()` / `.subPipeline()` / `.stream()` intentionally don't change the threaded type, since the orchestrator's `prev` for the next step always comes from the last regular `.step()`, never from a parallel group/sub-pipeline/stream. Purely a type-level addition — `PipelineBuilder` still mutates the same instance internally, so existing non-chained usage (calling `.step()` without reassigning the result) keeps working unchanged.
49
+ - `ParallelStageGroup.concurrency` is also exposed through `pipe().parallel(stages, { concurrency })`.
50
+
51
+ ---
52
+
3
53
  ## [1.3.7] - 2026-04-04
4
54
 
5
55
  ### Added