rateLimiterDO.ts
Language TypeScript1.6 KB43 lines
import { DurableObject } from "cloudflare:workers";import { PLINTH_RATE_LIMIT, reserveSlot, sleep, type RateLimiter } from "./lib/rateLimit.js";/*** Shared rate-limiter Durable Object (Workers only).** Every Plinth request across ALL McpAgent sessions and isolates routes through* ONE instance, `idFromName("plinth-global")`, so the global 10/10s budget holds* no matter how many users or parallel tool calls are in flight. A per-isolate* limiter would let each session spend the full budget independently and blow* the API limit.** State is in-memory: a rate limiter needs no durability. Worst case, an* eviction resets the window and permits one small burst, which is harmless.* The reserve computation is synchronous (no await), so it is atomic even when* the DO handles concurrent RPC calls.*/export class RateLimiterDO extends DurableObject {private slots: number[] = [];/** Reserve one slot; returns the ms the caller must wait before its request. */reserve(): number {const { delayMs, slots } = reserveSlot(this.slots,Date.now(),PLINTH_RATE_LIMIT.max,PLINTH_RATE_LIMIT.windowMs,);this.slots = slots;return delayMs;}}/** Workers RateLimiter: delegates scheduling to the one shared limiter DO. */export class WorkerRateLimiter implements RateLimiter {constructor(private readonly ns: DurableObjectNamespace<RateLimiterDO>) {}async acquire(): Promise<void> {const stub = this.ns.get(this.ns.idFromName("plinth-global"));const delayMs = await stub.reserve();if (delayMs > 0) await sleep(delayMs);}}