1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import * as Output from "../../Output.ts";
4import { isBindingHost } from "../Lambda/Function.ts";
5import type { Collection } from "./Collection.ts";
6
7/**
8 * Shared scaffolding for the OpenSearch Serverless runtime bindings.
9 *
10 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
11 * thin `Layer.effect(Cap, makeAoss…HttpBinding({ … }))` over one of the two
12 * builders below. Everything except the operation, the IAM action list, and
13 * (for collection-scoped operations) the injected collection `id` is
14 * boilerplate.
15 */
16
17/**
18 * Build the impl Effect for a collection-scoped operation: the runtime
19 * callable injects the bound {@link Collection}'s ID as `id` and the
20 * deploy-time half grants `actions` on the collection's ARN.
21 */
22export const makeAossCollectionHttpBinding = <
23 I extends { id?: string },
24 A,
25 E,
26 R,
27>(options: {
28 /** Fully-qualified binding tag, e.g. `AWS.OpenSearchServerless.CreateIndex`. */
29 tag: string;
30 /** The distilled operation; `id` is injected from the collection. */
31 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
32 /** IAM actions granted on the collection ARN. */
33 actions: readonly string[];
34}) =>
35 Effect.gen(function* () {
36 const op = yield* options.operation;
37
38 return Effect.fn(function* (collection: Collection) {
39 const CollectionId = yield* collection.collectionId;
40 if (!globalThis.__ALCHEMY_RUNTIME__) {
41 const host = yield* Binding.Host;
42 if (isBindingHost(host)) {
43 yield* host.bind`Allow(${host}, ${options.tag}(${collection}))`({
44 policyStatements: [
45 {
46 Effect: "Allow",
47 Action: [...options.actions],
48 Resource: [Output.interpolate`${collection.collectionArn}`],
49 },
50 ],
51 });
52 }
53 }
54 return Effect.fn(`${options.tag}(${collection.LogicalId})`)(function* (
55 request: Omit<I, "id">,
56 ) {
57 return yield* op({
58 ...request,
59 id: yield* CollectionId,
60 } as I);
61 });
62 });
63 });
64
65/**
66 * Build the impl Effect for an account-level operation (no collection
67 * argument): the deploy-time half grants `actions` on `*` — the account
68 * settings, policy stats, and lifecycle-policy read actions do not support
69 * resource-level scoping.
70 */
71export const makeAossAccountHttpBinding = <I, A, E, R>(options: {
72 /** Fully-qualified binding tag, e.g. `AWS.OpenSearchServerless.GetAccountSettings`. */
73 tag: string;
74 /** The distilled operation, invoked with the caller's request as-is. */
75 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
76 /** IAM actions granted on `*`. */
77 actions: readonly string[];
78}) =>
79 Effect.gen(function* () {
80 const op = yield* options.operation;
81
82 return Effect.fn(function* () {
83 if (!globalThis.__ALCHEMY_RUNTIME__) {
84 const host = yield* Binding.Host;
85 if (isBindingHost(host)) {
86 yield* host.bind`Allow(${host}, ${options.tag}())`({
87 policyStatements: [
88 {
89 Effect: "Allow",
90 Action: [...options.actions],
91 Resource: ["*"],
92 },
93 ],
94 });
95 }
96 }
97 return Effect.fn(options.tag)(function* (request?: I) {
98 return yield* op((request ?? {}) as I);
99 });
100 });
101 });