Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
# retry
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`withRetry` is the standalone, reusable retry executor that
`@suaveplan/workflow` exposes alongside the `Step.retry` configuration.
Where the step's built-in retry is bound to the step's lifecycle (it only
fires inside `Step.run`), `withRetry` can wrap any async function and is
intended for the moments callers want retry semantics outside the
workflow engine — e.g. retrying a manual cleanup, retrying a request
inside a custom step that doesn't quite fit `Step`, or building higher-level
retry-with-circuit-breaker compositions.
The module also re-exports the `RetryPresets` object so callers can pick
sensible defaults (`quick`, `standard`, `aggressive`) without duplicating
the numbers in every consumer.
## Features
- `withRetry(fn, options?)` — retries with the resolved config.
- `createRetryWrapper(options?)` — reusable closure capturing options.
- `resolveRetryConfig(partial?)` — fills in defaults from
`constants/index`.
- `RetryPresets.{none, quick, standard, aggressive}` — pre-baked configs.
- Hooks: `onRetry(err, attempt)` before each backoff, `shouldRetry(err,
attempt)` to short-circuit early.
## Usage: Basic Example
```ts
import { withRetry, RetryPresets } from "@suaveplan/workflow";
const result = await withRetry(
() => fetch("/api/data").then((r) => r.json()),
RetryPresets.standard(),
);
```
## Usage: Advanced Example
```ts
import { createRetryWrapper } from "@suaveplan/workflow";
const wrap = createRetryWrapper({
maxAttempts: 4,
strategy: "exponential",
delayMs: 100,
maxDelayMs: 2_000,
shouldRetry: (err) => !(err instanceof TypeError),
onRetry: (err, attempt) => {
console.warn(`attempt ${attempt} failed:`, err.message);
},
});
const json = await wrap(() => fetch("/api/v1").then((r) => r.json()));
```
## API Reference
- `RetryOptions extends Partial<RetryConfig>` —
- `shouldRetry?: (error, attempt) => boolean`
- `onRetry?: (error, attempt) => void`
- `resolveRetryConfig(options?: Partial<RetryConfig>): RetryConfig`.
- `withRetry<T>(fn: () => T | Promise<T>, options?: RetryOptions): Promise<T>`.
- `createRetryWrapper(options?: RetryOptions): <T>(fn) => Promise<T>`.
- `RetryPresets` — `{ none, quick, standard, aggressive }`, each a function
returning a fresh `RetryConfig`.
## Implementation Notes
`withRetry` always rethrows the *last* error, not the *first*. Callers
that want the first-error semantics can capture it inside an `onRetry`
closure. The loop checks `attempt >= maxAttempts` before `shouldRetry` so
the final attempt always actually runs even if `shouldRetry` would have
returned false — the contract is "make all the attempts you promised."
`RetryPresets` are factory functions rather than constants so each call
returns a fresh object. That keeps callers from accidentally mutating a
shared preset (e.g. by spreading-and-overriding) and seeing the change
ripple through the rest of the application.