1import * as Cloudflare from "@/Cloudflare";
2import type { RuntimeContext } from "@/index";
3import * as Cause from "effect/Cause";
4import * as Effect from "effect/Effect";
5import * as HttpServerResponse from "effect/http/HttpServerResponse";
6
7/**
8 * A DO-hosting Effect Worker whose `DEPLOY_N` env var is read from a mutable
9 * module variable at plan time, so consecutive deploys can force a
10 * structural-signature change — and therefore an instance replacement —
11 * without editing any source file. The props Effect resolves in the engine
12 * (test) process; the resulting plain string is what crosses the RPC
13 * boundary to the dev sidecar.
14 */
15let deployN = "0";
16export const setDeployN = (n: string) => {
17 deployN = n;
18};
19
20export class HandoffObject extends Cloudflare.DurableObject<
21 HandoffObject,
22 {
23 ping: () => Effect.Effect<string, never, RuntimeContext>;
24 }
25>()("HandoffObject") {}
26
27export const HandoffObjectLive = HandoffObject.make(
28 Effect.gen(function* () {
29 return Effect.gen(function* () {
30 return {
31 ping: () => Effect.succeed("pong"),
32 };
33 });
34 }),
35);
36
37export class HandoffWorker extends Cloudflare.Worker<
38 HandoffWorker,
39 {},
40 HandoffObject
41>()("HandoffWorker") {}
42
43export default HandoffWorker.make(
44 Effect.sync(() => ({
45 main: import.meta.url,
46 env: {
47 DEPLOY_N: deployN,
48 },
49 })),
50 Effect.gen(function* () {
51 const objects = yield* HandoffObject;
52 return {
53 fetch: Effect.gen(function* () {
54 const env = yield* Cloudflare.Workers.WorkerEnvironment;
55 const pong = yield* objects.getByName("test").ping();
56 return yield* HttpServerResponse.json({
57 deploy: String(env.DEPLOY_N),
58 pong,
59 });
60 }).pipe(
61 Effect.catchCause((cause) =>
62 HttpServerResponse.json(
63 { error: Cause.pretty(cause) },
64 { status: 500 },
65 ),
66 ),
67 ),
68 };
69 }).pipe(Effect.provide(HandoffObjectLive)),
70);