1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { Export } from "./Export.ts";
5
6/**
7 * Shared HTTP scaffolding for the BCM Data Exports runtime bindings.
8 *
9 * Every capability follows the same shape — resolve the distilled operation,
10 * register an IAM policy statement on the binding host, and return a runtime
11 * callable. The only variation is the operation, the IAM action(s), and
12 * whether the binding is scoped to one {@link Export} (injecting its
13 * `ExportArn`) or to the whole account (table-dictionary and list
14 * operations, which are not resource-scoped).
15 *
16 * BCM Data Exports is a global service — its endpoint resolver routes every
17 * standard-partition region to the implicit global endpoint, so no region
18 * pinning is needed.
19 *
20 * @internal — not exported from `index.ts`.
21 */
22
23/**
24 * Build the implementation effect for an export-scoped capability: the
25 * runtime callable injects the bound {@link Export}'s ARN as `ExportArn` and
26 * the deploy-time half grants `iamActions` on the export ARN.
27 */
28export const makeExportHttpBinding = <
29 I extends { ExportArn: string },
30 A,
31 E,
32 R,
33>(options: {
34 /**
35 * Short capability name used in the binding sid and runtime span, e.g.
36 * `"GetExecution"`.
37 */
38 capability: string;
39 /**
40 * IAM actions granted on the export ARN, e.g.
41 * `["bcm-data-exports:GetExecution"]`.
42 */
43 iamActions: readonly string[];
44 /**
45 * The distilled operation; `ExportArn` is injected from the resource.
46 */
47 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
48}) =>
49 Effect.gen(function* () {
50 const op = yield* options.operation;
51
52 return Effect.fn(function* (dataExport: Export) {
53 const ExportArn = yield* dataExport.exportArn;
54 if (!globalThis.__ALCHEMY_RUNTIME__) {
55 const host = yield* Binding.Host;
56 if (isBindingHost(host)) {
57 yield* host.bind`Allow(${host}, AWS.BCMDataExports.${options.capability}(${dataExport}))`(
58 {
59 policyStatements: [
60 {
61 Effect: "Allow",
62 Action: [...options.iamActions],
63 Resource: [dataExport.exportArn],
64 },
65 ],
66 },
67 );
68 }
69 }
70 return Effect.fn(
71 `AWS.BCMDataExports.${options.capability}(${dataExport.LogicalId})`,
72 )(function* (request?: Omit<I, "ExportArn">) {
73 return yield* op({ ...request, ExportArn: yield* ExportArn } as I);
74 });
75 });
76 });
77
78/**
79 * Build the implementation effect for an account-level capability (no
80 * export argument — the table dictionary and list operations). The
81 * deploy-time half grants `iamActions` on `*` because these operations are
82 * not resource-scoped.
83 */
84export const makeDataExportsAccountHttpBinding = <
85 I extends object,
86 A,
87 E,
88 R,
89>(options: {
90 /**
91 * Short capability name used in the binding sid and runtime span, e.g.
92 * `"ListTables"`.
93 */
94 capability: string;
95 /**
96 * IAM actions granted on `Resource: ["*"]`.
97 */
98 iamActions: readonly string[];
99 /**
100 * The distilled operation implementing the capability.
101 */
102 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
103}) =>
104 Effect.gen(function* () {
105 const op = yield* options.operation;
106
107 return Effect.fn(function* () {
108 if (!globalThis.__ALCHEMY_RUNTIME__) {
109 const host = yield* Binding.Host;
110 if (isBindingHost(host)) {
111 yield* host.bind`Allow(${host}, AWS.BCMDataExports.${options.capability}())`(
112 {
113 policyStatements: [
114 {
115 Effect: "Allow",
116 Action: [...options.iamActions],
117 Resource: ["*"],
118 },
119 ],
120 },
121 );
122 }
123 }
124 return Effect.fn(`AWS.BCMDataExports.${options.capability}`)(function* (
125 request?: I,
126 ) {
127 return yield* op((request ?? {}) as I);
128 });
129 });
130 });