1import type * as runtime from "@cloudflare/workers-types";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import { Worker, WorkerEnvironment } from "../Workers/Worker.ts";
5import {
6 Send,
7 SendEmailError,
8 type SendClient,
9 type SendEmailMessage,
10} from "./Send.ts";
11import type { SendEmail } from "./SendEmail.ts";
12
13export const SendBinding = Layer.effect(
14 Send,
15 Effect.gen(function* () {
16 const host = yield* Worker;
17 const env = yield* WorkerEnvironment;
18
19 return Effect.fn(function* (sender: SendEmail) {
20 if (!globalThis.__ALCHEMY_RUNTIME__) {
21 yield* host.bind(sender.name, {
22 bindings: [
23 {
24 type: "send_email",
25 name: sender.name,
26 destinationAddress: sender.destinationAddress,
27 allowedDestinationAddresses: sender.allowedDestinationAddresses,
28 allowedSenderAddresses: sender.allowedSenderAddresses,
29 },
30 ],
31 // Dev-only local-emulation opt-out, contributed as a parallel
32 // channel (like `hyperdrives`) so the wire binding stays pure.
33 ...(sender.devRemote ? { devRemote: { [sender.name]: true } } : {}),
34 });
35 }
36
37 const raw = Effect.sync(
38 () => (env as Record<string, runtime.SendEmail>)[sender.name]!,
39 );
40
41 const tryPromise = <T>(
42 fn: () => Promise<T>,
43 ): Effect.Effect<T, SendEmailError> =>
44 Effect.tryPromise({
45 try: fn,
46 catch: (error: any) =>
47 new SendEmailError({
48 message: error?.message ?? "Unknown send_email error",
49 cause: error,
50 }),
51 });
52
53 return {
54 raw,
55 send: (message: SendEmailMessage) =>
56 raw.pipe(Effect.flatMap((s) => tryPromise(() => s.send(message)))),
57 sendRaw: (message: runtime.EmailMessage) =>
58 raw.pipe(Effect.flatMap((s) => tryPromise(() => s.send(message)))),
59 } satisfies SendClient;
60 });
61 }),
62);