Open History to see the last commit
1import {
2 isSecretReference,
3 secretReferencesInSettings,
4 SettingsSecretUnresolvedError,
5 type SettingsSecretValue,
6 withSettingsSecrets,
7} from "@t3tools/contracts";
8import type * as SecretResolver from "@t3tools/secret-provider-core/server/SecretResolver";
9import * as Effect from "effect/Effect";
10import type * as Schema from "effect/Schema";
11
12/**
13 * Replaces each reference in the slots `schema` declares as `SettingsSecretValue` with the secret
14 * it names, read in one batch. A secret that cannot be read, or reads empty, keeps its reference,
15 * which `requireSettingsSecretLiteral` turns into an error for the consumer of that field.
16 */
17export const resolveSettingsSecrets = Effect.fn("SettingsSecrets.resolve")(function* <
18 Settings extends Readonly<Record<string, unknown>>,
19>(resolver: SecretResolver.SecretResolver["Service"], schema: Schema.Top, settings: Settings) {
20 const pending = secretReferencesInSettings(schema, settings);
21 if (pending.length === 0) return settings;
22 const values = yield* resolver.read(pending.map(({ reference }) => reference));
23 return withSettingsSecrets(
24 settings,
25 pending.flatMap((slot, index) => {
26 const value = values[index];
27 return value === undefined || value.trim() === "" ? [] : [{ ...slot, value }];
28 }),
29 );
30});
31
32/**
33 * The secret a credential setting holds or names. A named secret that cannot be read, or reads
34 * empty, fails instead of passing for a missing token, so callers that would fall back to another
35 * credential cannot silently switch accounts.
36 */
37export const requireSettingsSecret = Effect.fn("SettingsSecrets.require")(function* (
38 resolver: SecretResolver.SecretResolver["Service"],
39 value: SettingsSecretValue,
40) {
41 if (!isSecretReference(value)) return value;
42 const [secret] = yield* resolver.read([value]);
43 if (secret === undefined || secret.trim() === "") {
44 return yield* new SettingsSecretUnresolvedError({
45 provider: yield* resolver.label(value.provider),
46 });
47 }
48 return secret;
49});