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