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 CloudWatch Observability Admin runtime
7 * bindings.
8 *
9 * Every account-level Observability Admin read (`ListResourceTelemetry`,
10 * `GetTelemetryEvaluationStatus`, ...) targets account configuration rather
11 * than a discrete ARN, so each binding grants its action on
12 * `Resource: ["*"]`. The only variation between bindings is the distilled
13 * operation and the IAM action.
14 *
15 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
16 * a `Layer.effect(Cap, makeObservabilityAdminHttpBinding({ … }))`.
17 */
18export const makeObservabilityAdminHttpBinding = <I, A, E, R>(options: {
19 /**
20 * Short capability name used in the binding sid and runtime span, e.g.
21 * `"ListResourceTelemetry"`.
22 */
23 capability: string;
24 /**
25 * IAM actions granted on `Resource: ["*"]` (account-level configuration
26 * reads have no resource ARN to scope to).
27 */
28 iamActions: readonly string[];
29 /**
30 * The distilled operation implementing the capability.
31 */
32 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
33}) =>
34 Effect.gen(function* () {
35 const op = yield* options.operation;
36
37 return Effect.fn(function* () {
38 if (!globalThis.__ALCHEMY_RUNTIME__) {
39 const host = yield* Binding.Host;
40 if (isBindingHost(host)) {
41 yield* host.bind`Allow(${host}, AWS.ObservabilityAdmin.${options.capability}())`(
42 {
43 policyStatements: [
44 {
45 Effect: "Allow",
46 Action: [...options.iamActions],
47 // Account-level configuration reads have no resource ARN.
48 Resource: ["*"],
49 },
50 ],
51 },
52 );
53 }
54 }
55 return Effect.fn(`AWS.ObservabilityAdmin.${options.capability}`)(
56 function* (request?: I) {
57 return yield* op(request ?? ({} as I));
58 },
59 );
60 });
61 });