API reference
createValve options, valve methods, job options, and types.
The declaration files shipped with the package document every type with examples. The generated TypeDoc reference renders them.
createValve
function createValve(options: ValveOptions): Valve;Creates a valve. Throws ValveError with code INVALID_OPTIONS if an option
is invalid. At least one of rate or a finite concurrency is required.
ValveOptions
| Option | Type | Default | Description |
|---|---|---|---|
id | string | Required | Name of the shared budget. 1 to 200 characters, without { or }. |
rate | RateRule | RateRule[] | None | One rule, or a non-empty array of rules. |
concurrency | number | ConcurrencyOptions | Infinity | A number limits this instance. The object form can select global scope. |
store | Store | A new private memory store | Where the shared budget lives. |
maxQueue | number | Infinity | Maximum jobs waiting in this process. Beyond it, run rejects with QUEUE_FULL. |
maxWait | Duration | No limit | Maximum time a job may wait before starting. |
feedback | Feedback | false | defaultFeedback | Reads provider responses. false disables header reading. |
maxPause | Duration | '15m' | Cap on any one pause caused by feedback or backoff. |
backoff | { base?: Duration; max?: Duration } | { base: '1s', max: '30s' } | Exponential pause after a 429 with no usable header. |
onStoreError | 'wait' | 'throw' | 'open' | 'wait' | Behavior when the store fails. See Store failures. |
batch | number | 32 | Maximum jobs requested from the store in one call. |
RateRule
| Field | Type | Default | Description |
|---|---|---|---|
name | string | None | Stable rule identity; required for scoped rules. |
scope | string | None | Job identity dimension; absent means Valve-wide. |
limit | number | Required | Cost units allowed per interval. Positive integer. |
interval | Duration | Required | Window length. At least 1ms. |
burst | number | 1 | Units that may start back to back. GCRA only. Positive integer. |
algorithm | 'gcra' | 'fixed-window' | 'gcra' | See Choose an algorithm. |
Unnamed rules cannot share the same algorithm and interval. Set a unique
name to distinguish rules with matching algorithms and intervals. Scoped rules
require names and select a job identity through scope. Both fields accept
1-64 ASCII letters, digits, underscores or hyphens, beginning with a letter.
See Scoped rate limits. Scoped configurations
support at most 16 rules and batch 128 (default 32).
ConcurrencyOptions
| Field | Type | Default | Description |
|---|---|---|---|
limit | number | Required | Maximum running jobs. Positive integer. |
scope | 'local' | 'global' | 'local' | 'global' shares the limit across every valve with the same id. |
leaseTtl | Duration | '30s' | Global lease lifetime, renewed every third. Minimum 30ms. |
Global scope requires a store that implements release and renew. Both
built-in stores do.
Duration
A number of milliseconds, or a string ending in ms, s, m, or h.
Decimals are allowed.
250; // 250 ms
('1.5s'); // 1500 ms
('10m'); // 600000 ms
('24h'); // 86400000 msNegative values, NaN, Infinity, and unknown units are rejected.
Valve
run
run<R>(fn: (ctx: JobContext) => R, options?: RunOptions<Awaited<R>>): Promise<Awaited<R>>Queues fn and calls it when every rule and the concurrency limit allow.
Resolves or rejects with whatever fn returns or throws. fn receives
{ signal }, the signal from options.signal (or the lease signal under
global concurrency). run never throws synchronously, and never calls fn
synchronously.
RunOptions
| Option | Type | Default | Description |
|---|---|---|---|
scopes | Readonly<Record<string, string>> | None | Required identities for scoped rules; copied before enqueueing. |
cost | number | 1 | Units spent from every rule. |
priority | number | 0 | Higher starts first. Integer. |
maxWait | Duration | Valve maxWait | Queue deadline for this job. |
signal | AbortSignal | None | Cancels the job while queued. Passed to fn once running. |
observe | (result) => ResponseLike | undefined | None | Maps a resolved result to a response for feedback. |
wrap
wrap<A extends unknown[], R>(fn: (...args: A) => R, options?: Omit<RunOptions, 'signal'>): (...args: A) => Promise<Awaited<R>>Returns a function with the same parameters as fn. Each call goes through
the valve with options.
wrapFetch
wrapFetch<F extends FetchLike = typeof fetch>(fetchImpl?: F, options?: WrapFetchOptions<F>): FReturns a function with the same type as fetchImpl (default
globalThis.fetch). It queues each request, observes each response, and
retries eligible rate limit responses for GET, HEAD, OPTIONS, PUT and DELETE
by default. See Respect provider feedback.
WrapFetchOptions
| Option | Type | Default | Description |
|---|---|---|---|
scopes | Readonly<Record<string, string>> | (...args) => Readonly<Record<string, string>> | None | Fixed identities or a synchronous resolver, snapshotted once across retries. |
retries | number | 2 | Retries after a 429, or a 503 with a valid Retry-After. 0 disables. |
retryUnsafeMethods | boolean | false | Opt in to retries for other methods after verifying provider-specific idempotency. |
maxReplayBodySize | number | 65536 | Finite integer byte limit for inherited Request replay. 0 sends once without probing. |
cost | number | (...args) => number | 1 | Cost per request, or a function of the fetch arguments. |
priority | number | (...args) => number | 0 | Priority per request, or a function of the fetch arguments. |
maxWait | Duration | Valve maxWait | Queue deadline for each attempt. |
The request's abort signal, from init.signal or the Request, cancels queued
attempts and is passed to fetchImpl for running ones.
It also cancels a running Request body probe before fetch begins.
retries counts additional attempts after the initial request. The default of
2 permits up to three total requests. Once the retry limit is exhausted, the
final response is returned unchanged.
init.method overrides the Request method; otherwise the method defaults to
GET. POST, PATCH and unrecognized methods do not retry unless
retryUnsafeMethods: true. A supplied idempotency key does not opt in.
All responses still update shared cooldowns. Streamed init.body requests
never retry, including with the opt-in enabled. Use a dedicated wrapper for
verified operations; see the compatibility notes.
Inherited Request bodies retry only if a probe clone fits within
maxReplayBodySize. Probing occurs after admission, holds the concurrency
slot, and delays upload until the body ends or exceeds the limit. Larger
bodies and bodies exceeding 1,024 chunks are sent once. Empty chunks count
toward this second limit. The probe can retain the limit plus one source chunk
and stream prefetch. Set maxReplayBodySize: 0 to skip probing and send
Request bodies once. Explicit init.body overrides bypass probing of the
unused body. See Request bodies.
observe
observe(response: ResponseLike): voidReads a response with the valve's feedback reader. If it asks for a pause, writes a shared pause in the background. Safe to call with any response.
pause
pause(duration: Duration): Promise<void>Pauses the valve in every process sharing it. Only extends an existing pause.
Resolves once the store records it. Not capped by maxPause.
resume
resume(): Promise<void>Ends the current pause in every process sharing the valve.
stats
stats(): ValveStatsReturns a snapshot for this process. See Monitor a valve.
on
on<E extends keyof ValveEventMap>(event: E, listener: (payload: ValveEventMap[E]) => void): () => voidAdds a listener and returns a function that removes it. See Events.
idle
idle(): Promise<void>Resolves when nothing is queued or running in this process.
close
close(options?: { drain?: boolean }): Promise<void>Stops accepting jobs. Without drain, queued jobs reject with CLOSED. With
drain: true, they run first. Resolves when running jobs finish. Repeated
calls return the same promise. Never closes a client you supplied.
Valves also implement [Symbol.asyncDispose], which calls close().
id
readonly id: stringThe id passed to createValve.
Other exports
| Export | Entry point | Description |
|---|---|---|
memoryStore(options?) | valvejs | In-process store. Share one instance to share budgets. |
redisStore(options) | valvejs/redis | See Redis store. |
ValveError, isValveError | valvejs | See Errors. |
defaultFeedback | Both | The default feedback reader. |
| Feedback readers | valvejs/headers | See Feedback readers. |
memoryStore accepts { clock?: () => number }, a source of epoch
milliseconds. It defaults to Date.now.