Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
> Archived 2026-04-19: all tasks retroactively ticked; package shipped under universal/control-flow/retry.
# Tasks: enterprise-retry
## 1. Scaffold
- [x] 1.1 Create `packages/universal/retry/` with `package.json`, `tsconfig.json`, `tsconfig.lib.json`, `vite.config.ts`, `vitest.config.ts`, `README.md` skeleton
- [x] 1.2 Register coverage thresholds at 100/100/100/100 in `vitest.config.ts`; provider = istanbul
- [x] 1.3 Add cross-cutting guard tests in `tests/`: dep-graph, no-disallowed, tree-shake
- [x] 1.4 Add package to root `CLAUDE.md`, `README.md`, and `STRUCTURE.yml`
## 2. Types + errors
- [x] 2.1 Implement `src/types/retry.types.ts` — `RetryConfig`, `RetryResult`, `RetryStrategy`, `DelayFunction`, `JitterMode`, `ConstantBackoffOptions`, `LinearBackoffOptions`, `ExponentialBackoffOptions`
- [x] 2.2 Implement `src/errors/retry-error.ts` — `RetryError` base extends `SuaveplanError`; `RetryExhaustedError`, `AttemptTimeoutError` extend `RetryError`; `defineCodes` for `RETRY.EXHAUSTED`, `RETRY.ABORTED`, `RETRY.TIMEOUT`
- [x] 2.3 Implement `src/circuit-breaker/circuit-breaker.types.ts` — `CircuitBreakerConfig`, `CircuitBreakerEventMap`, `CircuitState`; `CircuitOpenError` extends `RetryError` with `RETRY.CIRCUIT_OPEN` code
- [x] 2.4 Zod schemas: `RetryConfigSchema`, `ConstantBackoffOptionsSchema`, `LinearBackoffOptionsSchema`, `ExponentialBackoffOptionsSchema`, `CircuitBreakerConfigSchema`
- [x] 2.5 Tests: error construction, code narrowing, `.attempts` / `.lastError` / `.attemptNumber` / `.timeoutMs` fields
- [x] 2.6 Implement `src/error-codes.ts` and auto-register via side-effect import in `src/index.ts`
## 3. Backoff strategies
- [x] 3.1 `src/strategies/constant/constant.ts` — `createConstantBackoff(opts)` → `RetryStrategy`; `ConstantBackoffOptions`
- [x] 3.2 `src/strategies/linear/linear.ts` — `createLinearBackoff(opts)` → `RetryStrategy`; `LinearBackoffOptions`
- [x] 3.3 `src/strategies/exponential/exponential.ts` — `createExponentialBackoff(opts)` → `RetryStrategy`; `applyJitter(delay, mode, rng?)`, `clampDelay(delay, min, max)`; `ExponentialBackoffOptions`
- [x] 3.4 All strategies accept injectable `rng?: Rng` for deterministic jitter
- [x] 3.5 Unit tests: delay values at each attempt, clamping, jitter modes (full/equal/decorrelated/none)
- [x] 3.6 Property-based tests: exponential `delay(n+1) >= delay(n)` with no jitter; jitter in `[0, d]`; clamp always in `[min, max]`
- [x] 3.7 Co-located `.md` for each strategy
## 4. Core retry
- [x] 4.1 Implement `src/retry/retry.ts`:
- `sleep(ms, signal?)` — cancellable; resolves early on abort
- `retry(fn, config)` → `Promise<RetryResult<T>>`
- `withRetry(fn, config)` → `Promise<T>` — throws `RetryExhaustedError` on exhaustion, `RetryError` on abort
- `withTimeout(fn, ms, signal?)` → `Promise<T>` — throws `AttemptTimeoutError`
- `createDelayFn(strategy)` → `DelayFunction`
- `withCircuitBreaker(fn, breaker)` → `Promise<T>`
- [x] 4.2 `retry` uses injected `clock?: Clock` for sleep duration; `rng?: Rng` from config forwarded to strategy
- [x] 4.3 `retry` honours `AbortSignal` between attempts (checks before each delay and after each call)
- [x] 4.4 Unit tests: all config options, abort between attempts, timeout, exhaustion, `shouldRetry` predicate
- [x] 4.5 Property-based tests: `withRetry` never exceeds `maxAttempts` calls to fn
- [x] 4.6 Co-located `src/retry/retry.md`
## 5. Circuit breaker
- [x] 5.1 Implement `src/circuit-breaker/circuit-breaker.ts` — `CircuitBreaker` class
- State machine: `closed → open → half-open → closed` (or back to open)
- `.call(fn, signal?)` — throws `CircuitOpenError` when open
- `.reset()` — force back to closed
- `.state` getter — `CircuitState`
- Event emitter via `@suaveplan/events`: `stateChange` event with `CircuitBreakerEventMap`
- `halfOpenProbes` attempts before promoting to closed
- `timeout` window after which open resets to half-open (uses `clock?: Clock`)
- [x] 5.2 Unit tests: failure threshold transitions, half-open probing, success promotion, manual reset, signal passing, clock injection
- [x] 5.3 Property-based tests: after `failureThreshold` consecutive failures → state is `'open'`
- [x] 5.4 Co-located `src/circuit-breaker/circuit-breaker.md`
## 6. OTel instrumentation
- [x] 6.1 Counter `retry.attempt.total` by outcome label (success / failure / exhausted / aborted)
- [x] 6.2 Histogram `retry.attempt.delay_ms` by strategy label
- [x] 6.3 Counter `circuit_breaker.state_change.total` by from/to label
- [x] 6.4 Histogram `circuit_breaker.call.duration_ms`
- [x] 6.5 No-op without exporter (tested)
## 7. Benchmarks
- [x] 7.1 `src/__benchmarks__/sleep.bench.ts` — `sleep(0)` call overhead baseline (mitata)
- [x] 7.2 `src/__benchmarks__/retry.bench.ts` — `withRetry` with always-succeeding fn (0-retry overhead)
- [x] 7.3 `src/__benchmarks__/backoff.bench.ts` — `createExponentialBackoff` delay computation 1 000 iterations
- [x] 7.4 `src/__benchmarks__/circuit-breaker.bench.ts` — `CircuitBreaker.call` closed-state hot path 10 000 iterations
- [x] 7.5 Publish baseline numbers in README
## 8. Docs
- [x] 8.1 Co-located `.md` for every implementation file (≥200 words, examples, API reference)
- [x] 8.2 Rewrite `README.md` with public API, circuit breaker state diagram, strategy comparison table, consumer guide
- [x] 8.3 `bun run docs:check --filter=@suaveplan/retry` ≥ 90%
## 9. Verify and publish
- [x] 9.1 `bunx turbo lint typecheck test --filter=@suaveplan/retry` green
- [x] 9.2 `bunx turbo test:coverage --filter=@suaveplan/retry` at 100/100/100/100
- [x] 9.3 `bunx turbo build --filter=@suaveplan/retry` green
- [x] 9.4 `bun scripts/publish.ts --package @suaveplan/retry` to Verdaccio at 0.0.0
- [x] 9.5 Archive OpenSpec change to `openspec/archive/2026-04-18-enterprise-retry/`