1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { PolicyStatement } from "../IAM/Policy.ts";
5
6/**
7 * Shared HTTP scaffolding for the AWS Organizations runtime bindings.
8 *
9 * Organizations is a management-account-scoped global service: every runtime
10 * operation targets the caller's organization or entities within it (roots,
11 * OUs, accounts, policies, handshakes) that are chosen per request at
12 * runtime, so every binding is account-level and grants its action(s) on
13 * `Resource: ["*"]`.
14 *
15 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
16 * a `Layer.effect(Cap, makeOrganizationsHttpBinding({ … }))` over the builder
17 * below. Everything except the operation and the IAM action list is
18 * boilerplate.
19 */
20export const makeOrganizationsHttpBinding = <I, A, E, R>(options: {
21 /**
22 * Short capability name used in the binding sid and runtime span, e.g.
23 * `"ListAccounts"`.
24 */
25 capability: string;
26 /**
27 * IAM actions granted on `Resource: ["*"]` (the target organization
28 * entities are chosen per request at runtime and unknowable at deploy
29 * time).
30 */
31 iamActions: readonly string[];
32 /**
33 * The distilled operation implementing the capability.
34 */
35 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
36}) =>
37 Effect.gen(function* () {
38 const op = yield* options.operation;
39
40 return Effect.fn(function* () {
41 if (!globalThis.__ALCHEMY_RUNTIME__) {
42 const host = yield* Binding.Host;
43 if (isBindingHost(host)) {
44 const policyStatements: PolicyStatement[] = [
45 {
46 Effect: "Allow",
47 Action: [...options.iamActions],
48 // The target organization entities are chosen per request at
49 // runtime.
50 Resource: ["*"],
51 },
52 ];
53 yield* host.bind`Allow(${host}, AWS.Organizations.${options.capability}())`(
54 {
55 policyStatements,
56 },
57 );
58 }
59 }
60 return Effect.fn(`AWS.Organizations.${options.capability}`)(function* (
61 request?: I,
62 ) {
63 return yield* op((request ?? {}) as I);
64 });
65 });
66 });