1import { ConfigError } from "@distilled.cloud/core/errors";
2import { Credentials } from "@distilled.cloud/prisma";
3import * as Context from "effect/Context";
4import * as Effect from "effect/Effect";
5import * as Layer from "effect/Layer";
6import { AuthProviders } from "../Auth/AuthProvider.ts";
7import { ProfileStore } from "../Auth/Profile.ts";
8import { PrismaEnvironment, fromProfile } from "./PrismaEnvironment.ts";
9
10export { Credentials } from "@distilled.cloud/prisma";
11
12const toConfigError = (cause: unknown) =>
13 new ConfigError({
14 message: `Failed to resolve Prisma credentials: ${
15 (cause as { message?: string })?.message ?? String(cause)
16 }`,
17 });
18
19/**
20 * Bridge an already-resolved {@link PrismaEnvironment} into the distilled
21 * `Credentials` service. Use this when the environment is provided by the
22 * caller (tests, standalone operation helpers).
23 */
24export const fromEnvironment = () =>
25 Layer.effect(
26 Credentials,
27 Effect.gen(function* () {
28 const env = yield* PrismaEnvironment;
29 return Effect.succeed({
30 apiToken: env.serviceToken,
31 apiBaseUrl: env.baseUrl,
32 });
33 }),
34 );
35
36/**
37 * Resolve Prisma credentials lazily from the configured auth profile.
38 *
39 * The `Credentials` service holds an *effect*, so nothing is resolved until
40 * the first API operation runs. That keeps `alchemy dev` — which only
41 * exercises local providers — from ever needing a Prisma token, matching the
42 * deferred behavior of the client layer this replaces.
43 */
44export const fromAuthProvider = () =>
45 Layer.effect(
46 Credentials,
47 Effect.gen(function* () {
48 const scope = yield* Effect.scope;
49 const authProviders = yield* AuthProviders;
50 const profile = yield* ProfileStore;
51 const environment = Layer.buildWithScope(
52 fromProfile().pipe(
53 Layer.provide(
54 Layer.mergeAll(
55 Layer.succeed(AuthProviders, authProviders),
56 Layer.succeed(ProfileStore, profile),
57 ),
58 ),
59 ),
60 scope,
61 ).pipe(Effect.map((context) => Context.get(context, PrismaEnvironment)));
62
63 return yield* Effect.cached(
64 environment.pipe(
65 Effect.map((env) => ({
66 apiToken: env.serviceToken,
67 apiBaseUrl: env.baseUrl,
68 })),
69 Effect.mapError(toConfigError),
70 ),
71 );
72 }),
73 );