wherewithal
v0.1.0
Published
Load and validate environment/config against any Standard Schema validator (Zod/Valibot/ArkType/...) with a fail-fast, all-errors-at-once report. Validator-agnostic, zero own deps.
Maintainers
Readme
wherewithal
Load and validate environment/config against any Standard Schema validator (Zod/Valibot/ArkType/...) with a fail-fast, all-errors-at-once report. Validator-agnostic, zero own deps.
The problem
Reading process.env safely means coercing strings to numbers/booleans, validating each value, failing fast at boot with a readable report, and getting a typed object out. Doing this by hand is tedious: every parseInt is a place to forget NaN, every missing variable is an incident waiting to happen.
Existing loaders like t3-env, envalid, and znv solve this, but they are coupled to Zod. Teams on Valibot or ArkType either reinvent the same wheel or maintain a parallel config. Standard Schema decouples validation libraries through a single interface, so an env loader built on it works unchanged whether you bring Zod, Valibot, ArkType, or any future compliant library.
Install
npm install wherewithal
# or
pnpm add wherewithal
# or
yarn add wherewithalBring your own validator separately:
npm install zod # or valibot, arktype, ...Use
10-second copy-paste with Zod:
import wherewithal from "wherewithal";
import { z } from "zod";
const env = wherewithal(
{
PORT: z.coerce.number(),
NODE_ENV: z.enum(["dev", "prod"]),
},
process.env,
);
// env.PORT: number
// env.NODE_ENV: "dev" | "prod"Compose with a .env file via Node's built-in loader:
node --env-file=.env app.jsOn failure, wherewithal throws a single EnvError listing every bad key at once:
Environment validation failed:
PORT expected number, received "not-a-number"
NODE_ENV expected one of ["dev","prod"], received "staging"API
wherewithal(schema, source, options?): Vars<S>
Sync entry. Calls each validator's ~standard.validate with source[key] and returns a frozen typed object.
- Throws
EnvErrorif any validator fails or throws. - Throws
Errorif any validator returns aPromise- usewherewithalAsyncinstead.
wherewithalAsync(schema, source, options?): Promise<Vars<S>>
Async entry. Awaits each validator's result. Use when any validator in the schema returns a Promise.
WherewithalOptions
interface WherewithalOptions {
onExtra?: "ignore" | "warn"; // default: "ignore"
}"ignore"- silently drop source keys not inschema."warn"- print oneconsole.warnlisting the extras. Extras never fail validation.
Vars<S>
Mapped type over schema outputs. Each key's type is its validator's Standard Schema InferOutput.
EnvError
Error subclass thrown on validation failure.
class EnvError extends Error {
readonly issues: ReadonlyArray<{ key: string; message: string }>;
}message- aligned multi-line report, one line per issue, keys padded to equal width.issues- machine-readable list for logging and monitoring.
Default export
wherewithal (the sync entry) is the default export. Named exports include wherewithalAsync, EnvError, and the Vars / WherewithalOptions types.
Non-goals
What wherewithal does NOT do:
- Does NOT bundle a validator. Bring Zod, Valibot, ArkType, or any Standard Schema lib.
- Does NOT parse
.envfiles. Compose withnode --env-file=.envordotenv. - Does NOT manage secrets. Use your platform's secret manager.
- Does NOT re-validate at runtime or watch for changes. Call once at boot; treat the result as immutable.
- Does NOT support nested or namespaced config trees. Use a flat schema; derive nested shapes in app code.
TypeScript note
Full type declarations ship in dist/. Type inference flows from each validator's Standard Schema InferOutput, so env.PORT is statically number when paired with z.coerce.number().
Requires TypeScript 5.7+ for verbatimModuleSyntax compatibility.
License
MIT
