1import type * as Cloudflare from "@/Cloudflare";
2import { Container, getContainer } from "@cloudflare/containers";
3import type { AsyncContainerWorker } from "./stack.ts";
4
5/**
6 * Container-backed Durable Object class hosted by a plain async Worker — the
7 * class implementation comes from the `@cloudflare/containers` npm package
8 * (the exact shape from issue #953, where the class is `@cloudflare/sandbox`'s
9 * `Sandbox`). `Container.fetch` forwards requests to the echo server listening
10 * on port 8080 inside the container.
11 */
12export class AsyncEchoObject extends Container {
13 defaultPort = 8080;
14}
15
16// InferEnv maps the Container binding to DurableObjectNamespace<AsyncEchoObject>,
17// which is exactly what `getContainer` expects.
18type Env = Cloudflare.InferEnv<typeof AsyncContainerWorker>;
19
20export default {
21 async fetch(request: Request, env: Env) {
22 const url = new URL(request.url);
23 // Reports what `env.ECHO` actually IS at runtime. A `durable_object_
24 // namespace` binding has `idFromName`; when the Worker instead uploads the
25 // Container declaration as a `json` binding, this echoes that json back
26 // (`{"_id":"Effect","op":"alchemy/EffectClass"}`) and `getContainer` below
27 // dies with `t.idFromName is not a function`.
28 if (url.pathname === "/binding") {
29 const binding = env.ECHO as unknown;
30 return Response.json({
31 kind:
32 typeof (binding as { idFromName?: unknown } | null)?.idFromName ===
33 "function"
34 ? "durable_object_namespace"
35 : JSON.stringify(binding),
36 });
37 }
38 if (url.pathname === "/hello") {
39 return getContainer(env.ECHO, "default").fetch(request);
40 }
41 return new Response("ok");
42 },
43};