@beignet/core
Version:
Core framework primitives for Beignet
99 lines (90 loc) • 3.14 kB
text/typescript
import type { StandardSchemaV1 } from "@standard-schema/spec";
import {
ConfigValidationError,
parseStandardSchemaAsync,
readEnv,
} from "../../config/index.js";
import { SchemaValidationError } from "../../errors/index.js";
import type { AnyPorts } from "../../ports/index.js";
import type { ServiceProvider } from "../../providers/index.js";
type ProviderConfigDef<Schema extends StandardSchemaV1<unknown, unknown>> = {
schema: Schema;
envPrefix?: string;
overrides?: Record<string, unknown>;
};
async function parseStandardSchema<
Schema extends StandardSchemaV1<unknown, unknown>,
>(
schema: Schema,
input: unknown,
): Promise<StandardSchemaV1.InferOutput<Schema>> {
try {
return await parseStandardSchemaAsync(schema, input);
} catch (error) {
if (error instanceof ConfigValidationError) {
throw new SchemaValidationError(error.issues);
}
throw error;
}
}
/**
* Load and validate config for a provider.
*
* Explicit overrides take precedence over env-derived config. When a provider
* declares `envPrefix`, matching env keys are stripped before validation.
*/
export async function loadProviderConfig<
Ports extends AnyPorts,
Schema extends StandardSchemaV1<unknown, unknown>,
>(
provider: ServiceProvider<Ports, Schema>,
env: Record<string, string | undefined>,
overrides: Record<string, unknown>,
): Promise<StandardSchemaV1.InferOutput<Schema> | undefined> {
const configDef = provider.config as ProviderConfigDef<Schema> | undefined;
if (!configDef) return undefined;
const { schema, envPrefix, overrides: fieldOverrides } = configDef;
const hasOverride = Object.hasOwn(overrides, provider.name);
const base = hasOverride
? overrides[provider.name]
: envPrefix
? readEnv({ env, prefix: envPrefix })
: {};
// Field-level overrides from the provider's own config definition (factory
// options) win over env-derived and server-supplied values. Defined values
// only, so absent options fall back to the base input and schema defaults.
let input = base;
if (fieldOverrides) {
const defined = Object.fromEntries(
Object.entries(fieldOverrides).filter(([, value]) => value !== undefined),
);
if (Object.keys(defined).length > 0) {
input = {
...(typeof base === "object" && base !== null ? base : {}),
...defined,
};
}
}
try {
return await parseStandardSchema(schema, input);
} catch (error) {
const suffix = envPrefix ? ` (envPrefix=${envPrefix})` : "";
const found = envPrefix
? Object.keys(env).filter(
(k) => k.startsWith(envPrefix) && env[k] !== undefined,
)
: [];
const hint = envPrefix
? found.length
? `\n Env vars found with prefix "${envPrefix}": ${found.join(", ")}`
: `\n No env vars found with prefix "${envPrefix}"`
: "";
throw new Error(
`Failed to load config for provider "${provider.name}"${suffix}: ${(error as Error).message}${hint}`,
);
}
}
/**
* Parse provider config with Standard Schema and normalize validation errors.
*/
export { parseStandardSchema };