1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { PolicyStatement } from "./Policy.ts";
5
6/**
7 * Shared HTTP scaffolding for the IAM audit/insight runtime bindings.
8 *
9 * IAM's runtime surface (policy simulation, credential reports, access
10 * advisor, account audit) targets *arbitrary* IAM entities discovered at
11 * runtime — e.g. a compliance Lambda iterating every role in the account —
12 * so every binding is account-level and grants its action(s) on
13 * `Resource: ["*"]` (where AWS scopes the action to an entity, the entity is
14 * chosen per request and unknowable at deploy time). The only variation
15 * between bindings is the distilled operation and the IAM action.
16 *
17 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
18 * a `Layer.effect(Cap, makeIamHttpBinding({ … }))`. Genuinely-different
19 * bindings (e.g. `GetAccessKeyLastUsed`, which injects the identifier of a
20 * bound canonical `AccessKey`) stay bespoke.
21 */
22export const makeIamHttpBinding = <I, A, E, R>(options: {
23 /**
24 * Short capability name used in the binding sid and runtime span, e.g.
25 * `"SimulateCustomPolicy"`.
26 */
27 capability: string;
28 /**
29 * IAM actions granted on `Resource: ["*"]` (the target entities are chosen
30 * per request at runtime and unknowable at deploy time).
31 */
32 iamActions: readonly string[];
33 /**
34 * The distilled operation implementing the capability.
35 */
36 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
37}) =>
38 Effect.gen(function* () {
39 const op = yield* options.operation;
40
41 return Effect.fn(function* () {
42 if (!globalThis.__ALCHEMY_RUNTIME__) {
43 const host = yield* Binding.Host;
44 if (isBindingHost(host)) {
45 const policyStatements: PolicyStatement[] = [
46 {
47 Effect: "Allow",
48 Action: [...options.iamActions],
49 // The target IAM entities are chosen per request at runtime.
50 Resource: ["*"],
51 },
52 ];
53 yield* host.bind`Allow(${host}, AWS.IAM.${options.capability}())`({
54 policyStatements,
55 });
56 }
57 }
58 return Effect.fn(`AWS.IAM.${options.capability}`)(function* (
59 request?: I,
60 ) {
61 return yield* op((request ?? {}) as I);
62 });
63 });
64 });