1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { Key } from "./Key.ts";
5
6/**
7 * Shared scaffolding for the AWS Payment Cryptography 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 builders
11 * below. Everything except the operation, the IAM action, and (for two-key
12 * operations) the injected request fields is boilerplate.
13 *
14 * Payment Cryptography data-plane operations authorize against the key ARN,
15 * so every builder grants `actions` on the bound {@link Key}(s) and the
16 * runtime callable injects the key ARN(s) into every request.
17 */
18
19/**
20 * Build the impl Effect for an operation scoped to a single {@link Key}
21 * whose input carries the key ARN in a `KeyIdentifier` field (all
22 * single-key Payment Cryptography operations use this field name).
23 */
24export const makePaymentCryptographyKeyHttpBinding = <
25 I extends { KeyIdentifier: string },
26 A,
27 E,
28 R,
29>(options: {
30 /** Fully-qualified binding tag, e.g. `AWS.PaymentCryptography.EncryptData`. */
31 tag: string;
32 /** The distilled operation; `KeyIdentifier` is injected from the key. */
33 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
34 /** IAM actions granted on the bound key's ARN. */
35 actions: readonly `payment-cryptography:${string}`[];
36}) =>
37 Effect.gen(function* () {
38 const op = yield* options.operation;
39
40 return Effect.fn(function* <K extends Key>(key: K) {
41 // Outputs yield a DEFERRED effect — resolve again per invocation below.
42 const KeyArn = yield* key.keyArn;
43 if (!globalThis.__ALCHEMY_RUNTIME__) {
44 const host = yield* Binding.Host;
45 if (isBindingHost(host)) {
46 yield* host.bind`Allow(${host}, ${options.tag}(${key}))`({
47 policyStatements: [
48 {
49 Effect: "Allow",
50 Action: [...options.actions],
51 Resource: [key.keyArn],
52 },
53 ],
54 });
55 }
56 }
57 return Effect.fn(`${options.tag}(${key.LogicalId})`)(function* (
58 request?: Omit<I, "KeyIdentifier">,
59 ) {
60 return yield* op({
61 ...request,
62 KeyIdentifier: yield* KeyArn,
63 } as unknown as I);
64 });
65 });
66 });
67
68/**
69 * Build the impl Effect for an operation spanning two {@link Key}s (e.g.
70 * `ReEncryptData` with incoming + outgoing keys, `GeneratePinData` with
71 * generation + encryption keys). The deploy-time half grants `actions` on
72 * both key ARNs, and the runtime callable injects each key's ARN into the
73 * request field named by `keyFields` (in bind-argument order).
74 */
75export const makePaymentCryptographyKeyPairHttpBinding = <
76 F1 extends string,
77 F2 extends string,
78 I extends Record<F1 | F2, string>,
79 A,
80 E,
81 R,
82>(options: {
83 /** Fully-qualified binding tag, e.g. `AWS.PaymentCryptography.ReEncryptData`. */
84 tag: string;
85 /** The distilled operation; both key fields are injected. */
86 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
87 /** IAM actions granted on both bound keys' ARNs. */
88 actions: readonly `payment-cryptography:${string}`[];
89 /** Request fields carrying the two keys' ARNs, in bind-argument order. */
90 keyFields: readonly [F1, F2];
91}) =>
92 Effect.gen(function* () {
93 const op = yield* options.operation;
94
95 return Effect.fn(function* <A1 extends Key, A2 extends Key>(
96 first: A1,
97 second: A2,
98 ) {
99 const FirstArn = yield* first.keyArn;
100 const SecondArn = yield* second.keyArn;
101 if (!globalThis.__ALCHEMY_RUNTIME__) {
102 const host = yield* Binding.Host;
103 if (isBindingHost(host)) {
104 for (const key of [first, second]) {
105 yield* host.bind`Allow(${host}, ${options.tag}(${key}))`({
106 policyStatements: [
107 {
108 Effect: "Allow",
109 Action: [...options.actions],
110 Resource: [key.keyArn],
111 },
112 ],
113 });
114 }
115 }
116 }
117 return Effect.fn(
118 `${options.tag}(${first.LogicalId}, ${second.LogicalId})`,
119 )(function* (request: Omit<I, F1 | F2>) {
120 return yield* op({
121 ...request,
122 [options.keyFields[0]]: yield* FirstArn,
123 [options.keyFields[1]]: yield* SecondArn,
124 } as unknown as I);
125 });
126 });
127 });