1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import * as Output from "../../Output.ts";
4import { isBindingHost } from "../Lambda/Function.ts";
5import type { Connection } from "./Connection.ts";
6import type { RepositoryLink } from "./RepositoryLink.ts";
7import type { SyncConfiguration } from "./SyncConfiguration.ts";
8
9/**
10 * Shared HTTP scaffolding for the CodeConnections runtime bindings.
11 *
12 * Every capability follows the same shape — resolve the distilled
13 * operation, register an IAM policy statement on the binding host, and
14 * return a runtime callable. The only variation is the operation, the IAM
15 * action(s), and the identifier(s) injected from the bound resource:
16 * a {@link Connection}'s ARN, a {@link RepositoryLink}'s ID, or a
17 * {@link SyncConfiguration}'s (resource name, sync type) identity.
18 *
19 * NOT exported from `index.ts`.
20 */
21
22/**
23 * Build the impl Effect for a connection-scoped operation: the runtime
24 * callable injects the bound {@link Connection}'s ARN as `ConnectionArn`
25 * and the deploy-time half grants `actions` on the connection ARN.
26 */
27export const makeConnectionScopedHttpBinding = <
28 I extends { ConnectionArn: string },
29 A,
30 E,
31 R,
32>(options: {
33 /** Fully-qualified binding tag, e.g. `AWS.CodeConnections.GetConnection`. */
34 tag: string;
35 /** The distilled operation; `ConnectionArn` is injected from the resource. */
36 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
37 /** IAM actions granted on the connection ARN. */
38 actions: readonly string[];
39}) =>
40 Effect.gen(function* () {
41 const op = yield* options.operation;
42
43 return Effect.fn(function* (connection: Connection) {
44 const ConnectionArn = yield* connection.connectionArn;
45 if (!globalThis.__ALCHEMY_RUNTIME__) {
46 const host = yield* Binding.Host;
47 if (isBindingHost(host)) {
48 yield* host.bind`Allow(${host}, ${options.tag}(${connection}))`({
49 policyStatements: [
50 {
51 Effect: "Allow",
52 Action: [...options.actions],
53 Resource: [Output.interpolate`${connection.connectionArn}`],
54 },
55 ],
56 });
57 }
58 }
59 return Effect.fn(`${options.tag}(${connection.LogicalId})`)(function* (
60 request?: Omit<I, "ConnectionArn">,
61 ) {
62 return yield* op({
63 ...request,
64 ConnectionArn: yield* ConnectionArn,
65 } as I);
66 });
67 });
68 });
69
70/**
71 * Build the impl Effect for a repository-link-scoped operation: the runtime
72 * callable injects the bound {@link RepositoryLink}'s ID as
73 * `RepositoryLinkId` and the deploy-time half grants `actions` on the
74 * repository link ARN.
75 */
76export const makeRepositoryLinkScopedHttpBinding = <
77 I extends { RepositoryLinkId: string },
78 A,
79 E,
80 R,
81>(options: {
82 /** Fully-qualified binding tag. */
83 tag: string;
84 /** The distilled operation; `RepositoryLinkId` is injected from the resource. */
85 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
86 /** IAM actions granted on the repository link ARN. */
87 actions: readonly string[];
88}) =>
89 Effect.gen(function* () {
90 const op = yield* options.operation;
91
92 return Effect.fn(function* (repositoryLink: RepositoryLink) {
93 const RepositoryLinkId = yield* repositoryLink.repositoryLinkId;
94 if (!globalThis.__ALCHEMY_RUNTIME__) {
95 const host = yield* Binding.Host;
96 if (isBindingHost(host)) {
97 yield* host.bind`Allow(${host}, ${options.tag}(${repositoryLink}))`({
98 policyStatements: [
99 {
100 Effect: "Allow",
101 Action: [...options.actions],
102 Resource: [
103 Output.interpolate`${repositoryLink.repositoryLinkArn}`,
104 ],
105 },
106 ],
107 });
108 }
109 }
110 return Effect.fn(`${options.tag}(${repositoryLink.LogicalId})`)(
111 function* (request: Omit<I, "RepositoryLinkId">) {
112 return yield* op({
113 ...request,
114 RepositoryLinkId: yield* RepositoryLinkId,
115 } as I);
116 },
117 );
118 });
119 });
120
121/**
122 * Build the impl Effect for a sync-configuration-scoped operation: the
123 * runtime callable injects the bound {@link SyncConfiguration}'s
124 * `ResourceName` + `SyncType` identity. Sync operations are not
125 * resource-scoped in IAM (sync configurations have no ARN), so the
126 * deploy-time half grants `actions` on `*`.
127 */
128export const makeSyncConfigurationScopedHttpBinding = <
129 I extends { ResourceName: string; SyncType: string },
130 A,
131 E,
132 R,
133>(options: {
134 /** Fully-qualified binding tag. */
135 tag: string;
136 /** The distilled operation; `ResourceName` + `SyncType` are injected. */
137 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
138 /** IAM actions granted on `*`. */
139 actions: readonly string[];
140}) =>
141 Effect.gen(function* () {
142 const op = yield* options.operation;
143
144 return Effect.fn(function* (syncConfiguration: SyncConfiguration) {
145 const ResourceName = yield* syncConfiguration.resourceName;
146 const SyncType = yield* syncConfiguration.syncType;
147 if (!globalThis.__ALCHEMY_RUNTIME__) {
148 const host = yield* Binding.Host;
149 if (isBindingHost(host)) {
150 yield* host.bind`Allow(${host}, ${options.tag}(${syncConfiguration}))`(
151 {
152 policyStatements: [
153 {
154 Effect: "Allow",
155 Action: [...options.actions],
156 Resource: ["*"],
157 },
158 ],
159 },
160 );
161 }
162 }
163 return Effect.fn(`${options.tag}(${syncConfiguration.LogicalId})`)(
164 function* (request?: Omit<I, "ResourceName" | "SyncType">) {
165 return yield* op({
166 ...request,
167 ResourceName: yield* ResourceName,
168 SyncType: yield* SyncType,
169 } as I);
170 },
171 );
172 });
173 });
174
175/**
176 * Build the impl Effect for an account-level operation (no target
177 * resource). The deploy-time half grants `actions` on `*`.
178 */
179export const makeCodeConnectionsAccountHttpBinding = <I, A, E, R>(options: {
180 /** Fully-qualified binding tag, e.g. `AWS.CodeConnections.ListConnections`. */
181 tag: string;
182 /** The distilled operation, invoked with the caller's request as-is. */
183 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
184 /** IAM actions granted on `*`. */
185 actions: readonly string[];
186}) =>
187 Effect.gen(function* () {
188 const op = yield* options.operation;
189
190 return Effect.fn(function* () {
191 if (!globalThis.__ALCHEMY_RUNTIME__) {
192 const host = yield* Binding.Host;
193 if (isBindingHost(host)) {
194 yield* host.bind`Allow(${host}, ${options.tag}())`({
195 policyStatements: [
196 {
197 Effect: "Allow",
198 Action: [...options.actions],
199 Resource: ["*"],
200 },
201 ],
202 });
203 }
204 }
205 return Effect.fn(options.tag)(function* (request?: I) {
206 return yield* op((request ?? {}) as I);
207 });
208 });
209 });