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 { DeliveryStream } from "./DeliveryStream.ts";
6
7/**
8 * Shared scaffolding for AWS Firehose HTTP bindings.
9 *
10 * NOT exported from `index.ts` — every near-identical `{Op}Http.ts` in this
11 * service is a thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of
12 * the builders below. Everything except the operation, the IAM action list,
13 * and the granted ARNs is boilerplate. Genuinely-different bindings (the
14 * batched `DeliveryStreamSink`) stay bespoke.
15 */
16
17/**
18 * Build the impl Effect for a delivery-stream-scoped operation
19 * (`PutRecord`, `PutRecordBatch`): the runtime callable injects the bound
20 * {@link DeliveryStream}'s physical name as `DeliveryStreamName` and the
21 * deploy-time half grants `actions` on the delivery stream's ARN.
22 */
23export const makeDeliveryStreamHttpBinding = <
24 I extends { DeliveryStreamName?: string },
25 A,
26 E,
27 R,
28>(options: {
29 /** Fully-qualified binding tag, e.g. `AWS.Firehose.PutRecord`. */
30 tag: string;
31 /** The distilled operation; `DeliveryStreamName` is injected. */
32 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
33 /** IAM actions granted on the delivery stream ARN. */
34 actions: readonly string[];
35}) =>
36 Effect.gen(function* () {
37 const op = yield* options.operation;
38
39 return Effect.fn(function* (deliveryStream: DeliveryStream) {
40 const DeliveryStreamName = yield* deliveryStream.deliveryStreamName;
41 if (!globalThis.__ALCHEMY_RUNTIME__) {
42 const host = yield* Binding.Host;
43 if (isBindingHost(host)) {
44 yield* host.bind`Allow(${host}, ${options.tag}(${deliveryStream}))`({
45 policyStatements: [
46 {
47 Effect: "Allow",
48 Action: [...options.actions],
49 Resource: [
50 Output.interpolate`${deliveryStream.deliveryStreamArn}`,
51 ],
52 },
53 ],
54 });
55 }
56 }
57 return Effect.fn(`${options.tag}(${deliveryStream.LogicalId})`)(
58 function* (request: Omit<I, "DeliveryStreamName">) {
59 return yield* op({
60 ...request,
61 DeliveryStreamName: yield* DeliveryStreamName,
62 } as I);
63 },
64 );
65 });
66 });
67
68/**
69 * Build the impl Effect for an account-level operation
70 * (`ListDeliveryStreams`): the runtime callable passes the caller's request
71 * through unchanged and the deploy-time half grants `actions` on `*`
72 * (these Firehose actions do not support resource-level permissions).
73 */
74export const makeFirehoseAccountHttpBinding = <I, A, E, R>(options: {
75 /** Fully-qualified binding tag, e.g. `AWS.Firehose.ListDeliveryStreams`. */
76 tag: string;
77 /** The distilled operation, invoked with the caller's request as-is. */
78 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
79 /** IAM actions granted on `*`. */
80 actions: readonly string[];
81}) =>
82 Effect.gen(function* () {
83 const op = yield* options.operation;
84
85 return Effect.fn(function* () {
86 if (!globalThis.__ALCHEMY_RUNTIME__) {
87 const host = yield* Binding.Host;
88 if (isBindingHost(host)) {
89 yield* host.bind`Allow(${host}, ${options.tag}())`({
90 policyStatements: [
91 {
92 Effect: "Allow",
93 Action: [...options.actions],
94 Resource: ["*"],
95 },
96 ],
97 });
98 }
99 }
100 return Effect.fn(options.tag)(function* (request?: I) {
101 return yield* op((request ?? {}) as I);
102 });
103 });
104 });