1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4
5/**
6 * Shared HTTP scaffolding for the Polly runtime bindings.
7 *
8 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
9 * thin `Layer.effect(Cap, makePollyHttpBinding({ … }))` over the builder
10 * below. Everything except the operation and the IAM action is boilerplate.
11 *
12 * Per the `polly` service authorization reference, only the lexicon
13 * management actions (`GetLexicon`, `PutLexicon`, `DeleteLexicon`) support
14 * resource-level IAM (the `lexicon` resource type); the synthesis, task,
15 * and describe actions authorize on `Resource: ["*"]`, so the account-level
16 * builder grants on `*`.
17 */
18export const makePollyHttpBinding = <I extends object, A, E, R>(options: {
19 /**
20 * Short capability name used in the binding sid and runtime span, e.g.
21 * `"SynthesizeSpeech"`.
22 */
23 capability: string;
24 /** IAM actions granted on `Resource: ["*"]`. */
25 iamActions: readonly string[];
26 /** The distilled operation implementing the capability. */
27 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
28}) =>
29 Effect.gen(function* () {
30 const op = yield* options.operation;
31
32 return Effect.fn(function* () {
33 if (!globalThis.__ALCHEMY_RUNTIME__) {
34 const host = yield* Binding.Host;
35 if (isBindingHost(host)) {
36 yield* host.bind`Allow(${host}, AWS.Polly.${options.capability}())`({
37 policyStatements: [
38 {
39 Effect: "Allow",
40 Action: [...options.iamActions],
41 Resource: ["*"],
42 },
43 ],
44 });
45 }
46 }
47 return Effect.fn(`AWS.Polly.${options.capability}`)(function* (
48 request?: I,
49 ) {
50 return yield* op((request ?? {}) as I);
51 });
52 });
53 });