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 OpenSearch Service HTTP bindings.
7 *
8 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
9 * thin `Layer.effect(Cap, makeOpenSearchHttpBinding({ … }))` over the builder
10 * below. Everything except the operation and the IAM action list is
11 * boilerplate.
12 *
13 * The bindings are account-level (the runtime request carries the
14 * `DomainName`) rather than domain-scoped: domain names are frequently
15 * runtime data — an operator Lambda watches every domain in the account, not
16 * one resource-bound domain — and OpenSearch domains take 15-25 minutes to
17 * provision, so a deploy-time resource resolution would gate every consumer
18 * on a live domain.
19 */
20
21/**
22 * Build the impl Effect for an OpenSearch configuration-API operation. The
23 * deploy-time half grants `actions` on `resources` (default `*` — domain
24 * names addressed at runtime are data, so the grant cannot be narrowed to a
25 * deploy-time ARN).
26 */
27export const makeOpenSearchHttpBinding = <I, A, E, R>(options: {
28 /** Fully-qualified binding tag, e.g. `AWS.OpenSearch.DescribeDomain`. */
29 tag: string;
30 /** The distilled operation, invoked with the caller's request as-is. */
31 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
32 /** IAM actions granted. */
33 actions: readonly string[];
34 /**
35 * IAM resources the actions are granted on.
36 * @default ["*"]
37 */
38 resources?: readonly string[];
39}) =>
40 Effect.gen(function* () {
41 const op = yield* options.operation;
42
43 return Effect.fn(function* () {
44 if (!globalThis.__ALCHEMY_RUNTIME__) {
45 const host = yield* Binding.Host;
46 if (isBindingHost(host)) {
47 yield* host.bind`Allow(${host}, ${options.tag}())`({
48 policyStatements: [
49 {
50 Effect: "Allow",
51 Action: [...options.actions],
52 Resource: [...(options.resources ?? ["*"])],
53 },
54 ],
55 });
56 }
57 }
58 return Effect.fn(options.tag)(function* (request?: I) {
59 return yield* op((request ?? {}) as I);
60 });
61 });
62 });