1import type * as serverless from "@distilled.cloud/aws/redshift-serverless";
2import type * as Duration from "effect/Duration";
3import type * as Effect from "effect/Effect";
4import * as Binding from "../../Binding.ts";
5import type { RuntimeContext } from "../../RuntimeContext.ts";
6import {
7 connectEnvPrefix as makeConnectEnvPrefix,
8 type SqlConnectionInfo,
9} from "../Connection/internal.ts";
10import type { Workgroup } from "./Workgroup.ts";
11
12/**
13 * Connection descriptor for a Redshift Serverless workgroup. Extends the
14 * shared SQL shape with the temporary credential's expiration so callers
15 * can reason about token lifetime (credentials are minted fresh on every
16 * resolution of the runtime effect, so per-execution pools never outlive
17 * them).
18 */
19export interface WorkgroupConnectionInfo extends SqlConnectionInfo {
20 /**
21 * When the temporary database credentials expire (15-60 minutes from
22 * minting, per {@link ConnectOptions.duration}).
23 */
24 expiration: Date | undefined;
25}
26
27export interface ConnectOptions {
28 /**
29 * Database to connect to.
30 * @default "dev"
31 */
32 database?: string;
33 /**
34 * How long the temporary credentials remain valid, e.g. `"15 minutes"` or
35 * `Duration.minutes(30)` (a bare number is milliseconds). Rounded to whole
36 * seconds on the wire; must land between 900 and 3600 seconds.
37 * @default 900 seconds
38 */
39 duration?: Duration.Input;
40}
41
42/**
43 * Environment variable prefix under which {@link Connect} publishes the
44 * workgroup endpoint on the host Function, derived from the workgroup's
45 * logical ID. A workgroup with logical ID `Analytics` yields
46 * `REDSHIFT_SERVERLESS_ANALYTICS` and the variables
47 * `REDSHIFT_SERVERLESS_ANALYTICS_HOST` /
48 * `REDSHIFT_SERVERLESS_ANALYTICS_PORT`.
49 */
50export const connectEnvPrefix = (logicalId: string): string =>
51 makeConnectEnvPrefix("REDSHIFT_SERVERLESS", logicalId);
52
53/**
54 * Runtime binding that resolves pgwire connection settings for a Redshift
55 * Serverless {@link Workgroup} using IAM-minted temporary database
56 * credentials.
57 *
58 * At deploy time it attaches the `redshift-serverless:GetCredentials` IAM
59 * policy on the workgroup ARN and publishes the workgroup endpoint as
60 * environment variables. At runtime it calls
61 * `redshift-serverless:GetCredentials` to mint short-lived credentials and
62 * returns a typed {@link WorkgroupConnectionInfo} whose `url` feeds
63 * `Drizzle.Postgres` directly.
64 *
65 * The Redshift Data API ({@link RedshiftData.Statements}) remains the
66 * recommended default — it needs no driver, no VPC reach, and no credential
67 * plumbing. Use `Connect` when you want a real pgwire connection (e.g.
68 * Drizzle). Redshift speaks the postgres wire protocol on port 5439 —
69 * configure Drizzle with `prepare: false` and avoid `RETURNING` (Redshift
70 * does not support either). The host Function must be able to reach the
71 * workgroup endpoint (attach it to the workgroup's VPC, or make the
72 * workgroup `publiclyAccessible`). Provide the implementation with
73 * `Effect.provide(AWS.RedshiftServerless.ConnectHttp)`.
74 *
75 * ### Connecting to a Workgroup
76 * **Example:** Resolve Connection Info inside a Function
77 * ```typescript
78 * const connect = yield* RedshiftServerless.Connect(workgroup);
79 * // inside a handler — mints fresh temporary credentials:
80 * const { host, port, username, password, url } = yield* connect;
81 * ```
82 *
83 * **Example:** Drizzle over the Connection URL
84 * ```typescript
85 * const connect = yield* RedshiftServerless.Connect(workgroup, {
86 * database: "analytics",
87 * });
88 * const db = yield* Drizzle.Postgres(
89 * Effect.map(connect, (info) => info.url),
90 * { prepare: false },
91 * );
92 * ```
93 *
94 * @binding
95 */
96export interface Connect extends Binding.Service<
97 Connect,
98 "AWS.RedshiftServerless.Connect",
99 (
100 workgroup: Workgroup,
101 options?: ConnectOptions,
102 ) => Effect.Effect<
103 Effect.Effect<
104 WorkgroupConnectionInfo,
105 serverless.GetCredentialsError,
106 RuntimeContext
107 >
108 >
109> {}
110export const Connect = Binding.Service<Connect>(
111 "AWS.RedshiftServerless.Connect",
112);