import { z } from "zod"; const nonEmptyString = z.string().trim().min(1); const urlString = z.url(); export type Environment = Record; 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( 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 }); } }