1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import type { PolicyStatement } from "../IAM/Policy.ts";
4import { isBindingHost } from "../Lambda/Function.ts";
5import type { Accelerator } from "./Accelerator.ts";
6import { withGaRegion } from "./common.ts";
7import type { EndpointGroup } from "./EndpointGroup.ts";
8
9/**
10 * Shared scaffolding for AWS Global Accelerator HTTP bindings.
11 *
12 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
13 * a `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two builders
14 * below. Everything except the operation and the IAM action list is
15 * boilerplate.
16 *
17 * The Global Accelerator control-plane API only exists in us-west-2, so the
18 * operation is resolved under {@link withGaRegion} — the binding works no
19 * matter which region the host Function is deployed to.
20 */
21
22/**
23 * Build the impl Effect for a Global Accelerator operation scoped to an
24 * {@link Accelerator}: the deploy-time half grants `actions` on the bound
25 * accelerator's ARN, and the runtime half injects the accelerator's
26 * `AcceleratorArn` into every request.
27 */
28export const makeGaAcceleratorHttpBinding = <
29 I extends { AcceleratorArn: string },
30 A,
31 E,
32 R,
33>(options: {
34 /** Fully-qualified binding tag, e.g. `AWS.GlobalAccelerator.DescribeAccelerator`. */
35 tag: string;
36 /** The distilled operation. */
37 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
38 /** IAM actions granted on the accelerator ARN. */
39 actions: readonly string[];
40}) =>
41 Effect.gen(function* () {
42 const op = yield* withGaRegion(options.operation);
43
44 return Effect.fn(function* (accelerator: Accelerator) {
45 const AcceleratorArn = yield* accelerator.acceleratorArn;
46 if (!globalThis.__ALCHEMY_RUNTIME__) {
47 const host = yield* Binding.Host;
48 if (isBindingHost(host)) {
49 yield* host.bind`Allow(${host}, ${options.tag}(${accelerator}))`({
50 policyStatements: [
51 {
52 Effect: "Allow",
53 Action: [...options.actions],
54 Resource: [accelerator.acceleratorArn],
55 },
56 ],
57 });
58 }
59 }
60 return Effect.fn(`${options.tag}(${accelerator.LogicalId})`)(function* (
61 request?: Omit<I, "AcceleratorArn">,
62 ) {
63 const acceleratorArn = yield* AcceleratorArn;
64 // The region must also be pinned at the call site: the yield-time
65 // snapshot is only a fallback — the calling fiber's ambient Region
66 // (the host Function's own region) wins over it.
67 return yield* withGaRegion(
68 op({ ...request, AcceleratorArn: acceleratorArn } as I),
69 );
70 });
71 });
72 });
73
74/**
75 * Build the impl Effect for a Global Accelerator operation scoped to an
76 * {@link EndpointGroup}: the deploy-time half grants `actions` on the bound
77 * endpoint group's ARN, and the runtime half injects the group's
78 * `EndpointGroupArn` into every request.
79 */
80export const makeGaEndpointGroupHttpBinding = <
81 I extends { EndpointGroupArn: string },
82 A,
83 E,
84 R,
85>(options: {
86 /** Fully-qualified binding tag, e.g. `AWS.GlobalAccelerator.AddEndpoints`. */
87 tag: string;
88 /** The distilled operation. */
89 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
90 /** IAM actions granted on the endpoint group ARN. */
91 actions: readonly string[];
92 /**
93 * Additional statements granted alongside the group-scoped one (e.g. the
94 * `ec2:Describe*` / `elasticloadbalancing:DescribeLoadBalancers` reads
95 * Global Accelerator performs on the caller's behalf when endpoints are
96 * added).
97 */
98 extraStatements?: readonly PolicyStatement[];
99}) =>
100 Effect.gen(function* () {
101 const op = yield* withGaRegion(options.operation);
102
103 return Effect.fn(function* (endpointGroup: EndpointGroup) {
104 const EndpointGroupArn = yield* endpointGroup.endpointGroupArn;
105 if (!globalThis.__ALCHEMY_RUNTIME__) {
106 const host = yield* Binding.Host;
107 if (isBindingHost(host)) {
108 yield* host.bind`Allow(${host}, ${options.tag}(${endpointGroup}))`({
109 policyStatements: [
110 {
111 Effect: "Allow",
112 Action: [...options.actions],
113 Resource: [endpointGroup.endpointGroupArn],
114 },
115 ...(options.extraStatements ?? []),
116 ],
117 });
118 }
119 }
120 return Effect.fn(`${options.tag}(${endpointGroup.LogicalId})`)(function* (
121 request: Omit<I, "EndpointGroupArn">,
122 ) {
123 const endpointGroupArn = yield* EndpointGroupArn;
124 // Call-site region pin — see makeGaAcceleratorHttpBinding above.
125 return yield* withGaRegion(
126 op({
127 ...request,
128 EndpointGroupArn: endpointGroupArn,
129 } as I),
130 );
131 });
132 });
133 });