1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { AppMonitor } from "./AppMonitor.ts";
5
6/**
7 * Shared scaffolding for CloudWatch RUM HTTP bindings.
8 *
9 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
10 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over the builder below
11 * (or reuses {@link bindRumAppMonitorPolicy} when the runtime callable needs
12 * bespoke request shaping, like `PutRumEvents` injecting the monitor id and
13 * details). Everything except the operation and the IAM action list is
14 * boilerplate.
15 */
16
17/**
18 * Deploy-time half shared by every app-monitor-scoped RUM binding: resolve
19 * the hosting Function and grant `actions` on the monitor's ARN. A no-op
20 * inside the deployed runtime.
21 */
22export const bindRumAppMonitorPolicy = (options: {
23 tag: string;
24 monitor: AppMonitor;
25 actions: readonly string[];
26}) =>
27 Effect.gen(function* () {
28 if (!globalThis.__ALCHEMY_RUNTIME__) {
29 const host = yield* Binding.Host;
30 if (isBindingHost(host)) {
31 yield* host.bind`Allow(${host}, ${options.tag}(${options.monitor}))`({
32 policyStatements: [
33 {
34 Effect: "Allow",
35 Action: [...options.actions],
36 Resource: [options.monitor.appMonitorArn],
37 },
38 ],
39 });
40 }
41 }
42 });
43
44/**
45 * Build the impl Effect for an operation scoped to a single bound
46 * {@link AppMonitor} and addressed by monitor name. The runtime callable
47 * injects the monitor's name as the request's `Name`; the deploy-time half
48 * grants `actions` on the monitor ARN.
49 */
50export const makeRumAppMonitorHttpBinding = <
51 I extends { Name: string },
52 A,
53 E,
54 R,
55>(options: {
56 /** Fully-qualified binding tag, e.g. `AWS.RUM.GetAppMonitorData`. */
57 tag: string;
58 /** The distilled operation; `Name` is injected from the monitor. */
59 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
60 /** IAM actions granted on the monitor ARN. */
61 actions: readonly string[];
62}) =>
63 Effect.gen(function* () {
64 const op = yield* options.operation;
65
66 return Effect.fn(function* (monitor: AppMonitor) {
67 const Name = yield* monitor.appMonitorName;
68 yield* bindRumAppMonitorPolicy({
69 tag: options.tag,
70 monitor,
71 actions: options.actions,
72 });
73 return Effect.fn(`${options.tag}(${monitor.LogicalId})`)(function* (
74 request: Omit<I, "Name">,
75 ) {
76 return yield* op({ ...request, Name: yield* Name } as I);
77 });
78 });
79 });