@conduit-client/service-renewable-resource-manager
v3.23.1
Published
Generic renewable-resource manager service (lazy cache + single-flight fetch + refresh-storm dedup)
Readme
@conduit-client/service-renewable-resource-manager
A generic, pluggable service for managing a single renewable resource — a value you fetch, cache, hand out on demand, and re-fetch when it goes stale. CSRF tokens are the first consumer; SFAP JWTs and other renewable credentials fit the same shape.
See the design rationale in
adrs/2026-05-13-renewable-resource-manager-as-service.md.
What it is
- Lazy & externally-triggered. No TTL, no expiry tracking, no autonomous
renewal. It fetches on first
get()and re-fetches only when a caller invokesrefresh(). "Renewable" describes the resource (it can be re-fetched), not a behavior of the manager. - Single-flight. Concurrent
get()/refresh()calls share one in-flight fetch. - Refresh-storm bounded. When many callers fail against the same stale value
at once,
refresh(staleValue)collapses them to a single network round-trip (see below).
Usage
import {
BasicRenewableResourceManager,
buildRenewableResourceManagerDescriptor,
} from '@conduit-client/service-renewable-resource-manager/v1';
const manager = new BasicRenewableResourceManager<string>({
fetch: () => fetchTokenFromServer(), // "go to the network"
storage: {
get: () => durableStorage.get(KEY),
set: (v) => durableStorage.set(KEY, v),
clear: () => durableStorage.remove(KEY),
},
});
// register as a named, versioned service
const descriptor = buildRenewableResourceManagerDescriptor('csrfToken', manager);API
RenewableResourceManager<T extends string | number | boolean>
get(): PromiseLike<T | undefined>— cached value, or fetch+cache if empty.refresh(staleValue?: T): PromiseLike<T | undefined>— fetch fresh, overwrite cache. WithstaleValue, short-circuits if storage already moved past it.clear(): PromiseLike<void>— discard the cache (logout, org switch, teardown). Routine staleness usesrefresh, which is non-destructive.
T is constrained to primitives so the storm-dedup comparison can use ===.
Error contract
- A rejected
PromiseLike⇒ the fetch could not complete (network/server error). Rejections propagate; they are never swallowed intoundefined. - A resolved
undefined⇒ the fetch completed and there is genuinely no value. The cache is not cleared in this case — useclear()for that.
Refresh-storm dedup, by timing regime
- Concurrent failures (arriving while a fetch is in flight) share that fetch via single-flight.
- Late failures (arriving after the first refresh stored a fresh value) are
caught by
refresh(staleValue): storage no longer holdsstaleValue, so the stored value is returned with no network hop.
Storage adapter preconditions
The manager relies on (but cannot enforce):
- writes are durable before the returned
PromiseLikeresolves; - reads observe the most recent settled write (read-your-writes);
- the manager is the sole writer of the slot, and replacement is monotonic — the storm-dedup treats "stored differs from stale" as "someone refreshed."
