1import * as Cloudflare from "@/Cloudflare";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import * as Stream from "effect/Stream";
5import * as RpcSerialization from "effect/rpc/RpcSerialization";
6import * as RpcServer from "effect/rpc/RpcServer";
7import { CounterRpcs } from "./group.ts";
8
9/**
10 * Typed counter Durable Object built on
11 * {@link Cloudflare.RpcDurableObject}. Persists `count` in
12 * `state.storage` and serves `Increment` / `Get` / `CountUpTo` over
13 * an `RpcServer.toHttpEffect(group)` on the DO's `fetch`.
14 */
15export default class RpcCounterObject extends Cloudflare.RpcDurableObject<RpcCounterObject>()(
16 "RpcCounterObject",
17 { schema: CounterRpcs },
18 Effect.gen(function* () {
19 const state = yield* Cloudflare.DurableObjectState;
20
21 return Effect.gen(function* () {
22 let count = (yield* state.storage.get<number>("count")) ?? 0;
23
24 const handlers = CounterRpcs.toLayer({
25 Increment: () =>
26 Effect.gen(function* () {
27 count += 1;
28 yield* state.storage.put("count", count);
29 return { count };
30 }),
31 Get: () => Effect.succeed({ count }),
32 CountUpTo: ({ upto }) =>
33 Stream.fromIterable(
34 Array.from({ length: Math.max(0, upto) }, (_, i) => i + 1),
35 ),
36 Reset: () =>
37 Effect.gen(function* () {
38 count = 0;
39 yield* state.storage.put("count", 0);
40 }),
41 });
42
43 return RpcServer.toHttpEffect(CounterRpcs).pipe(
44 Effect.provide(Layer.mergeAll(handlers, RpcSerialization.layerNdjson)),
45 );
46 });
47 }),
48) {}