1import type * as runtime from "@cloudflare/workers-types";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import * as Redacted from "effect/Redacted";
5import * as Output from "../../Output.ts";
6import { Worker, WorkerEnvironment } from "../Workers/Worker.ts";
7import { Connect, type ConnectClient } from "./Connect.ts";
8import type { Connection } from "./Connection.ts";
9import { defaultPort, type DevOrigin } from "./Connection.ts";
10
11export const ConnectBinding = Layer.effect(
12 Connect,
13 Effect.gen(function* () {
14 const env = yield* WorkerEnvironment;
15 const host = yield* Worker;
16
17 return Effect.fn(function* (connection: Connection) {
18 if (!globalThis.__ALCHEMY_RUNTIME__) {
19 yield* host.bind`${connection}`({
20 bindings: [
21 {
22 type: "hyperdrive",
23 name: connection.LogicalId,
24 id: connection.hyperdriveId as unknown as string,
25 },
26 ],
27 hyperdrives: getHyperdriveDevOrigin(connection),
28 });
29 }
30
31 const hd = Effect.sync(
32 () =>
33 (env as Record<string, runtime.Hyperdrive>)[connection.LogicalId]!,
34 );
35
36 return {
37 raw: hd,
38 connectionString: hd.pipe(
39 Effect.map((hd) => Redacted.make(hd.connectionString)),
40 ),
41 host: hd.pipe(Effect.map((hd) => hd.host)),
42 port: hd.pipe(Effect.map((hd) => hd.port)),
43 user: hd.pipe(Effect.map((hd) => hd.user)),
44 password: hd.pipe(Effect.map((hd) => Redacted.make(hd.password))),
45 database: hd.pipe(Effect.map((hd) => hd.database)),
46 } satisfies ConnectClient;
47 });
48 }),
49);
50
51export const getHyperdriveDevOrigin = (connection: Connection) => {
52 const origin = Output.map(
53 Output.all(connection.dev, connection.origin, connection.mtls),
54 ([dev, origin, mtls]): Required<DevOrigin> => {
55 if (dev) {
56 return {
57 scheme: dev.scheme,
58 host: dev.host,
59 port: dev.port ?? defaultPort(dev.scheme),
60 user: dev.user,
61 database: dev.database,
62 password: dev.password,
63 sslmode: dev.sslmode ?? "prefer",
64 };
65 }
66 if ("accessClientId" in origin) {
67 throw new Error(
68 `Hyperdrive instance ${connection.LogicalId} has an origin that requires Cloudflare Access. This is not supported in development mode. ` +
69 "Select a different origin or set the `dev` property to an origin that does not require Cloudflare Access.",
70 );
71 }
72 return {
73 scheme: origin.scheme,
74 host: origin.host,
75 port: origin.port ?? defaultPort(origin.scheme),
76 user: origin.user,
77 database: origin.database,
78 password: origin.password,
79 sslmode: mtls?.sslmode ?? "require",
80 };
81 },
82 );
83 return Output.map(
84 Output.all(connection.hyperdriveId, Output.asOutput(origin)),
85 ([id, origin]) => ({
86 [id]: origin,
87 }),
88 ) as unknown as Record<string, Required<DevOrigin>>;
89};