1import * as Cloudflare from "@/Cloudflare/index.ts";
2import * as Alchemy from "@/index.ts";
3import * as Neon from "@/Neon/index.ts";
4import * as Effect from "effect/Effect";
5import * as Layer from "effect/Layer";
6import * as pathe from "pathe";
7import HyperdriveEffectWorker from "./effect-worker.ts";
8
9/**
10 * Async Worker that binds the shared Hyperdrive Connection via `env`. The
11 * Connection (and its Neon Postgres origin) share the same logical IDs as
12 * the ones the effect-worker binds, so the engine dedupes them to a single
13 * deployed resource that both workers observe.
14 */
15export const AsyncWorker = Cloudflare.Worker("HyperdriveAsyncWorker", {
16 main: pathe.resolve(import.meta.dirname, "async-worker.ts"),
17 env: {
18 HD: Effect.gen(function* () {
19 const project = yield* Neon.Project("HyperdriveBindingProject");
20 return yield* Cloudflare.Hyperdrive.Connection(
21 "HyperdriveBindingConnection",
22 { origin: project.origin },
23 );
24 }),
25 },
26});
27
28export type AsyncWorkerEnv = Cloudflare.InferEnv<typeof AsyncWorker>;
29
30/**
31 * Deploys two Workers that bind ONE shared Hyperdrive Connection — the
32 * effect-worker (via `Cloudflare.Hyperdrive.Connect`) and the async-worker
33 * (via `env: { HD: connection }`). Extracted into its own stack file so it
34 * can also be inspected directly, e.g.
35 *
36 * ```sh
37 * alchemy logs --tail --stage test --config ./test/Cloudflare/Hyperdrive/fixtures/stack.ts
38 * ```
39 */
40export default Alchemy.Stack(
41 "HyperdriveBindingStack",
42 {
43 providers: Layer.merge(Cloudflare.providers(), Neon.providers()),
44 state: Cloudflare.state(),
45 },
46 Effect.gen(function* () {
47 const effectWorker = yield* HyperdriveEffectWorker;
48 const asyncWorker = yield* AsyncWorker;
49 return {
50 effectWorkerUrl: effectWorker.url.as<string>(),
51 asyncWorkerUrl: asyncWorker.url.as<string>(),
52 };
53 }),
54);