znv
Version:
Parse your environment with Zod schemas
93 lines • 4.12 kB
JavaScript
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