Resolve Config With Explicit Source Precedence
A configuration resolver that merges defaults, file, environment and CLI sources in a declared order and records which source won each key.
Code
export interface ConfigSource {
readonly name: string;
readonly values: Record<string, unknown>;
}
export interface ResolvedConfig<T> {
readonly value: T;
readonly provenance: Record<string, string>;
}
export function resolveConfig<T extends Record<string, unknown>>(
sources: readonly ConfigSource[],
options: { required?: readonly string[] } = {},
): ResolvedConfig<T> {
const merged: Record<string, unknown> = {};
const provenance: Record<string, string> = {};
const seenNames = new Set<string>();
for (const source of sources) {
if (seenNames.has(source.name)) {
throw new Error("duplicate config source name: " + source.name);
}
seenNames.add(source.name);
if (!source.values || typeof source.values !== "object") {
throw new TypeError("source " + source.name + " values must be an object");
}
for (const [key, value] of Object.entries(source.values)) {
if (value === undefined) continue;
merged[key] = value;
provenance[key] = source.name;
}
}
const missing = (options.required ?? []).filter((key) => !(key in merged));
if (missing.length > 0) {
throw new Error("missing required config keys: " + missing.join(", "));
}
return { value: merged as T, provenance };
}Report a problem with this exampleOpens a pre-filled issue on GitHub.