1import { ConfigError } from "@distilled.cloud/core/errors";
2import { Credentials } from "@distilled.cloud/hetzner";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5import {
6 deferUntilFirstUse,
7 orDieCredentialsUnavailable,
8 resolveProviderConfig,
9} from "../Auth/Resolve.ts";
10import {
11 HETZNER_AUTH_PROVIDER_NAME,
12 type HetznerAuthConfig,
13 type HetznerResolvedCredentials,
14} from "./AuthProvider.ts";
15
16export {
17 Credentials,
18 CredentialsFromEnv,
19 credentials,
20 DEFAULT_API_BASE_URL,
21 type Config as CredentialsConfig,
22} from "@distilled.cloud/hetzner";
23
24/**
25 * Build a `Credentials` layer that resolves Hetzner credentials via the
26 * Alchemy AuthProvider using the configured profile (defaults to "default",
27 * overridable with the `ALCHEMY_PROFILE` env/config value).
28 *
29 * Maps onto `@distilled.cloud/hetzner`'s `{ token, apiBaseUrl }` shape.
30 */
31export const fromAuthProvider = () =>
32 Layer.effect(
33 Credentials,
34 Effect.gen(function* () {
35 // Defer profile lookup and credential resolution until first use, so
36 // building the provider layers never requires a configured profile.
37 const resolve = yield* resolveProviderConfig<
38 HetznerAuthConfig,
39 HetznerResolvedCredentials
40 >(HETZNER_AUTH_PROVIDER_NAME).pipe(
41 Effect.flatMap(({ profileName, resolve }) =>
42 resolve.pipe(
43 Effect.map((creds) => ({
44 token: creds.token,
45 apiBaseUrl: creds.apiBaseUrl,
46 })),
47 Effect.mapError(
48 (e) =>
49 new ConfigError({
50 message: `Failed to resolve Hetzner credentials from ${profileName === undefined ? "the CI environment" : `profile '${profileName}'`}: ${(e as { message?: string }).message ?? String(e)}`,
51 }),
52 ),
53 ),
54 ),
55 deferUntilFirstUse,
56 );
57 return yield* resolve.pipe(
58 orDieCredentialsUnavailable(HETZNER_AUTH_PROVIDER_NAME),
59 Effect.cached,
60 );
61 }),
62 );