Rebasely Beta: data may be purged

examples/MCP-tools Public

TypeScript · 66 lines · 2.6 KiB · at dae155a

// Small stand-ins for the Workers runtime, enough for the code under test.import { take, type Bucket } from "../src/rate-limiter/bucket.ts";import type { RateLimiter } from "../src/rate-limiter/index.ts";type Entry = { value: string; metadata: unknown };// The parts of KVNamespace that src/usage.ts uses, over a Map. list() pages by `limit` so the// cursor loop gets exercised.export function fakeKv(limit = 1000): KVNamespace & { entries: Map<string, Entry> } {  const entries = new Map<string, Entry>();  const kv = {    entries,    async get(key: string, type?: string) {      const e = entries.get(key);      if (!e) return null;      return type === "json" ? JSON.parse(e.value) : e.value;    },    async put(key: string, value: string, options?: { metadata?: unknown }) {      entries.set(key, { value, metadata: options?.metadata ?? null });    },    async list(options: { prefix?: string; cursor?: string } = {}) {      const names = [...entries.keys()].filter((k) => k.startsWith(options.prefix ?? "")).sort();      const start = Number(options.cursor ?? 0);      const page = names.slice(start, start + limit);      const done = start + limit >= names.length;      return {        keys: page.map((name) => ({ name, metadata: entries.get(name)!.metadata })),        list_complete: done,        ...(done ? {} : { cursor: String(start + limit) }),      };    },  };  return kv as unknown as KVNamespace & { entries: Map<string, Entry> };}// An ExecutionContext that keeps what was handed to waitUntil, so a test can await it.export function fakeCtx(): ExecutionContext & { settle: () => Promise<void> } {  const pending: Promise<unknown>[] = [];  const ctx = {    waitUntil: (p: Promise<unknown>) => void pending.push(p),    passThroughOnException() {},    props: {},    settle: async () => void (await Promise.all(pending.splice(0))),  };  return ctx as unknown as ExecutionContext & { settle: () => Promise<void> };}// A RATE_LIMITER namespace with one in-memory bucket per name, running the real bucket logic// against a clock the test controls.export function fakeLimiter(clock = { now: 0 }): DurableObjectNamespace<RateLimiter> & { clock: { now: number } } {  const buckets = new Map<string, Bucket>();  const ns = {    clock,    getByName(name: string) {      return {        async take() {          const { bucket, decision } = take(buckets.get(name), clock.now);          buckets.set(name, bucket);          return decision;        },      };    },  };  return ns as unknown as DurableObjectNamespace<RateLimiter> & { clock: { now: number } };}