1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { ResponsePlan } from "./ResponsePlan.ts";
5
6/**
7 * Shared scaffolding for AWS Systems Manager Incident Manager HTTP bindings.
8 *
9 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
10 * a `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two builders
11 * below. Everything except the operation and the IAM action list is
12 * boilerplate.
13 */
14
15/**
16 * Build the impl Effect for an operation scoped to a {@link ResponsePlan}
17 * (`StartIncident`): the deploy-time half grants `actions` on the bound
18 * response plan's ARN and the runtime half injects the plan's
19 * `responsePlanArn` into every request.
20 */
21export const makeIncidentsResponsePlanHttpBinding = <
22 I extends { responsePlanArn: string },
23 A,
24 E,
25 R,
26>(options: {
27 /** Fully-qualified binding tag, e.g. `AWS.SSMIncidents.StartIncident`. */
28 tag: string;
29 /** The distilled operation; `responsePlanArn` is injected from the plan. */
30 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
31 /** IAM actions granted on the response plan ARN. */
32 actions: readonly string[];
33}) =>
34 Effect.gen(function* () {
35 const op = yield* options.operation;
36
37 return Effect.fn(function* (plan: ResponsePlan) {
38 const ResponsePlanArn = yield* plan.arn;
39 if (!globalThis.__ALCHEMY_RUNTIME__) {
40 const host = yield* Binding.Host;
41 if (isBindingHost(host)) {
42 yield* host.bind`Allow(${host}, ${options.tag}(${plan}))`({
43 policyStatements: [
44 {
45 Effect: "Allow",
46 Action: [...options.actions],
47 Resource: [plan.arn],
48 },
49 ],
50 });
51 }
52 }
53 return Effect.fn(`${options.tag}(${plan.LogicalId})`)(function* (
54 request?: Omit<I, "responsePlanArn">,
55 ) {
56 return yield* op({
57 ...request,
58 responsePlanArn: yield* ResponsePlanArn,
59 } as I);
60 });
61 });
62 });
63
64/**
65 * Build the impl Effect for an incident-record-plane operation (incident
66 * records, timeline events, related items, findings). Incident records are
67 * runtime entities — their ARNs embed the response-plan name plus a UUID
68 * minted when the incident starts — so there is no deployed resource to
69 * scope the grant to and the deploy-time half grants `actions` on `*`. The
70 * runtime callable passes the caller's request through unchanged.
71 */
72export const makeIncidentsAccountHttpBinding = <I, A, E, R>(options: {
73 /** Fully-qualified binding tag, e.g. `AWS.SSMIncidents.GetIncidentRecord`. */
74 tag: string;
75 /** The distilled operation, invoked with the caller's request as-is. */
76 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
77 /** IAM actions granted on `*`. */
78 actions: readonly string[];
79}) =>
80 Effect.gen(function* () {
81 const op = yield* options.operation;
82
83 return Effect.fn(function* () {
84 if (!globalThis.__ALCHEMY_RUNTIME__) {
85 const host = yield* Binding.Host;
86 if (isBindingHost(host)) {
87 yield* host.bind`Allow(${host}, ${options.tag}())`({
88 policyStatements: [
89 {
90 Effect: "Allow",
91 Action: [...options.actions],
92 Resource: ["*"],
93 },
94 ],
95 });
96 }
97 }
98 return Effect.fn(options.tag)(function* (request: I) {
99 return yield* op(request);
100 });
101 });
102 });