1import type * as iamcredentials from "@distilled.cloud/gcp/iamcredentials_v1";
2import * as Data from "effect/Data";
3import type * as Effect from "effect/Effect";
4import * as Binding from "../../Binding.ts";
5import type { RuntimeContext } from "../../RuntimeContext.ts";
6import type { Bucket } from "./Bucket.ts";
7
8export interface SignGetObjectUrlRequest {
9 /** Object name (key) to mint a signed download URL for. */
10 object: string;
11 /** Serve this generation instead of the live one. */
12 generation?: string;
13 /**
14 * Seconds the URL stays valid (at most 604800, seven days).
15 * @default 900
16 */
17 expiresIn?: number;
18 /** Override the `Content-Type` Cloud Storage responds with. */
19 contentType?: string;
20}
21
22/** The runtime could not resolve its own service account to sign with. */
23export class SignedUrlFailed extends Data.TaggedError(
24 "GCP.Storage.SignedUrlFailed",
25)<{
26 message: string;
27 cause?: unknown;
28}> {}
29
30export type SignGetObjectUrlError =
31 | SignedUrlFailed
32 | iamcredentials.SignBlobProjectsServiceAccountsError;
33
34/**
35 * Mint V4 signed download (GET) URLs for objects in a Cloud Storage
36 * {@link Bucket}, so a browser can fetch an object without Google
37 * credentials.
38 *
39 * The URL is signed with the host's own runtime service account through
40 * the IAM Credentials `signBlob` API (no key file). The binding grants the
41 * host `roles/storage.objectViewer` on the bucket (a signed URL carries the
42 * signer's permissions) and `roles/iam.serviceAccountTokenCreator` on its
43 * own service account only. See the [signed URL
44 * guide](https://cloud.google.com/storage/docs/access-control/signed-urls).
45 *
46 * ### Signing Download URLs
47 * **Example:** Mint a signed GET URL
48 * ```typescript
49 * const signGetObjectUrl = yield* GCP.Storage.SignGetObjectUrl(bucket);
50 * const url = yield* signGetObjectUrl({ object: "reports/2026.pdf" });
51 * // hand `url` to a browser — it downloads the object without credentials
52 * // …provided with Effect.provide(GCP.Storage.SignGetObjectUrlHttp)
53 * ```
54 *
55 * **Example:** Custom expiry and response Content-Type
56 * ```typescript
57 * const url = yield* signGetObjectUrl({
58 * object: "reports/2026.pdf",
59 * expiresIn: 3600,
60 * contentType: "application/pdf",
61 * });
62 * ```
63 *
64 * @binding
65 * @category Storage
66 */
67export interface SignGetObjectUrl extends Binding.Service<
68 SignGetObjectUrl,
69 "GCP.Storage.SignGetObjectUrl",
70 (
71 bucket: Bucket,
72 ) => Effect.Effect<
73 (
74 request: SignGetObjectUrlRequest,
75 ) => Effect.Effect<string, SignGetObjectUrlError, RuntimeContext>
76 >
77> {}
78
79export const SignGetObjectUrl = Binding.Service<SignGetObjectUrl>(
80 "GCP.Storage.SignGetObjectUrl",
81);