1import { Region as AwsRegion } from "@distilled.cloud/aws/Region";
2import * as Effect from "effect/Effect";
3import * as Binding from "../../Binding.ts";
4import { isBindingHost } from "../Lambda/Function.ts";
5import { KVS_REGION, withKvsRegion } from "./common.ts";
6import type { Distribution } from "./Distribution.ts";
7import type { KeyValueStore } from "./KeyValueStore.ts";
8
9/**
10 * Shared scaffolding for CloudFront 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, the IAM action list, and the
15 * injected identifier is boilerplate.
16 */
17
18/**
19 * Build the impl Effect for a distribution-scoped operation. The runtime
20 * callable injects the bound {@link Distribution}'s id as the request's
21 * `DistributionId`; the deploy-time half grants `actions` on the
22 * distribution's ARN.
23 */
24export const makeDistributionScopedHttpBinding = <
25 I extends { DistributionId: string },
26 A,
27 E,
28 R,
29>(options: {
30 /** Fully-qualified binding tag, e.g. `AWS.CloudFront.GetInvalidation`. */
31 tag: string;
32 /** The distilled operation; `DistributionId` is injected from the resource. */
33 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
34 /** IAM actions granted on the distribution ARN. */
35 actions: readonly string[];
36}) =>
37 Effect.gen(function* () {
38 const op = yield* options.operation;
39
40 return Effect.fn(function* (distribution: Distribution) {
41 const DistributionId = yield* distribution.distributionId;
42 if (!globalThis.__ALCHEMY_RUNTIME__) {
43 const host = yield* Binding.Host;
44 if (isBindingHost(host)) {
45 yield* host.bind`Allow(${host}, ${options.tag}(${distribution}))`({
46 policyStatements: [
47 {
48 Effect: "Allow",
49 Action: [...options.actions],
50 Resource: [distribution.distributionArn],
51 },
52 ],
53 });
54 }
55 }
56 return Effect.fn(`${options.tag}(${distribution.LogicalId})`)(function* (
57 request: Omit<I, "DistributionId">,
58 ) {
59 return yield* op({
60 ...request,
61 DistributionId: yield* DistributionId,
62 } as I);
63 });
64 });
65 });
66
67/**
68 * Build the impl Effect for a KeyValueStore data-plane operation
69 * (`cloudfront-keyvaluestore`). The runtime callable injects the bound
70 * {@link KeyValueStore}'s ARN as the request's `KvsARN`; the deploy-time half
71 * grants `actions` on the store's ARN. The data-plane endpoint is global
72 * (derived from the ARN), but distilled signs with plain SigV4 against the
73 * context region and the service only accepts `us-east-1` signatures — so
74 * the operation is resolved with the Region pinned to {@link KVS_REGION},
75 * exactly like the resource providers in `common.ts`.
76 */
77export const makeKeyValueStoreScopedHttpBinding = <
78 I extends { KvsARN: string },
79 A,
80 E,
81 R,
82>(options: {
83 /** Fully-qualified binding tag, e.g. `AWS.CloudFront.GetKey`. */
84 tag: string;
85 /** The distilled operation; `KvsARN` is injected from the resource. */
86 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
87 /** IAM actions granted on the KeyValueStore ARN. */
88 actions: readonly string[];
89}) =>
90 Effect.gen(function* () {
91 // The distilled Region service value is `Effect<RegionName>`, not a raw
92 // string (see `withKvsRegion` in common.ts).
93 const op = yield* options.operation.pipe(
94 Effect.provideService(AwsRegion, Effect.succeed(KVS_REGION)),
95 );
96
97 return Effect.fn(function* (store: KeyValueStore) {
98 const KvsARN = yield* store.keyValueStoreArn;
99 if (!globalThis.__ALCHEMY_RUNTIME__) {
100 const host = yield* Binding.Host;
101 if (isBindingHost(host)) {
102 yield* host.bind`Allow(${host}, ${options.tag}(${store}))`({
103 policyStatements: [
104 {
105 Effect: "Allow",
106 Action: [...options.actions],
107 Resource: [store.keyValueStoreArn],
108 },
109 ],
110 });
111 }
112 }
113 return Effect.fn(`${options.tag}(${store.LogicalId})`)(function* (
114 request: Omit<I, "KvsARN">,
115 ) {
116 // The region must also be pinned at the call site: the yield-time
117 // snapshot is only a fallback — the calling fiber's ambient Region
118 // (the host Function's own region) wins over it.
119 return yield* withKvsRegion(
120 op({
121 ...request,
122 KvsARN: yield* KvsARN,
123 } as I),
124 );
125 });
126 });
127 });