1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { Profile } from "./Profile.ts";
5
6/**
7 * Shared scaffolding for Route 53 Profiles HTTP bindings.
8 *
9 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
10 * a `Layer.effect(Cap, makeProfilesHttpBinding({ … }))` over the builder
11 * below. Everything except the operation, the IAM action list, and the grant
12 * scope is boilerplate.
13 */
14
15/**
16 * Build the impl Effect for a Route 53 Profiles operation scoped to a
17 * {@link Profile}: the deploy-time half grants `actions`, and the runtime
18 * half injects the profile's `ProfileId` into every request.
19 *
20 * The grant is on `*` — the Route 53 Profiles list actions follow the
21 * common AWS List-action pattern of not supporting resource-level
22 * permissions (a profile-ARN-scoped grant is rejected with AccessDenied).
23 */
24export const makeProfilesHttpBinding = <
25 I extends { ProfileId?: string },
26 A,
27 E,
28 R,
29>(options: {
30 /** Fully-qualified binding tag, e.g. `AWS.Route53Profiles.ListProfileAssociations`. */
31 tag: string;
32 /** The distilled operation. */
33 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
34 /** IAM actions granted by the binding. */
35 actions: readonly string[];
36}) =>
37 Effect.gen(function* () {
38 const op = yield* options.operation;
39
40 return Effect.fn(function* (profile: Profile) {
41 const ProfileId = yield* profile.profileId;
42 if (!globalThis.__ALCHEMY_RUNTIME__) {
43 const host = yield* Binding.Host;
44 if (isBindingHost(host)) {
45 yield* host.bind`Allow(${host}, ${options.tag}(${profile}))`({
46 policyStatements: [
47 {
48 Effect: "Allow",
49 Action: [...options.actions],
50 Resource: ["*"],
51 },
52 ],
53 });
54 }
55 }
56 return Effect.fn(`${options.tag}(${profile.LogicalId})`)(function* (
57 request?: Omit<I, "ProfileId">,
58 ) {
59 const profileId = yield* ProfileId;
60 return yield* op({ ...request, ProfileId: profileId } as I);
61 });
62 });
63 });