1import type { Credentials } from "@distilled.cloud/aws/Credentials";
2import type { Region } from "@distilled.cloud/aws/Region";
3import * as Effect from "effect/Effect";
4import type * as HttpClient from "effect/http/HttpClient";
5import * as Binding from "../../Binding.ts";
6import * as Output from "../../Output.ts";
7import { isBindingHost } from "../Lambda/Function.ts";
8import type { Broker } from "./Broker.ts";
9
10/**
11 * Shared HTTP scaffolding for the Amazon MQ runtime bindings.
12 *
13 * Every MQ capability follows the same shape — resolve the distilled
14 * operation, register an IAM policy statement on the binding host, and return
15 * a runtime callable that injects the broker id. The only variation is the
16 * operation, the IAM action(s), and whether the binding is scoped to one
17 * broker or to the whole account, so those are the only inputs.
18 *
19 * @internal — not exported from `index.ts`.
20 */
21
22type MqRequirements = Credentials | Region | HttpClient.HttpClient;
23
24export interface MqBrokerHttpBindingConfig<Req extends object, Out, Err> {
25 /**
26 * Short capability name used in the binding sid and runtime span, e.g.
27 * `"DescribeBroker"`.
28 */
29 capability: string;
30 /**
31 * IAM actions granted to the binding host on the broker ARN, e.g.
32 * `["mq:DescribeBroker"]`.
33 */
34 iamActions: readonly string[];
35 /**
36 * The distilled MQ operation implementing the capability.
37 */
38 operation: Effect.Effect<
39 (input: Req & { BrokerId: string }) => Effect.Effect<Out, Err>,
40 never,
41 MqRequirements
42 >;
43}
44
45/**
46 * Build the implementation effect for a broker-scoped MQ capability:
47 * `Layer.effect(Cap, makeMqBrokerHttpBinding({ ... }))`.
48 *
49 * The runtime callable injects the bound broker's id, so `Req` is the
50 * operation's request type without `BrokerId`.
51 */
52export const makeMqBrokerHttpBinding = <Req extends object, Out, Err>(
53 config: MqBrokerHttpBindingConfig<Req, Out, Err>,
54) =>
55 Effect.gen(function* () {
56 const op = yield* config.operation;
57
58 return Effect.fn(function* (broker: Broker) {
59 const BrokerId = yield* broker.brokerId;
60 if (!globalThis.__ALCHEMY_RUNTIME__) {
61 const host = yield* Binding.Host;
62 if (isBindingHost(host)) {
63 yield* host.bind`Allow(${host}, AWS.MQ.${config.capability}(${broker}))`(
64 {
65 policyStatements: [
66 {
67 Effect: "Allow",
68 Action: [...config.iamActions],
69 Resource: [Output.interpolate`${broker.brokerArn}`],
70 },
71 ],
72 },
73 );
74 }
75 }
76 return Effect.fn(`AWS.MQ.${config.capability}(${broker.LogicalId})`)(
77 function* (request?: Req) {
78 return yield* op({
79 ...(request ?? ({} as Req)),
80 BrokerId: yield* BrokerId,
81 });
82 },
83 );
84 });
85 });
86
87export interface MqAccountHttpBindingConfig<Req extends object, Out, Err> {
88 /**
89 * Short capability name used in the binding sid and runtime span, e.g.
90 * `"ListBrokers"`.
91 */
92 capability: string;
93 /**
94 * IAM actions granted to the binding host on `Resource: ["*"]` (MQ
95 * account-level operations are not resource-scoped).
96 */
97 iamActions: readonly string[];
98 /**
99 * The distilled MQ operation implementing the capability.
100 */
101 operation: Effect.Effect<
102 (input: Req) => Effect.Effect<Out, Err>,
103 never,
104 MqRequirements
105 >;
106}
107
108/**
109 * Build the implementation effect for an account-level MQ capability (no
110 * broker argument): `Layer.effect(Cap, makeMqAccountHttpBinding({ ... }))`.
111 */
112export const makeMqAccountHttpBinding = <Req extends object, Out, Err>(
113 config: MqAccountHttpBindingConfig<Req, Out, Err>,
114) =>
115 Effect.gen(function* () {
116 const op = yield* config.operation;
117
118 return Effect.fn(function* () {
119 if (!globalThis.__ALCHEMY_RUNTIME__) {
120 const host = yield* Binding.Host;
121 if (isBindingHost(host)) {
122 yield* host.bind`Allow(${host}, AWS.MQ.${config.capability}())`({
123 policyStatements: [
124 {
125 Effect: "Allow",
126 Action: [...config.iamActions],
127 Resource: ["*"],
128 },
129 ],
130 });
131 }
132 }
133 return Effect.fn(`AWS.MQ.${config.capability}`)(function* (
134 request?: Req,
135 ) {
136 return yield* op(request ?? ({} as Req));
137 });
138 });
139 });