UNPKG

znv

Version:

Parse your environment with Zod schemas

93 lines 4.12 kB
import * as z from "zod"; import { getSchemaWithPreprocessor } from "./preprocessors.js"; import { makeDefaultReporter, errorMap, } from "./reporter.js"; /** * Since there might be a provided default value of `null` or `undefined`, we * return a tuple that also indicates whether we found a default. */ export function resolveDefaultValueForSpec(defaults, nodeEnv) { if (defaults) { if (nodeEnv != null && Object.hasOwn(defaults, nodeEnv)) { return [true, defaults[nodeEnv]]; } if ("_" in defaults) return [true, defaults["_"]]; } return [false, undefined]; } /** * Mostly an internal convenience function for testing. Returns the input * parameter unchanged, but with the same inference used in `parseEnv` applied. */ export const inferSchemas = (schemas) => schemas; /** * Parses the passed environment object using the provided map of Zod schemas * and returns the immutably-typed, parsed environment. * * This version of `parseEnv` is intended for internal use and requires a * reporter or token formatters to be passed in. The versions exported in * `index.js` and `compat.js` provide defaults for this third parameter, making * it optional. */ export function parseEnvImpl(env, schemas, reporterOrTokenFormatters) { const reporter = typeof reporterOrTokenFormatters === "function" ? reporterOrTokenFormatters : makeDefaultReporter(reporterOrTokenFormatters); const parsed = {}; const errors = []; for (const [key, schemaOrSpec] of Object.entries(schemas)) { const envValue = env[key]; let defaultUsed = false; let defaultValue; try { if (schemaOrSpec instanceof z.ZodType) { if (envValue == null && schemaOrSpec instanceof z.ZodDefault) { defaultUsed = true; defaultValue = schemaOrSpec._def.defaultValue(); // we "unwrap" the default value ourselves and pass it to the schema. // in the very unlikely case that the value isn't stable AND // validation fails, this ensures the default value we report is the // one that was actually used. // (consider `z.number().gte(0.5).default(() => Math.random())` -- if // we invoked the default getter and got 0.7, and then ran the parser // against a missing env var and it generated another default of 0.4, // we'd report a default value that _should_ have passed.) parsed[key] = schemaOrSpec.parse(defaultValue, { errorMap }); } else { parsed[key] = getSchemaWithPreprocessor(schemaOrSpec).parse(envValue, { errorMap }); } } else if (envValue == null) { [defaultUsed, defaultValue] = resolveDefaultValueForSpec(schemaOrSpec.defaults, env["NODE_ENV"]); if (defaultUsed) { parsed[key] = schemaOrSpec.schema.parse(defaultValue, { errorMap }); } else { // if there's no default, pass our envValue through the // schema-with-preprocessor (it's an edge case, but our schema might // accept `null`, and the preprocessor will convert `undefined` to // `null` for us). parsed[key] = getSchemaWithPreprocessor(schemaOrSpec.schema).parse(envValue, { errorMap }); } } else { parsed[key] = getSchemaWithPreprocessor(schemaOrSpec.schema).parse(envValue, { errorMap }); } } catch (e) { errors.push({ key, receivedValue: envValue, error: e, defaultUsed, defaultValue, }); } } if (errors.length > 0) { throw new Error(reporter(errors, schemas)); } return parsed; } //# sourceMappingURL=parse-env.js.map