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 { Region } from "../Region.ts";
6import type { ReportDefinition } from "./ReportDefinition.ts";
7
8/**
9 * Shared HTTP scaffolding for the AWS Cost and Usage Report (`cur`) runtime
10 * bindings.
11 *
12 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
13 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the builders
14 * below.
15 *
16 * The CUR control plane is a global service hosted only in `us-east-1`, so
17 * every operation is resolved with the distilled Region pinned there —
18 * exactly like the {@link ReportDefinition} provider. The distilled Region
19 * service value is `Effect<RegionName>`, not a raw string, hence
20 * `Effect.succeed`.
21 */
22const CUR_REGION = "us-east-1" as const;
23
24const pinCur = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
25 effect.pipe(Effect.provideService(Region, Effect.succeed(CUR_REGION)));
26
27/**
28 * Build the impl Effect for an account-level CUR operation.
29 * `cur:DescribeReportDefinitions` enumerates every report definition in the
30 * account, so the grant is on `Resource: ["*"]`.
31 */
32export const makeCurHttpBinding = <I extends object, A, E, R>(options: {
33 /**
34 * Short capability name used in the binding sid and runtime span, e.g.
35 * `"DescribeReportDefinitions"`.
36 */
37 capability: string;
38 /** IAM actions granted on `Resource: ["*"]`. */
39 iamActions: readonly string[];
40 /** The distilled operation implementing the capability. */
41 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
42}) =>
43 Effect.gen(function* () {
44 const op = yield* pinCur(options.operation);
45
46 return Effect.fn(function* () {
47 if (!globalThis.__ALCHEMY_RUNTIME__) {
48 const host = yield* Binding.Host;
49 if (isBindingHost(host)) {
50 const policyStatements: PolicyStatement[] = [
51 {
52 Effect: "Allow",
53 Action: [...options.iamActions],
54 Resource: ["*"],
55 },
56 ];
57 yield* host.bind`Allow(${host}, AWS.CostAndUsageReport.${options.capability}())`(
58 {
59 policyStatements,
60 },
61 );
62 }
63 }
64 return Effect.fn(`AWS.CostAndUsageReport.${options.capability}`)(
65 function* (request?: I) {
66 // The region must also be pinned at the call site: the yield-time
67 // snapshot is only a fallback — the calling fiber's ambient Region
68 // (the host Function's own region) wins over it.
69 return yield* pinCur(op((request ?? {}) as I));
70 },
71 );
72 });
73 });
74
75/**
76 * Build the impl Effect for an operation scoped to one
77 * {@link ReportDefinition}. The runtime callable injects the bound report's
78 * name as the request's `ReportName`; the deploy-time half grants
79 * `iamActions` on the report definition's ARN
80 * (`arn:aws:cur:us-east-1:{account}:definition/{name}`).
81 */
82export const makeReportDefinitionHttpBinding = <
83 I extends { ReportName?: string },
84 A,
85 E,
86 R,
87>(options: {
88 /**
89 * Short capability name used in the binding sid and runtime span, e.g.
90 * `"ListTagsForResource"`.
91 */
92 capability: string;
93 /** IAM actions granted on the report definition's ARN. */
94 iamActions: readonly string[];
95 /** The distilled operation; `ReportName` is injected. */
96 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
97}) =>
98 Effect.gen(function* () {
99 const op = yield* pinCur(options.operation);
100
101 return Effect.fn(function* (report: ReportDefinition) {
102 const ReportName = yield* report.reportName;
103 if (!globalThis.__ALCHEMY_RUNTIME__) {
104 const host = yield* Binding.Host;
105 if (isBindingHost(host)) {
106 yield* host.bind`Allow(${host}, AWS.CostAndUsageReport.${options.capability}(${report}))`(
107 {
108 policyStatements: [
109 {
110 Effect: "Allow",
111 Action: [...options.iamActions],
112 Resource: [report.reportArn],
113 },
114 ],
115 },
116 );
117 }
118 }
119 return Effect.fn(
120 `AWS.CostAndUsageReport.${options.capability}(${report.LogicalId})`,
121 )(function* (request?: Omit<I, "ReportName">) {
122 // Call-site region pin — see makeCurHttpBinding above.
123 return yield* pinCur(
124 op({
125 ...request,
126 ReportName: yield* ReportName,
127 } as I),
128 );
129 });
130 });
131 });