1import { ConfigError } from "@distilled.cloud/core/errors";
2import { Credentials, toConfig } from "@distilled.cloud/railway";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5import * as Redacted from "effect/Redacted";
6import {
7 deferUntilFirstUse,
8 orDieCredentialsUnavailable,
9 resolveProviderConfig,
10} from "../Auth/Resolve.ts";
11import {
12 RAILWAY_AUTH_PROVIDER_NAME,
13 type RailwayAuthConfig,
14 type RailwayResolvedCredentials,
15} from "./AuthProvider.ts";
16
17export {
18 Credentials,
19 CredentialsFromEnv,
20 CredentialsFromToken,
21 DEFAULT_API_BASE_URL,
22 type Config as CredentialsConfig,
23 type TokenKind,
24} from "@distilled.cloud/railway";
25
26/**
27 * Build a `Credentials` layer that resolves Railway credentials via the
28 * current Alchemy profile, or directly from environment variables in CI.
29 *
30 * Maps onto `@distilled.cloud/railway`'s
31 * `{ token, tokenKind: "account", apiBaseUrl }` shape. Alchemy itself only
32 * reads `RAILWAY_API_TOKEN` (account Bearer). Distilled's own
33 * `CredentialsFromEnv` also accepts `RAILWAY_TOKEN` / `RAILWAY_PROJECT_TOKEN`
34 * as fallbacks.
35 */
36export const fromAuthProvider = () =>
37 Layer.effect(
38 Credentials,
39 Effect.gen(function* () {
40 // Defer profile lookup and credential resolution until first use, so
41 // building the provider layers never requires a configured profile.
42 const resolve = yield* resolveProviderConfig<
43 RailwayAuthConfig,
44 RailwayResolvedCredentials
45 >(RAILWAY_AUTH_PROVIDER_NAME).pipe(
46 Effect.flatMap(({ profileName, resolve }) =>
47 resolve.pipe(
48 Effect.map((creds) =>
49 toConfig({
50 token: Redacted.value(creds.token),
51 tokenKind: creds.tokenKind,
52 apiBaseUrl: creds.apiBaseUrl,
53 }),
54 ),
55 Effect.mapError(
56 (e) =>
57 new ConfigError({
58 message: `Failed to resolve Railway credentials from ${profileName === undefined ? "the CI environment" : `profile '${profileName}'`}: ${(e as { message?: string }).message ?? String(e)}`,
59 }),
60 ),
61 ),
62 ),
63 deferUntilFirstUse,
64 );
65 return yield* resolve.pipe(
66 orDieCredentialsUnavailable(RAILWAY_AUTH_PROVIDER_NAME),
67 Effect.cached,
68 );
69 }),
70 );