1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import type { PolicyStatement } from "../IAM/Policy.ts";
4import { isBindingHost } from "../Lambda/Function.ts";
5import type { Service } from "./Service.ts";
6
7/**
8 * Shared scaffolding for AWS Cloud Map 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 three
12 * builders below. Everything except the operation, the IAM action list, and
13 * the injected identifier is boilerplate.
14 */
15
16/**
17 * Build the impl Effect for a service-scoped control/data operation. The
18 * runtime callable injects the bound {@link Service}'s id as the request's
19 * `ServiceId`; the deploy-time half grants `actions` on the service ARN
20 * plus any `extraStatements` (e.g. the Route 53 record management Cloud Map
21 * performs on the caller's behalf for DNS services).
22 */
23export const makeCloudMapServiceHttpBinding = <
24 I extends { ServiceId: string },
25 A,
26 E,
27 R,
28>(options: {
29 /** Fully-qualified binding tag, e.g. `AWS.CloudMap.GetInstance`. */
30 tag: string;
31 /** The distilled operation; `ServiceId` is injected from the service. */
32 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
33 /** IAM actions granted on the service ARN. */
34 actions: readonly string[];
35 /** Additional statements granted alongside the service-scoped one. */
36 extraStatements?: readonly PolicyStatement[];
37}) =>
38 Effect.gen(function* () {
39 const op = yield* options.operation;
40
41 return Effect.fn(function* (service: Service) {
42 const ServiceId = yield* service.serviceId;
43 if (!globalThis.__ALCHEMY_RUNTIME__) {
44 const host = yield* Binding.Host;
45 if (isBindingHost(host)) {
46 yield* host.bind`Allow(${host}, ${options.tag}(${service}))`({
47 policyStatements: [
48 {
49 Effect: "Allow",
50 Action: [...options.actions],
51 Resource: [service.serviceArn],
52 },
53 ...(options.extraStatements ?? []),
54 ],
55 });
56 }
57 }
58 return Effect.fn(`${options.tag}(${service.LogicalId})`)(function* (
59 request?: Omit<I, "ServiceId">,
60 ) {
61 return yield* op({
62 ...request,
63 ServiceId: yield* ServiceId,
64 } as I);
65 });
66 });
67 });
68
69/**
70 * Build the impl Effect for a discovery data-plane operation
71 * (`DiscoverInstances` / `DiscoverInstancesRevision`). The runtime callable
72 * injects the bound {@link Service}'s namespace and service names; the
73 * deploy-time half grants `actions` on `*` — the discovery data-plane
74 * actions do not support resource-level IAM permissions.
75 */
76export const makeCloudMapDiscoveryHttpBinding = <
77 I extends { NamespaceName: string; ServiceName: string },
78 A,
79 E,
80 R,
81>(options: {
82 /** Fully-qualified binding tag, e.g. `AWS.CloudMap.DiscoverInstances`. */
83 tag: string;
84 /** The distilled operation; the names are injected from the service. */
85 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
86 /** IAM actions granted on `*` (no resource-level scoping). */
87 actions: readonly string[];
88}) =>
89 Effect.gen(function* () {
90 const op = yield* options.operation;
91
92 return Effect.fn(function* (service: Service) {
93 const NamespaceName = yield* service.namespaceName;
94 const ServiceName = yield* service.serviceName;
95 if (!globalThis.__ALCHEMY_RUNTIME__) {
96 const host = yield* Binding.Host;
97 if (isBindingHost(host)) {
98 yield* host.bind`Allow(${host}, ${options.tag}(${service}))`({
99 policyStatements: [
100 {
101 Effect: "Allow",
102 Action: [...options.actions],
103 Resource: ["*"],
104 },
105 ],
106 });
107 }
108 }
109 return Effect.fn(`${options.tag}(${service.LogicalId})`)(function* (
110 request?: Omit<I, "NamespaceName" | "ServiceName">,
111 ) {
112 return yield* op({
113 ...request,
114 NamespaceName: yield* NamespaceName,
115 ServiceName: yield* ServiceName,
116 } as I);
117 });
118 });
119 });
120
121/**
122 * Build the impl Effect for an account-level operation (`GetOperation`).
123 * The deploy-time half grants `actions` on `*` — these actions do not
124 * support resource-level IAM permissions.
125 */
126export const makeCloudMapAccountHttpBinding = <I, A, E, R>(options: {
127 /** Fully-qualified binding tag, e.g. `AWS.CloudMap.GetOperation`. */
128 tag: string;
129 /** The distilled operation, invoked with the caller's request as-is. */
130 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
131 /** IAM actions granted on `*`. */
132 actions: readonly string[];
133}) =>
134 Effect.gen(function* () {
135 const op = yield* options.operation;
136
137 return Effect.fn(function* () {
138 if (!globalThis.__ALCHEMY_RUNTIME__) {
139 const host = yield* Binding.Host;
140 if (isBindingHost(host)) {
141 yield* host.bind`Allow(${host}, ${options.tag}())`({
142 policyStatements: [
143 {
144 Effect: "Allow",
145 Action: [...options.actions],
146 Resource: ["*"],
147 },
148 ],
149 });
150 }
151 }
152 return Effect.fn(options.tag)(function* (request?: I) {
153 return yield* op((request ?? {}) as I);
154 });
155 });
156 });