Skip to content

risevest/envault

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@risemaxi/envault

Build-time validated, typed environment variables for any bundler.

envault validates process.env against a Standard Schema at build time, then exposes the validated values through a virtual module. The schema library never enters your bundle, and a missing or invalid variable fails the build instead of crashing at runtime.

Works with Zod, Valibot, ArkType, Effect Schema, TypeBox — anything implementing the Standard Schema ~standard interface.

envault moves validation to the build step. The schema runs once during bundling; only the validated, typed values reach the output.

Install

npm install -D @risemaxi/envault

Quick start

1. Define a schema

// env.schema.ts
import { z } from "zod";

export default z.object({
  API_URL: z.string().url(),
  SECRET: z.string().min(1),
  PORT: z.coerce.number().default(3000),
});

2. Add the plugin

// vite.config.ts
import envault from "@risemaxi/envault/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [envault({ schemaFile: "./env.schema.ts" })],
});

3. Import your env

import { env } from "virtual:env";

console.log(env.API_URL); // string
console.log(env.PORT); // number

A ./env.d.ts is generated next to your config so env is fully typed from the schema's output.

Bundlers

Every integration takes the same options. Import the one for your bundler:

import envault from "@risemaxi/envault/vite";
import envault from "@risemaxi/envault/rollup";
import envault from "@risemaxi/envault/rolldown";
import envault from "@risemaxi/envault/webpack";
import envault from "@risemaxi/envault/rspack";
import envault from "@risemaxi/envault/esbuild";
import envault from "@risemaxi/envault/bun";
// webpack.config.js
const envault = require("@risemaxi/envault/webpack");

module.exports = {
  plugins: [envault({ schemaFile: "./env.schema.ts" })],
};

The default export of @risemaxi/envault is the Vite plugin:

import envault from "@risemaxi/envault";

Expo / Metro

// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const { withEnvConfig } = require("@risemaxi/envault/metro");

const config = getDefaultConfig(__dirname);

module.exports = withEnvConfig(config, {
  schemaFile: "./env.schema.ts",
});

withEnvConfig preserves any existing resolver.resolveRequest (including Expo's defaults) and delegates every other module to it.

Validation runs when the virtual module is first bundled, not when the Metro config is loaded. Tools that merely load your config without bundling — such as EAS Build's fingerprint and config-resolution steps, which run before your project's env vars are injected — therefore don't trip validation on env that isn't present yet.

Metro's resolver is synchronous, so an inline schema passed to withEnvConfig must validate synchronously. Use schemaFile for asynchronous schemas.

Passing a schema directly

Instead of a file path, pass the schema object inline:

import { z } from "zod";
import envault from "@risemaxi/envault/vite";

const schema = z.object({
  API_URL: z.string(),
  PORT: z.coerce.number().default(3000),
});

export default {
  plugins: [envault({ schema })],
};

With an inline schema no .d.ts is generated (there is no file path to reference). Type virtual:env yourself, or use schemaFile to get types for free.

How it works

  1. For a schemaFile, envault loads the schema in a child process with jiti, so .ts/.js schemas and their imports work the same in any project — CommonJS or ESM, any Node ≥ 18 — and validation runs in the child. The schema library stays out of the build process.
  2. For an inline schema, validation runs directly in the build.
  3. If validation fails, the build fails, listing every missing/invalid variable.
  4. On success the virtual module is generated as export const env = { ... } with the validated values inlined. Keys are JSON-quoted, so even names like MY-VAR are reachable via env["MY-VAR"].
  5. A .d.ts is written so env is typed directly from the schema's output.

No schema-library code or validation logic ends up in the final bundle.

Where env values come from

envault validates the environment your bundler/runtime provides, using each one's native env loading:

  • Vite loads .env files into import.meta.env, not process.env, so envault reads them through Vite's own loader (honoring your mode and envDir). .env, .env.local, and .env.[mode] work with no extra setup.
  • Bun and Expo / Metro load .env files into process.env, which envault reads directly.
  • Rollup, Rolldown, webpack, Rspack, and esbuild have no built-in .env loading. Populate process.env yourself — real environment variables (CI, shell) or a loader like dotenv (import "dotenv/config") before the build.

Real process.env values always take precedence over values from .env files.

Options

Option Type Default Description
schema StandardSchemaV1 A Standard Schema object. Mutually exclusive with schemaFile.
schemaFile string Path (from project root) to a module that default-exports a schema.
virtualModule string "virtual:env" The virtual module specifier to import from.
dtsFile string "./env.d.ts" Where to write the generated .d.ts.

schema and schemaFile are mutually exclusive; provide exactly one.

Supported schema libraries

Any library implementing Standard Schema v1:

Exported utilities

import {
  envault, // Vite plugin factory (also the default export)
  vite,
  rollup,
  rolldown,
  webpack,
  rspack,
  esbuild,
  bun, // per-bundler factories
  validateEnv, // (schema, env) => Promise<ValidatedEnv> — async, in-process
  validateEnvSync, // (schema, env) => ValidatedEnv — sync, throws on async schemas
  loadSchemaEnv, // (schemaFile, env) => ValidatedEnv — load + validate in a child process
  clearSchemaCache, // () => void — drop the loadSchemaEnv cache
  generateModule, // (env) => string — the virtual module source
  generateDts, // (schemaImport, virtualModule?) => string — the .d.ts source
} from "@risemaxi/envault";

@risemaxi/envault/metro additionally exports withEnvConfig.

License

MIT

About

Simple build time env validation

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors