1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import { Region } from "../Region.ts";
5import type { EmailContact } from "./EmailContact.ts";
6
7/**
8 * Shared scaffolding for the AWS User Notifications Contacts HTTP bindings.
9 *
10 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
11 * a `Layer.effect(Cap, makeEmailContactHttpBinding({ … }))` over the builder
12 * below. Everything except the operation and the IAM action list is
13 * boilerplate.
14 *
15 * User Notifications Contacts is a global service managed from a single
16 * control-plane region (`us-east-1`) — like its sibling `notifications`
17 * service — so every operation is resolved with the Region pinned.
18 */
19const US_EAST_1 = "us-east-1";
20const pinContactsRegion = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
21 effect.pipe(Effect.provideService(Region, Effect.succeed(US_EAST_1)));
22
23/**
24 * Build the impl Effect for a User Notifications Contacts operation scoped
25 * to one {@link EmailContact}: the deploy-time half grants `actions` on the
26 * bound contact's ARN, and the runtime half injects the contact's ARN as
27 * the `arn` of every request.
28 */
29export const makeEmailContactHttpBinding = <
30 I extends { arn: string },
31 A,
32 E,
33 R,
34 Req = Omit<I, "arn">,
35>(options: {
36 /** Fully-qualified binding tag, e.g. `AWS.NotificationsContacts.SendActivationCode`. */
37 tag: string;
38 /** The distilled operation. */
39 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
40 /** IAM actions granted on the email contact ARN. */
41 actions: readonly string[];
42}) =>
43 Effect.gen(function* () {
44 const op = yield* pinContactsRegion(options.operation);
45
46 return Effect.fn(function* (contact: EmailContact) {
47 // Output yields a DEFERRED effect — resolve again per invocation below.
48 const ContactArn = yield* contact.emailContactArn;
49 if (!globalThis.__ALCHEMY_RUNTIME__) {
50 const host = yield* Binding.Host;
51 if (isBindingHost(host)) {
52 yield* host.bind`Allow(${host}, ${options.tag}(${contact}))`({
53 policyStatements: [
54 {
55 Effect: "Allow",
56 Action: [...options.actions],
57 Resource: [contact.emailContactArn],
58 },
59 ],
60 });
61 }
62 }
63 return Effect.fn(`${options.tag}(${contact.LogicalId})`)(function* (
64 request?: Req,
65 ) {
66 const arn = yield* ContactArn;
67 // The region must also be pinned at the call site: the yield-time
68 // snapshot is only a fallback — the calling fiber's ambient Region
69 // (the host Function's own region) wins over it.
70 return yield* pinContactsRegion(
71 op({ ...(request as object), arn } as I),
72 );
73 });
74 });
75 });