5.5 KiB
5.5 KiB
Tasks: retry-injectable-timers
Change ID: retry-injectable-timers
Packages touched: @suaveplan/types, @suaveplan/retry, @suaveplan/cron, @suaveplan/job-scheduler
1. Land TimersLike in @suaveplan/types
- Add
src/timers-like.tsexportingTimersLikeper proposal. - Add sibling
src/timers-like.md(≥200 words, Purpose / Features / Usage Basic / Usage Advanced / API Reference / Implementation Notes). - Re-export
TimersLikefromsrc/index.ts(explicit named, alphabetic placement). bunx turbo lint typecheck --filter=@suaveplan/typesclean.
2. Alias SchedulerTimers to TimersLike in @suaveplan/cron
- In
packages/primitives/cron/src/scheduler/cron-scheduler.types.ts:- Replace the local
SchedulerTimersinterface declaration with:import type { TimersLike } from "@suaveplan/types"; export type SchedulerTimers = TimersLike;
- Replace the local
- Verify no internal cron code structurally needs anything beyond the two methods (the alias is transparent — no call-site changes expected).
bunx turbo lint typecheck test --filter=@suaveplan/cron— all green, 100% coverage preserved.
3. Thread TimersLike through @suaveplan/retry
- In
packages/control-flow/retry/src/types/retry.types.ts: addtimers?: TimersLiketoRetryConfig. Update the Zod schema inretry.schemas.tsif it strict-checks unknown keys (must permittimers). - In
packages/control-flow/retry/src/retry/retry.ts:sleep(ms, signal, timers?): pullsetTimer/clearTimerfromtimerswhen supplied; fall back toglobalThis.setTimeout/clearTimeout.withTimeout(fn, ms, attemptNumber, signal, timers?): same pattern.retry(): passconfig.timersthrough bothsleep()calls.withRetry(): no change beyond config passthrough (already delegates toretry()).
- Update
retry.mdto mention the new option in API Reference + add an "Advanced: deterministic tests with FakeClock" example. - Add
retry/retry-timers.test.ts(or extendretry.test.tsif still under 500 lines): FakeClock-driven exponential backoff exercising injected timers end-to-end — assert the run completes in zero real wall-clock time after enoughclock.advance(ms)calls. bunx turbo lint typecheck test:coverage --filter=@suaveplan/retry— 100% coverage onretry.tsandretry.types.tspreserved.
4. Replace inline retry loop in @suaveplan/job-scheduler
- In
packages/control-flow/job-scheduler/src/execution/job-execution.ts:- Import
retryfrom@suaveplan/retryand aRetryStrategybuilder. - Build a
delay: RetryStrategyfromJobOptions.retryDelayMs(constant strategy via@suaveplan/retry'sconstantBackoff). - Compose
retry({ maxAttempts: retries + 1, delay, signal, timers, shouldRetry: () => true })around the handler invocation. - Translate
RetryExhaustedError→JOB_SCHEDULER.EXECUTION_FAILED(preservecause) andAttemptTimeoutError→JOB_SCHEDULER.TIMEOUT. - Preserve
job:startevent emission per attempt; emitjob:failon final exhaustion; emitjob:completeon success. - Delete the dead inline-loop helpers in
job-execution.tsonce their callers are gone.
- Import
- If
executeJob's test file references the inline retry helpers, port the assertions to the new behavior. Add a regression test proving FakeClock +timersinjection still produces deterministic retry timing. bunx turbo lint typecheck test:coverage --filter=@suaveplan/job-scheduler— 100% coverage preserved. No skipped tests, no lowered thresholds.
5. Cross-package verification
bunx turbo lint typecheck test --filter=@suaveplan/types --filter=@suaveplan/retry --filter=@suaveplan/cron --filter=@suaveplan/job-scheduler— every gate green.- Banned-pattern scan over the four touched packages:
grep -rn "@deprecated\|TODO\|FIXME\|ts-ignore\|ts-expect-error\|\.skip\|\.only\|console\.log\|biome-ignore" \ packages/core/types/src/timers-like.ts \ packages/control-flow/retry/src/ \ packages/primitives/cron/src/scheduler/ \ packages/control-flow/job-scheduler/src/ --include="*.ts" - Verify no direct runner imports introduced (
from "vitest"/from "bun:test") in any modified test file. - Verify no
node:imports introduced in any modified universal-tier file.
6. Publish + archive
bunx turbo build --filter=@suaveplan/types --filter=@suaveplan/retry --filter=@suaveplan/cron --filter=@suaveplan/job-scheduler— confirmdist/outputs.bun scripts/publish.ts --package @suaveplan/types(publish-or-skip; bumps allowed if Verdaccio rejects the static0.1.0).bun scripts/publish.ts --package @suaveplan/retry.bun scripts/publish.ts --package @suaveplan/cron.bun scripts/publish.ts --package @suaveplan/job-scheduler.- Update
CLAUDE.mdretryentry to mentiontimers?: TimersLike; updatejob-schedulerentry to drop the FakeClock-determinism caveat and note it now delegates to@suaveplan/retry. - Update
MEMORY.mdif any in-flight memory references the inline retry-loop justification. bun scripts/folder-reorg/generate-manifest.ts— no manifest changes expected (no new packages) but run to confirm.- Archive proposal + tasks + spec delta to
openspec/archive/<today>-retry-injectable-timers/. Fold the## ADDED Requirementsfromspecs/types/spec.mdintoopenspec/specs/types/spec.md(create the file if it does not yet exist); fold the## MODIFIED Requirementsfromspecs/retry/spec.mdintoopenspec/specs/retry/spec.md.