//#region src/utils/limiter.d.ts /** * Describes the request a limiter is being asked to admit. */ interface RateLimitContext { /** Request path, e.g. `/v2/cdn/stories/home`. */ path: string; /** Query parameters of the request, less the access token. */ query: Record; /** * Name of the quota this request draws from. The Content API client buckets * per rate-limit tier; the Management API client uses a single bucket. */ bucket: string; /** * Requests per second the client would allow for this bucket on its own. * * A custom limiter coordinating several instances against one token divides * this by the number of instances rather than hard-coding a limit that would * have to be the lowest tier's to stay safe. */ limit: number; } /** * Admission control for outgoing requests. * * Implement this to coordinate a fleet of clients sharing one token, for * example against Redis. The in-memory default applies per-second windows local * to the instance. * * The three hooks are called once per HTTP request, retries included, and are * handed the same context object so a limiter can pair a release to its * acquire. A rejection from `acquire` fails the request; one from either * reporting hook is swallowed, so a blip in shared storage cannot turn a served * request into an error. */ interface RateLimiter { /** * Resolves when the request may start. Nothing times this out, so a limiter * that can stall must impose its own deadline. */ acquire: (context: RateLimitContext) => Promise; /** * Reports a response, including one a retry went on to replace — a limiter * that only saw a request's last response would miss most of the throttling * it is meant to react to. * * The response is a clone, so reading its body is safe. */ recordResponse?: (context: RateLimitContext, response: Response) => void | Promise; /** Reports that the request settled, whether or not it succeeded. */ release?: (context: RateLimitContext) => void | Promise; } /** * Tuning for the additive-increase/multiplicative-decrease adaptation applied * by the default limiter. */ interface AdaptiveConfig { /** * Fraction the effective limit drops to when a request is throttled. A value * outside `(0, 1)` would make a back-off an increase, and falls back to the * default. * @default 0.5 */ decreaseFactor?: number; /** * Requests per second added back per `recoveryIntervalMs` of sustained * success. Defaults to a twenty-fifth of the bucket's ceiling, at least 1, so * that recovery takes about as long on a 50/s tier as on a 6/s one. A ceiling * above eight times the bucket's limit counts as eight times it. */ increaseStep?: number; /** * How long the limit must hold without being throttled before it recovers. * @default 1000 */ recoveryIntervalMs?: number; /** * Floor the effective limit never drops below. Values below 1 are raised to 1. * @default 1 */ minRequestsPerSecond?: number; /** * Shortest spacing between two decreases of the same bucket. A burst of * responses throttled by one overloaded window describes a single event, so * without this the limit would collapse to the floor on the first burst. * @default 1000 */ decreaseCooldownMs?: number; } /** * Lets a bucket's ceiling rise above its configured limit while an edge cache * is absorbing the traffic, instead of pinning it to the origin's quota. * * Both fields are required, so the mechanism is inert wherever it is not * configured — an API with no cache in front of it never opts in. */ interface CacheAwareConfig { /** * Highest rate the cache is assumed able to serve; the ceiling never passes it. * * It is an outer bound, not the operating one: twenty times a bucket's own * limit binds first on every bucket under a twentieth of this. */ cachedRequestsPerSecond: number; /** * Whether the response was served from cache without reaching the origin. * * Returns `undefined` when the response carries no such signal, which keeps * the bucket at its configured limit. A runtime that hides the header — a * browser, for one — therefore never leaves that limit. */ detectCacheHit: (response: Response) => boolean | undefined; } /** * What a bucket's pacing looks like after a response has been accounted for. */ interface RateLimitStatus { /** Quota the request drew from, as `context.bucket` named it. */ bucket: string; /** Requests per second the bucket is admitting right now. */ requestsPerSecond: number; /** Highest rate the bucket may recover to. */ ceiling: number; /** Rate the bucket would be paced at with no adaptation at all. */ configuredLimit: number; /** * Share of the observed responses the cache served, between 0 and 1. * * `undefined` while the bucket has no cache measurement to report: cache * awareness is off, or no response has carried a readable cache status. */ cacheHitShare: number | undefined; } interface DefaultRateLimiterOptions { /** AIMD adaptation. `false` pins every bucket to its base limit. @default true */ adaptive?: boolean | AdaptiveConfig; /** * Lowers a bucket's ceiling to the quota advertised by a response header. * Returns `undefined` when the response carries no applicable quota. */ parseServerLimit?: (response: Response) => number | undefined; /** * Raises a bucket's ceiling in proportion to the share of its responses the * cache serves. Omitted, the ceiling never passes the configured limit. * * Ignored when `adaptive` is `false`, which pins every bucket outright. */ cacheAware?: CacheAwareConfig; /** * Called whenever a response changes how a bucket is being paced, so a * caller can see the rate it is running at without inferring it from timings. * * It runs on the response path, so it should return quickly. A failure is * swallowed: observing the limiter must not turn a served request into an * error. */ onRateLimitChange?: (status: RateLimitStatus) => void; } /** * Creates the in-memory limiter used unless the caller supplies its own. * * Each bucket gets a per-second window sized by `context.limit`, shrinking * multiplicatively on a throttled response and recovering additively while none * is. It never drops below the configured floor, and never rises above * `context.limit` unless `cacheAware` is configured and a cache is observably * absorbing the bucket's traffic, in which case it may recover to a multiple of * it. */ declare function createDefaultRateLimiter(options?: DefaultRateLimiterOptions): RateLimiter; //#endregion export { AdaptiveConfig, CacheAwareConfig, DefaultRateLimiterOptions, RateLimitContext, RateLimitStatus, RateLimiter, createDefaultRateLimiter }; //# sourceMappingURL=limiter.d.mts.map