1import * as Cloudflare from "@/Cloudflare";
2import { Layer } from "effect";
3import * as Effect from "effect/Effect";
4import * as Path from "effect/Path";
5import { HttpRouter } from "effect/http";
6import * as Etag from "effect/http/Etag";
7import * as HttpPlatform from "effect/http/HttpPlatform";
8import * as HttpApi from "effect/http-api/HttpApi";
9import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder";
10import * as HttpApiGroup from "effect/http-api/HttpApiGroup";
11
12import { createTask, decodeTask, encodeTask, getTask, Task } from "./api.ts";
13
14const HttpPlatformStub = Layer.succeed(HttpPlatform.HttpPlatform, {
15 platform: "web",
16 compression: {
17 algorithms: new Set<HttpPlatform.CompressionAlgorithm>(),
18 compressResponse: (response) => Effect.succeed(response),
19 },
20 fileResponse: () => Effect.die("HttpPlatform.fileResponse not supported"),
21 fileWebResponse: () =>
22 Effect.die("HttpPlatform.fileWebResponse not supported"),
23});
24
25export class TasksDOGroup extends HttpApiGroup.make("TasksDO")
26 .add(getTask)
27 .add(createTask) {}
28
29export class TaskDOApi extends HttpApi.make("TaskDOApi").add(TasksDOGroup) {}
30
31/**
32 * Durable Object backing the `createTaskDO` / `getTaskDO` endpoints.
33 * Persists tasks in the DO's transactional storage and exposes simple
34 * RPC methods that the Worker calls from its HttpApi handlers.
35 */
36export default class TasksObject extends Cloudflare.DurableObject<TasksObject>()(
37 "TasksObject",
38 Effect.gen(function* () {
39 const state = yield* Cloudflare.DurableObjectState;
40
41 return Effect.gen(function* () {
42 const tasksGroup = HttpApiBuilder.group(
43 TaskDOApi,
44 "TasksDO",
45 (handlers) =>
46 handlers
47 .handle("getTask", ({ params }) =>
48 state.storage
49 .get<Task>(params.id)
50 .pipe(Effect.flatMap(decodeTask), Effect.orDie),
51 )
52 .handle("createTask", ({ payload }) => {
53 const id = crypto.randomUUID();
54 const task = new Task({
55 id,
56 title: payload.title,
57 completed: false,
58 });
59 return state.storage
60 .put(id, encodeTask(task))
61 .pipe(Effect.as(task));
62 }),
63 );
64
65 return {
66 fetch: HttpApiBuilder.layer(TaskDOApi).pipe(
67 Layer.provide(tasksGroup),
68 Layer.provide([Etag.layer, HttpPlatformStub, Path.layer]),
69 HttpRouter.toHttpEffect,
70 ),
71 };
72 });
73 }),
74) {}