| 1 | import * as Cloudflare from "@/Cloudflare"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Schedule from "effect/Schedule"; |
| 4 | import * as Stream from "effect/Stream"; |
| 5 | |
| 6 | const KV = Cloudflare.KV.Namespace("DurableObjectWorkerEnvironmentKV", { |
| 7 | title: "durable-object-worker-environment-kv", |
| 8 | }); |
| 9 | |
| 10 | export class WorkerEnvironmentKVObject extends Cloudflare.DurableObject<WorkerEnvironmentKVObject>()( |
| 11 | "WorkerEnvironmentKVObject", |
| 12 | Effect.gen(function* () { |
| 13 | const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); |
| 14 | |
| 15 | return Effect.gen(function* () { |
| 16 | const state = yield* Cloudflare.DurableObjectState; |
| 17 | return { |
| 18 | identity: () => Effect.sync(() => state.id.toString()), |
| 19 | put: (key: string, value: string) => kv.put(key, value), |
| 20 | get: (key: string) => kv.get(key), |
| 21 | // Egress colo is diagnostic, not a placement guarantee. |
| 22 | colo: () => |
| 23 | Effect.promise(async () => { |
| 24 | const response = await fetch( |
| 25 | "https://cloudflare.com/cdn-cgi/trace", |
| 26 | ); |
| 27 | const trace = await response.text(); |
| 28 | return trace.match(/^colo=(.*)$/m)?.[1] ?? "unknown"; |
| 29 | }), |
| 30 | // Mirrors the `tick` example from the tutorial: |
| 31 | // https://alchemy.run/cloudflare/compute/durable-objects |
| 32 | // An RPC method that returns a Stream of sequential numbers. |
| 33 | tick: (n: number) => |
| 34 | Stream.iterate(0, (i) => i + 1).pipe( |
| 35 | Stream.take(n), |
| 36 | Stream.schedule(Schedule.spaced("100 millis")), |
| 37 | ), |
| 38 | }; |
| 39 | }); |
| 40 | }).pipe(Effect.provide(Cloudflare.KV.ReadWriteNamespaceBinding)), |
| 41 | ) {} |