1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { EventDataStore } from "./EventDataStore.ts";
5
6/**
7 * Shared scaffolding for AWS CloudTrail HTTP bindings.
8 *
9 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
10 * a `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two builders
11 * below. Everything except the operation, the IAM action list, and (for
12 * Lake operations) the injected event data store is boilerplate.
13 */
14
15/**
16 * Build the impl Effect for a CloudTrail Lake operation scoped to an
17 * {@link EventDataStore}: the deploy-time half grants `actions` on the bound
18 * store's ARN. Query operations are keyed by `QueryId` and need no request
19 * injection; list/generate operations reference the store in the request via
20 * `injectEventDataStore` / `injectEventDataStores`.
21 */
22export const makeCloudTrailEventDataStoreHttpBinding = <I, A, E, R>(options: {
23 /** Fully-qualified binding tag, e.g. `AWS.CloudTrail.GetQueryResults`. */
24 tag: string;
25 /** The distilled operation. */
26 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
27 /** IAM actions granted on the event data store ARN. */
28 actions: readonly string[];
29 /** Inject the bound store's ARN as the request's `EventDataStore` field. */
30 injectEventDataStore?: boolean;
31 /** Inject the bound store's ARN as the request's `EventDataStores` list. */
32 injectEventDataStores?: boolean;
33}) =>
34 Effect.gen(function* () {
35 const op = yield* options.operation;
36
37 return Effect.fn(function* (store: EventDataStore) {
38 const Arn = yield* store.eventDataStoreArn;
39 if (!globalThis.__ALCHEMY_RUNTIME__) {
40 const host = yield* Binding.Host;
41 if (isBindingHost(host)) {
42 yield* host.bind`Allow(${host}, ${options.tag}(${store}))`({
43 policyStatements: [
44 {
45 Effect: "Allow",
46 Action: [...options.actions],
47 Resource: [store.eventDataStoreArn],
48 },
49 ],
50 });
51 }
52 }
53 return Effect.fn(`${options.tag}(${store.LogicalId})`)(function* (
54 request?: Omit<I, "EventDataStore" | "EventDataStores">,
55 ) {
56 const arn = yield* Arn;
57 return yield* op({
58 ...request,
59 ...(options.injectEventDataStore ? { EventDataStore: arn } : {}),
60 ...(options.injectEventDataStores ? { EventDataStores: [arn] } : {}),
61 } as I);
62 });
63 });
64 });
65
66/**
67 * Build the impl Effect for an account-level CloudTrail operation (event
68 * history lookup, digest public keys, Insights metrics). The deploy-time
69 * half grants `actions` on `*` — these CloudTrail actions are not
70 * resource-scoped.
71 */
72export const makeCloudTrailAccountHttpBinding = <I, A, E, R>(options: {
73 /** Fully-qualified binding tag, e.g. `AWS.CloudTrail.LookupEvents`. */
74 tag: string;
75 /** The distilled operation, invoked with the caller's request as-is. */
76 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
77 /** IAM actions granted on `*`. */
78 actions: readonly string[];
79}) =>
80 Effect.gen(function* () {
81 const op = yield* options.operation;
82
83 return Effect.fn(function* () {
84 if (!globalThis.__ALCHEMY_RUNTIME__) {
85 const host = yield* Binding.Host;
86 if (isBindingHost(host)) {
87 yield* host.bind`Allow(${host}, ${options.tag}())`({
88 policyStatements: [
89 {
90 Effect: "Allow",
91 Action: [...options.actions],
92 Resource: ["*"],
93 },
94 ],
95 });
96 }
97 }
98 return Effect.fn(options.tag)(function* (request?: I) {
99 return yield* op((request ?? {}) as I);
100 });
101 });
102 });