Skip to content

Latest commit

 

History

History

README.md

@vlandoss/env examples

Real-world usage examples for @vlandoss/env. Each example is runtime-isolated: it declares its own runtime in a local mise.toml, brings its own package manager and lockfile, and consumes @vlandoss/env from a packed tarball — exactly as an external consumer would. End-to-end tests use Playwright and exercise real env failure modes (missing required vars, wrong types, per-mode config isolation, SSR↔client hydration drift, config loaded synchronously from a CommonJS/require() entry).

Example Runtime Package manager env entries exercised
backend-node Node.js 24 pnpm @vlandoss/env + @vlandoss/env/fs + @vlandoss/env/zod
backend-bun Bun 1.2.4 bun @vlandoss/env + @vlandoss/env/fs + @vlandoss/env/zod
backend-deno Deno 2 (server) + Node 24 (test runner) bun @vlandoss/env + @vlandoss/env/fs + @vlandoss/env/zod
backend-node-cjs Node.js 24 pnpm @vlandoss/env + @vlandoss/env/fs (sync loadConfig via require()) + @vlandoss/env/zod
backend-bun-cjs Bun 1.2.4 bun @vlandoss/env + @vlandoss/env/fs (sync loadConfig via require()) + @vlandoss/env/zod
backend-deno-cjs Deno 2 (server) + Node 24 (test runner) bun @vlandoss/env + @vlandoss/env/fs (sync loadConfig via require()) + @vlandoss/env/zod
worker-cloudflare Cloudflare Workers (wrangler) pnpm @vlandoss/env (runtimeEnv: c.env, per-request) + @vlandoss/env/zod
edge-nextjs Next.js Edge runtime pnpm @vlandoss/env + @vlandoss/env/zod (no FS on Edge)
spa-vite-plugin Node.js (Vite) pnpm @vlandoss/env + @vlandoss/env/vite (envConfig() + #config)
spa-vite-dynamic Node.js (Vite) pnpm @vlandoss/env (dynamic import()) + @vlandoss/env/vite (for __ENV_NAME__)
ssr-react-router Node.js (React Router 7) pnpm @vlandoss/env + @vlandoss/env/fs + @vlandoss/env/react (<EnvScript />)
ssr-tanstack-start Node.js (TanStack Start) pnpm @vlandoss/env + @vlandoss/env/vite + @vlandoss/env/react

The backend-*, worker-*, and edge-nextjs examples drive Playwright in HTTP-only mode (the request fixture); the 4 SPA / SSR examples drive a real chromium browser.

How @vlandoss/env is consumed

Each example declares the dependency as a local file tarball:

"@vlandoss/env": "file:../../package/.local/vlandoss-env.tgz"

The tarball is generated by mise run env:pack (which calls pnpm pack inside package/). This guarantees each example consumes the package as published — same files, same publishConfig.exports, same peerDeps — instead of reaching into the workspace source tree.

backend-deno is the special case: deno install / deno add don't extract file: tarballs — they leave node_modules/@vlandoss/env as a dangling symlink to the .tgz. So we use bun install to hydrate the directory (bun extracts the tarball into a flat layout) and let Deno read it via nodeModulesDir: "manual". Deno still owns the runtime; bun is only there to do the install step.

Setup (from the monorepo root)

mise install          # node + pnpm; bun/deno installed per-example as needed
mise run setup        # bootstraps everything: tools, root deps, env tarball, all examples, Playwright browsers
mise run test:e2e     # runs all 12 e2e suites

Run a single example

Each task is invoked from the monorepo root with the //path:task syntax:

mise run //examples/backend-node:test:e2e
mise run //examples/backend-bun:start
mise run //examples/spa-vite-plugin:dev
mise run //examples/ssr-react-router:test:types

Or cd into the example and run tasks unprefixed:

cd examples/backend-bun
mise run test:e2e
mise run start

Iterating on package/ while developing

Each example's setup task depends on //:env:pack, and env:pack declares sources / outputs so it skips when nothing changed. So a regular mise run //examples/<name>:test:e2e already does the right thing from cold.

For an active edit loop:

mise run env:watch
# in another terminal
mise run //examples/backend-node:start

env:watch runs tsdown --watch plus a chokidar that pipes into env:pack + examples:bump (which reinstalls the tarball in every example).

Why isolated?

@vlandoss/env advertises itself as runtime-agnostic. Examples that secretly resolve through a shared pnpm workspace don't actually prove that promise. With per-example mise.toml, lockfiles, and a packed tarball, each example is a real consumer — it works exactly as it would for someone running pnpm add @vlandoss/env in a fresh project.