1import * as Cloudflare from "@/Cloudflare";
2import * as Alchemy from "@/index.ts";
3import * as Effect from "effect/Effect";
4import * as path from "pathe";
5import type { AsyncEchoObject } from "./worker.ts";
6
7/**
8 * A Container bound directly in an async Worker's `env` (issue #953): the
9 * Container IS the Durable Object binding plus its ContainerApplication.
10 * The class implementation ships inside the worker script (from
11 * `@cloudflare/containers`); `className` names the exported class since it
12 * differs from the binding name (`ECHO`).
13 *
14 * The Container's logical id deliberately MATCHES the env key. The Worker's
15 * `durable_object_namespace` binding and its `containers` script metadata
16 * describe the same env entry and must be contributed under one `sid` —
17 * binding rows are collapsed by sid (last write wins), so splitting them
18 * across two `bind` calls (the env key and the ContainerApplication's logical
19 * id) dropped the namespace binding for exactly this shape. See the
20 * "async container bound on env" cases in `Container.test.ts`.
21 */
22export const AsyncContainerWorker = Cloudflare.Worker("AsyncContainerWorker", {
23 main: path.resolve(import.meta.dirname, "worker.ts"),
24 env: {
25 ECHO: Cloudflare.Container<AsyncEchoObject>("ECHO", {
26 className: "AsyncEchoObject",
27 image: "mendhak/http-https-echo:latest",
28 observability: { logs: { enabled: true } },
29 }),
30 },
31});
32
33export default (state = Cloudflare.state()) =>
34 Alchemy.Stack(
35 "AsyncContainerStack",
36 { providers: Cloudflare.providers(), state },
37 Effect.gen(function* () {
38 const worker = yield* AsyncContainerWorker;
39 return { url: worker.url.as<string>() };
40 }),
41 );