UNPKG

envase

Version:

Type-safe environment variable validation with Standard Schema compliance

87 lines 3.37 kB
import { EnvaseError } from "./errors/envase-error.js"; export const detectNodeEnv = (env) => { const nodeEnv = env.NODE_ENV; return { isProduction: nodeEnv === 'production', isTest: nodeEnv === 'test', isDevelopment: nodeEnv === 'development', }; }; export const envvar = (name, schema) => [name, schema]; export const parseEnv = (env, envSchema) => { const envvarValidationIssues = []; // biome-ignore lint/suspicious/noExplicitAny: Explicit 'any' is required due to nature of recursive processing const parseConfigObject = (schema) => { return Object.fromEntries(Object.entries(schema).map(([key, value]) => { if (Array.isArray(value)) { const [envvarName, schema] = value; const envvarValue = env[envvarName]; const result = schema['~standard'].validate(envvarValue); if (result instanceof Promise || ('then' in result && typeof result.then === 'function')) { throw new Error(`Schema validation for envvar "${envvarName}" must be synchronous`); } if (result.issues) { envvarValidationIssues.push({ name: envvarName, value: envvarValue, messages: result.issues.map(({ message }) => message), }); return [key, null]; } return [key, result.value]; } return [key, parseConfigObject(value)]; })); }; const config = parseConfigObject(envSchema); if (envvarValidationIssues.length > 0) { throw new EnvaseError(envvarValidationIssues); } return config; }; // Helper to check if value is a resolver function const isResolver = (value) => typeof value === 'function'; // Helper to process computed schema recursively const processComputed = (computed, rawConfig) => { return Object.fromEntries(Object.entries(computed).map(([key, value]) => [ key, isResolver(value) ? value(rawConfig) : processComputed(value, rawConfig), ])); }; // Helper to deep merge two objects const deepMerge = (target, source) => { const result = { ...target }; for (const key of Object.keys(source)) { const sourceValue = source[key]; const targetValue = result[key]; if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue) && targetValue && typeof targetValue === 'object' && !Array.isArray(targetValue)) { result[key] = deepMerge(targetValue, sourceValue); } else { result[key] = sourceValue; } } return result; }; // Implementation export function createConfig(env, options) { // Parse raw config using existing parseEnv const rawConfig = parseEnv(env, options.schema); // If no computed values, return raw config if (!options.computed) { return rawConfig; } // Compute derived values (handles nested structures) const computedValues = processComputed(options.computed, rawConfig); // Deep merge raw config with computed values return deepMerge(rawConfig, computedValues); } //# sourceMappingURL=core.js.map