1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4
5/**
6 * Shared scaffolding for AWS CloudHSM (v2) HTTP bindings.
7 *
8 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
9 * thin `Layer.effect(Cap, makeCloudHsmHttpBinding({ … }))` over the builder
10 * below. Everything except the operation and the IAM action list is
11 * boilerplate.
12 *
13 * All CloudHSM bindings are account-level: the identifiers the operations
14 * act on (cluster ids, backup ids, backup ARNs) are runtime data produced by
15 * the service itself — backups are auto-created, ids are auto-assigned and
16 * the API exposes no resource ARNs on clusters — so the deploy-time half
17 * grants `actions` on `*`.
18 */
19export const makeCloudHsmHttpBinding = <I, A, E, R>(options: {
20 /** Fully-qualified binding tag, e.g. `AWS.CloudHSMV2.DescribeClusters`. */
21 tag: string;
22 /** The distilled operation, invoked with the caller's request as-is. */
23 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
24 /** IAM actions granted on `*`. */
25 actions: readonly string[];
26}) =>
27 Effect.gen(function* () {
28 const op = yield* options.operation;
29
30 return Effect.fn(function* () {
31 if (!globalThis.__ALCHEMY_RUNTIME__) {
32 const host = yield* Binding.Host;
33 if (isBindingHost(host)) {
34 yield* host.bind`Allow(${host}, ${options.tag}())`({
35 policyStatements: [
36 {
37 Effect: "Allow",
38 Action: [...options.actions],
39 Resource: ["*"],
40 },
41 ],
42 });
43 }
44 }
45 return Effect.fn(options.tag)(function* (request?: I) {
46 return yield* op((request ?? {}) as I);
47 });
48 });
49 });