1import * as Effect from "effect/Effect";
2import { bindGcpHost } from "../Host.ts";
3import { grantFor, type GcpHttpOp } from "../HttpBinding.ts";
4import type { Bucket } from "./Bucket.ts";
5
6/**
7 * Grant `role` on the bound bucket's own IAM policy (not the project), the
8 * way AWS S3 bindings scope statements to the bucket ARN.
9 */
10export const grantOnBucket = (tag: string, bucket: Bucket, role: string) =>
11 bindGcpHost({
12 tag,
13 resource: bucket,
14 iam: [grantFor({ role, on: "storage.bucket" }, bucket.bucketName)],
15 });
16
17/**
18 * Shared HTTP scaffolding for Cloud Storage object metadata bindings
19 * (`DeleteObject`): yields the distilled operation once at Layer
20 * construction and injects the bound bucket's name.
21 * NOT exported from index.ts.
22 */
23export const makeObjectHttpBinding = <
24 I extends { bucket?: string },
25 A,
26 E,
27>(options: {
28 tag: string;
29 role: string;
30 operation: GcpHttpOp<I, A, E>;
31}) =>
32 Effect.gen(function* () {
33 const run = yield* options.operation;
34 return Effect.fn(function* (bucket: Bucket) {
35 yield* grantOnBucket(options.tag, bucket, options.role);
36 const bucketName = yield* bucket.bucketName;
37 return Effect.fn(`${options.tag}(${bucket.LogicalId})`)(function* (
38 request: Omit<I, "bucket">,
39 ) {
40 return yield* run({
41 ...request,
42 bucket: yield* bucketName,
43 } as I);
44 });
45 });
46 });