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