chore(config): centralize feature settings
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const nonEmptyString = z.string().trim().min(1);
|
||||
const urlString = z.url();
|
||||
|
||||
export type Environment = Record<string, string | undefined>;
|
||||
|
||||
export function requiredEnvironmentValue(
|
||||
name: string,
|
||||
environment: Environment = process.env,
|
||||
) {
|
||||
const parsed = nonEmptyString.safeParse(environment[name]);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Missing required environment variable: ${name}`);
|
||||
}
|
||||
return parsed.data;
|
||||
}
|
||||
|
||||
export function optionalEnvironmentValue(
|
||||
name: string,
|
||||
environment: Environment = process.env,
|
||||
) {
|
||||
const parsed = nonEmptyString.safeParse(environment[name]);
|
||||
return parsed.success ? parsed.data : null;
|
||||
}
|
||||
|
||||
export function requiredEnvironmentUrl(
|
||||
name: string,
|
||||
environment: Environment = process.env,
|
||||
) {
|
||||
const value = requiredEnvironmentValue(name, environment);
|
||||
const parsed = urlString.safeParse(value);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Environment variable ${name} must be an absolute URL`);
|
||||
}
|
||||
return parsed.data.replace(/\/$/, "");
|
||||
}
|
||||
|
||||
export function readFeatureEnvironment<Names extends readonly string[]>(
|
||||
feature: string,
|
||||
names: Names,
|
||||
environment: Environment = process.env,
|
||||
): { [Name in Names[number]]: string } {
|
||||
try {
|
||||
return Object.fromEntries(
|
||||
names.map((name) => [name, requiredEnvironmentValue(name, environment)]),
|
||||
) as { [Name in Names[number]]: string };
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Invalid configuration";
|
||||
throw new Error(`${feature} is not configured: ${message}`, { cause: error });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user