1import type { PresignError } from "@distilled.cloud/aws/Presign";
2import type * as Effect from "effect/Effect";
3import * as Binding from "../../Binding.ts";
4import type { RuntimeContext } from "../../RuntimeContext.ts";
5import type { SqlConnectionInfo } from "../Connection/internal.ts";
6import { connectEnvPrefix as makeConnectEnvPrefix } from "../Connection/internal.ts";
7import type { Cluster } from "./Cluster.ts";
8
9export interface ConnectOptions {
10 /**
11 * Connect as the built-in `admin` role. Grants `dsql:DbConnectAdmin` on the
12 * cluster (instead of `dsql:DbConnect`) and signs the auth token for the
13 * `DbConnectAdmin` action.
14 * @default false
15 */
16 admin?: boolean;
17 /**
18 * Database role to connect as when `admin` is not set. Must be a Postgres
19 * role in the cluster that was granted to an IAM identity via
20 * `AWS IAM GRANT`.
21 */
22 username?: string;
23 /**
24 * Database to connect to.
25 * @default "postgres"
26 */
27 database?: string;
28}
29
30/**
31 * Environment variable prefix under which {@link Connect} publishes the
32 * cluster endpoint on the host Function, derived from the cluster's logical
33 * ID. A cluster with logical ID `AppDb` yields `DSQL_APPDB` and the variable
34 * `DSQL_APPDB_HOST`.
35 */
36export const connectEnvPrefix = (logicalId: string): string =>
37 makeConnectEnvPrefix("DSQL", logicalId);
38
39/**
40 * Runtime binding that resolves connection settings for an Aurora DSQL
41 * cluster using IAM database authentication.
42 *
43 * DSQL clusters expose a **public** Postgres-wire endpoint
44 * (`<clusterId>.dsql.<region>.on.aws:5432`) and authenticate exclusively
45 * with short-lived IAM auth tokens — there is no password secret and no VPC
46 * requirement. The deploy half grants `dsql:DbConnect` (or
47 * `dsql:DbConnectAdmin` with `admin: true`) on the cluster to the host
48 * Function; the runtime half mints a presigned token client-side (a pure
49 * SigV4 computation, no API call) and returns a {@link SqlConnectionInfo}
50 * whose `url` feeds `Drizzle.Postgres` / any Postgres client directly.
51 *
52 * Token freshness is structural: yielding the returned connection effect
53 * re-mints the token, and execution-scoped pools (`Drizzle.Postgres`)
54 * rebuild per execution — a ~15-minute token can never outlive its pool.
55 *
56 * ### Connecting to a Cluster
57 * **Example:** Resolve Connection Info inside a Function
58 * ```typescript
59 * const conn = yield* DSQL.Connect(cluster, { admin: true });
60 * // inside a handler — each yield mints a fresh auth token:
61 * const { host, port, username, password, url } = yield* conn;
62 * ```
63 *
64 * **Example:** Drizzle over DSQL
65 * ```typescript
66 * const conn = yield* DSQL.Connect(cluster, { admin: true });
67 * const db = yield* Drizzle.Postgres(conn.pipe(Effect.map((info) => info.url)));
68 * // inside a handler:
69 * const rows = yield* db.select().from(Widgets);
70 * ```
71 *
72 * **Example:** Connect as a Custom Database Role
73 * ```typescript
74 * const conn = yield* DSQL.Connect(cluster, {
75 * username: "app",
76 * database: "postgres",
77 * });
78 * ```
79 *
80 * @binding
81 */
82export interface Connect extends Binding.Service<
83 Connect,
84 "AWS.DSQL.Connect",
85 (
86 cluster: Cluster,
87 options?: ConnectOptions,
88 ) => Effect.Effect<
89 Effect.Effect<SqlConnectionInfo, PresignError, RuntimeContext>
90 >
91> {}
92export const Connect = Binding.Service<Connect>("AWS.DSQL.Connect");