1import { Credentials } from "@distilled.cloud/gcp/Credentials";
2import * as Effect from "effect/Effect";
3import * as HttpClient from "effect/http/HttpClient";
4import type { Output } from "../../Output.ts";
5import { bindGcpHost } from "../Host.ts";
6import { type BindingIam, grantFor } from "../HttpBinding.ts";
7
8type GcpHttpOp<I, A, E> = Effect.Effect<
9 (input: I) => Effect.Effect<A, E>,
10 never,
11 Credentials | HttpClient.HttpClient
12> &
13 ((input: I) => Effect.Effect<A, E, Credentials | HttpClient.HttpClient>);
14
15type NamedResource = {
16 readonly name: Output<string, never>;
17 readonly LogicalId: string;
18};
19
20/**
21 * Shared HTTP scaffolding for Managed Kafka named-resource bindings.
22 * Distilled ops are OperationMethods: yield them once at Layer
23 * construction (after providing Credentials + HttpClient) so the inner
24 * runtime Effect is `Effect<A, E>` and does not leak `GcpOpContext`.
25 * NOT exported from index.ts.
26 */
27export const makeManagedKafkaHttpBinding =
28 <Resource extends NamedResource>() =>
29 <I extends { name?: string }, A, E>(options: {
30 tag: string;
31 iam: BindingIam;
32 operation: GcpHttpOp<I, A, E>;
33 }) =>
34 Effect.gen(function* () {
35 const run = yield* options.operation;
36 return Effect.fn(function* (resource: Resource) {
37 yield* bindGcpHost({
38 tag: options.tag,
39 resource: resource,
40 iam: [grantFor(options.iam, resource.name)],
41 });
42 const name = yield* resource.name;
43 return Effect.fn(`${options.tag}(${resource.LogicalId})`)(function* (
44 request?: Omit<I, "name">,
45 ) {
46 const resourceName = yield* name;
47 return yield* run({
48 ...(request ?? {}),
49 name: resourceName,
50 } as I);
51 });
52 });
53 });