1// Async (non-Effect) Worker that exercises a native KV binding:
2// put / get / getWithMetadata / list / delete. The binding under test is
3// selected with `?binding=NAME` (default `KV`) so one worker can exercise
4// several namespaces — e.g. a locally-simulated one and an
5// `Alchemy.remote()`-opted one side by side.
6interface KVNamespaceLike {
7 put(
8 key: string,
9 value: string,
10 options?: { metadata?: unknown },
11 ): Promise<void>;
12 get(key: string): Promise<string | null>;
13 getWithMetadata(
14 key: string,
15 ): Promise<{ value: string | null; metadata: unknown }>;
16 list(): Promise<{ keys: Array<{ name: string }> }>;
17 delete(key: string): Promise<void>;
18}
19
20type Env = Record<string, KVNamespaceLike>;
21
22export default {
23 fetch: async (request: Request, env: Env) => {
24 const url = new URL(request.url);
25 const kv = env[url.searchParams.get("binding") ?? "KV"];
26 if (url.pathname === "/roundtrip") {
27 await kv.put("key1", "value1");
28 await kv.put("key2", "value2", { metadata: { hello: "world" } });
29 const value = await kv.get("key1");
30 const { value: value2, metadata } = await kv.getWithMetadata("key2");
31 const list = await kv.list();
32 await kv.delete("key1");
33 const afterDelete = await kv.get("key1");
34 return Response.json({
35 value,
36 value2,
37 metadata,
38 keys: list.keys.map((k) => k.name),
39 afterDelete,
40 });
41 }
42 if (url.pathname === "/get") {
43 const value = await kv.get(url.searchParams.get("key") ?? "");
44 return Response.json({ value });
45 }
46 return new Response("not found", { status: 404 });
47 },
48};