3.3 KiB
3.3 KiB
id, epic, title, type, status, feature, depends-on, blocks, created, updated
| id | epic | title | type | status | feature | depends-on | blocks | created | updated | |||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-rate-limit-implementations | security-headers-rate-limit-sbom | Rate-limit implementations — Noop, InMemory, Recording | technical-story | done | core-shared |
|
|
2026-05-20T00:00:00Z | 2026-05-20T08:38:25.271Z |
Goal
Provide three working IRateLimit implementations that cover dev, test, and production-delegate scenarios: NoopRateLimit (always-allow, zero overhead), InMemoryRateLimit (per-process fixed-window with check-at-read expiry), and RecordingRateLimit (test helper in core-testing that captures all calls for assertions).
Why
The withRateLimit wrapper (Story 04) and the auth.signIn backfill (Story 05) both need concrete classes to wire. NoopRateLimit is also the required default in BindContext so apps without a wired rate-limit implementation boot safely. RecordingRateLimit is the testing primitive every future rate-limited use-case test will use; landing it here alongside the production impls keeps the testing toolkit coherent.
Done when
packages/core-shared/src/rate-limit/noop-rate-limit.tsexportsNoopRateLimitwith unit test:consumealways resolves{ allowed: true, remaining: Infinity, resetAt: epoch },resetis a no-op.packages/core-shared/src/rate-limit/in-memory-rate-limit.tsexportsInMemoryRateLimitusing check-at-read fixed-window expiry (nosetTimeout); unit test: per-bucket tracking, decrement onconsume, reset at window boundary (synthetic clock), explicitresetcall restores budget to declared value.packages/core-testing/src/rate-limit/recording-rate-limit.tsexportsRecordingRateLimitcapturingconsume+resetinvocation arguments; sibling test verifies capture correctness.pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diffall pass after each task.
In scope
NoopRateLimit— always-allow impl, no state, no timers.InMemoryRateLimit—Map-backed, per-bucket{ count, resetAt }, check-at-read expiry, clock injection via constructor arg for testability.RecordingRateLimit— inpackages/core-testing; exposesconsumeCalls+resetCallsaccessors for test assertions.- Barrel exports from
core-sharedandcore-testing.
Out of scope
- Redis-backed or any distributed impl — deferred, consumer wires via ADR-022 library evaluation.
- Token-bucket algorithm — InMemoryRateLimit uses fixed-window for simplicity; documented as dev-only.
withRateLimitwrapper — Story 04.
Tasks
- Implement
NoopRateLimitinpackages/core-shared/src/rate-limit/noop-rate-limit.tswith sibling unit test (consume always allowed, reset no-op); implementInMemoryRateLimitinpackages/core-shared/src/rate-limit/in-memory-rate-limit.tswith sibling unit test (per-bucket tracking, check-at-read expiry via injected clock, explicit reset restores budget); export both fromcore-sharedbarrel; all gates pass. - Implement
RecordingRateLimitinpackages/core-testing/src/rate-limit/recording-rate-limit.tscapturingconsume+resetcall arguments verbatim withconsumeCalls+resetCallsaccessors; sibling test verifies capture correctness; export fromcore-testingbarrel; all gates pass.