1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { Channel } from "./Channel.ts";
5
6/**
7 * Shared scaffolding for Amazon IVS stream data-plane HTTP bindings.
8 *
9 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service
10 * is a `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two
11 * builders below. Everything except the operation and the IAM action list
12 * is boilerplate: channel-scoped bindings inject the bound channel's ARN
13 * as the request's `channelArn` and grant `actions` on that ARN;
14 * account-scoped bindings pass the request through and grant `actions` on
15 * `*`.
16 */
17
18/**
19 * Build the impl Effect for an IVS operation scoped to a {@link Channel}:
20 * the deploy-time half grants `actions` on the bound channel's ARN, and
21 * the runtime half injects the channel's ARN into every request as
22 * `channelArn`.
23 */
24export const makeIvsChannelHttpBinding = <
25 I extends { channelArn: string },
26 A,
27 E,
28 R,
29>(options: {
30 /** Fully-qualified binding tag, e.g. `AWS.IVS.GetStream`. */
31 tag: string;
32 /** The distilled operation; `channelArn` is injected from the channel. */
33 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
34 /** IAM actions granted on the channel ARN. */
35 actions: readonly string[];
36}) =>
37 Effect.gen(function* () {
38 const op = yield* options.operation;
39
40 return Effect.fn(function* (channel: Channel) {
41 const ChannelArn = yield* channel.channelArn;
42 if (!globalThis.__ALCHEMY_RUNTIME__) {
43 const host = yield* Binding.Host;
44 if (isBindingHost(host)) {
45 yield* host.bind`Allow(${host}, ${options.tag}(${channel}))`({
46 policyStatements: [
47 {
48 Effect: "Allow",
49 Action: [...options.actions],
50 Resource: [channel.channelArn],
51 },
52 ],
53 });
54 }
55 }
56 return Effect.fn(`${options.tag}(${channel.LogicalId})`)(function* (
57 request?: Omit<I, "channelArn">,
58 ) {
59 const channelArn = yield* ChannelArn;
60 return yield* op({ ...request, channelArn } as I);
61 });
62 });
63 });
64
65/**
66 * Build the impl Effect for an account-level IVS operation (e.g.
67 * enumerating the account's live streams). The deploy-time half grants
68 * `actions` on `*` — these list actions are not scoped to a single
69 * channel resource.
70 */
71export const makeIvsAccountHttpBinding = <I, A, E, R>(options: {
72 /** Fully-qualified binding tag, e.g. `AWS.IVS.ListStreams`. */
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 });