1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { ResolverEndpoint } from "./ResolverEndpoint.ts";
5import type { ResolverRule } from "./ResolverRule.ts";
6
7/**
8 * Shared scaffolding for AWS Route 53 Resolver HTTP bindings.
9 *
10 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
11 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two
12 * builders below. Everything except the operation and the IAM action is
13 * boilerplate:
14 *
15 * - {@link makeRoute53ResolverEndpointHttpBinding} — operations scoped to one
16 * bound {@link ResolverEndpoint}. The runtime callable injects the
17 * endpoint's ID as the request's `ResolverEndpointId`; the deploy-time half
18 * grants `actions` on the endpoint ARN.
19 * - {@link makeRoute53ResolverRuleHttpBinding} — operations scoped to one
20 * bound {@link ResolverRule}; injects `ResolverRuleId` and grants `actions`
21 * on the rule ARN.
22 */
23
24/**
25 * Build the impl Effect for an operation scoped to a single bound
26 * {@link ResolverEndpoint}. The runtime callable injects the endpoint's ID as
27 * the request's `ResolverEndpointId`; the deploy-time half grants `actions`
28 * on the endpoint ARN.
29 */
30export const makeRoute53ResolverEndpointHttpBinding = <
31 I extends { ResolverEndpointId?: string },
32 A,
33 E,
34 R,
35>(options: {
36 /** Fully-qualified binding tag, e.g. `AWS.Route53Resolver.GetResolverEndpoint`. */
37 tag: string;
38 /** The distilled operation; `ResolverEndpointId` is injected from the resource. */
39 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
40 /** IAM actions granted on the resolver endpoint ARN. */
41 actions: readonly string[];
42}) =>
43 Effect.gen(function* () {
44 const op = yield* options.operation;
45
46 return Effect.fn(function* (endpoint: ResolverEndpoint) {
47 const ResolverEndpointId = yield* endpoint.resolverEndpointId;
48 if (!globalThis.__ALCHEMY_RUNTIME__) {
49 const host = yield* Binding.Host;
50 if (isBindingHost(host)) {
51 yield* host.bind`Allow(${host}, ${options.tag}(${endpoint}))`({
52 policyStatements: [
53 {
54 Effect: "Allow",
55 Action: [...options.actions],
56 Resource: [endpoint.resolverEndpointArn],
57 },
58 ],
59 });
60 }
61 }
62 return Effect.fn(`${options.tag}(${endpoint.LogicalId})`)(function* (
63 request?: Omit<I, "ResolverEndpointId">,
64 ) {
65 return yield* op({
66 ...request,
67 ResolverEndpointId: yield* ResolverEndpointId,
68 } as I);
69 });
70 });
71 });
72
73/**
74 * Build the impl Effect for an operation scoped to a single bound
75 * {@link ResolverRule}. The runtime callable injects the rule's ID as the
76 * request's `ResolverRuleId`; the deploy-time half grants `actions` on the
77 * rule ARN.
78 */
79export const makeRoute53ResolverRuleHttpBinding = <
80 I extends { ResolverRuleId?: string },
81 A,
82 E,
83 R,
84>(options: {
85 /** Fully-qualified binding tag, e.g. `AWS.Route53Resolver.GetResolverRule`. */
86 tag: string;
87 /** The distilled operation; `ResolverRuleId` is injected from the resource. */
88 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
89 /** IAM actions granted on the resolver rule ARN. */
90 actions: readonly string[];
91}) =>
92 Effect.gen(function* () {
93 const op = yield* options.operation;
94
95 return Effect.fn(function* (rule: ResolverRule) {
96 const ResolverRuleId = yield* rule.resolverRuleId;
97 if (!globalThis.__ALCHEMY_RUNTIME__) {
98 const host = yield* Binding.Host;
99 if (isBindingHost(host)) {
100 yield* host.bind`Allow(${host}, ${options.tag}(${rule}))`({
101 policyStatements: [
102 {
103 Effect: "Allow",
104 Action: [...options.actions],
105 Resource: [rule.resolverRuleArn],
106 },
107 ],
108 });
109 }
110 }
111 return Effect.fn(`${options.tag}(${rule.LogicalId})`)(function* (
112 request?: Omit<I, "ResolverRuleId">,
113 ) {
114 return yield* op({
115 ...request,
116 ResolverRuleId: yield* ResolverRuleId,
117 } as I);
118 });
119 });
120 });