Summary
We use envalid under QuickJS (qjs) in a Node-free container image to validate env once at docker entrypoint (before the process becomes ready). The browser consumes a generated runtime-env module and does not run envalid. That setup works well, but only after a small must-run-before-import host shim.
It would help if envalid either:
- shipped an official envalid/quickjs (or similar) entry that applies safe host patches, or
- documented host prerequisites and offered optional adapters so consumers don’t reverse-engineer load-time assumptions.
Environment
envalid 8.x
- QuickJS (
qjs), no Node in the final image
- Bundled ESM validator (esbuild) run from the container entrypoint
- Shared env schema / types with the browser app; validation only in entrypoint
Problems we hit
console at import time. Default reporter does something like console.error.bind(console) when the module loads. QuickJS hosts often only expose console.log (or incomplete console). Import crashes before any cleanEnv / custom reporter runs.
url() needs URL. url() uses new URL(x) (then returns the original string). Host QuickJS may have no URL, or only a partial one.
Error.captureStackTrace. EnvError / EnvMissingError call V8’s Error.captureStackTrace. QuickJS doesn’t have it → throw during error construction unless stubbed.
Because (1) happens at import, shims must load before import 'envalid'. That forces awkward import-order rules in consumer code.
What we shim today (minimal)
- Ensure console.error / warn / info / log exist and are bindable
- Stub Error.captureStackTrace as no-op
- Provide a minimal URL for absolute
scheme://… (enough for url(), not full WHATWG)
Proposal
Option A (preferred): import 'envalid/quickjs' (side-effect or explicit installQuickJsHost()), then normal envalid API.
Option B: Keep core unchanged; export something like:
import { ensureHostForEnvalid } from 'envalid/host'
ensureHostForEnvalid({ console, URL, captureStackTrace: 'noop' })
Option C (docs-only): Document that non-Node hosts must provide bindable console.*, URL for url(), and optionally Error.captureStackTrace before importing envalid.
Also useful: delay console.error.bind(console) until first report / first cleanEnv, so a custom reporter can be set without a complete console.
Why this fits envalid
README already targets “Node, Bun, and other compatible JS runtimes.” Tiny QuickJS runtimes are a real pattern for validate env before the process becomes ready, without shipping Node. Official host support (or clear docs) beats every consumer reinventing the same shim.
Non-goals
- Full WHATWG URL inside envalid
- QuickJS stdlib (std / os) integration
- Replacing custom reporters
Happy to PR a tiny envalid/quickjs install helper + a short “non-Node hosts” doc section if that direction works for you.
Summary
We use
envalidunder QuickJS (qjs) in a Node-free container image to validate env once at docker entrypoint (before the process becomes ready). The browser consumes a generatedruntime-envmodule and does not runenvalid. That setup works well, but only after a small must-run-before-import host shim.It would help if envalid either:
Environment
envalid8.xqjs), no Node in the final imageProblems we hit
consoleat import time. Default reporter does something likeconsole.error.bind(console)when the module loads. QuickJS hosts often only expose console.log (or incomplete console). Import crashes before any cleanEnv / custom reporter runs.url()needs URL.url()uses new URL(x) (then returns the original string). Host QuickJS may have no URL, or only a partial one.Error.captureStackTrace. EnvError / EnvMissingError call V8’s Error.captureStackTrace. QuickJS doesn’t have it → throw during error construction unless stubbed.Because (1) happens at import, shims must load before
import 'envalid'. That forces awkward import-order rules in consumer code.What we shim today (minimal)
scheme://…(enough for url(), not full WHATWG)Proposal
Option A (preferred):
import 'envalid/quickjs'(side-effect or explicit installQuickJsHost()), then normal envalid API.Option B: Keep core unchanged; export something like:
Option C (docs-only): Document that non-Node hosts must provide bindable console.*, URL for url(), and optionally Error.captureStackTrace before importing envalid.
Also useful: delay
console.error.bind(console)until first report / first cleanEnv, so a custom reporter can be set without a complete console.Why this fits envalid
README already targets “Node, Bun, and other compatible JS runtimes.” Tiny QuickJS runtimes are a real pattern for validate env before the process becomes ready, without shipping Node. Official host support (or clear docs) beats every consumer reinventing the same shim.
Non-goals
Happy to PR a tiny
envalid/quickjsinstall helper + a short “non-Node hosts” doc section if that direction works for you.