Sources
Bind an environment source with useSource, and what the Node, Worker, and manual providers read.
A source is the object envapt reads variables from. On Node, Bun, and Deno one is bound automatically on import, reading process.env and the .env file cascade. On Cloudflare Workers and in the browser you bind a source yourself with Envapter.useSource(...), then read with the same typed API.
On Node, Bun, and Deno you never call useSource. The Node entry binds a source that reads a clone of process.env
plus the .env cascade at import time.
The providers
envapt ships two sources.
FileSourceis bound automatically on the Node entry and is Node-only. It reads a clone ofprocess.envand the.envcascade.PortableSourcewraps any object you pass, for every runtime without a filesystem. Pass the Cloudflareenvbinding on Workers (see Workers), or the config your bundler injects into a browser build likeimport.meta.env(see Browser).
PortableSource snapshots the object at construction (so later mutation does not leak in) and JSON-stringifies non-string values so the converters parse them the same way.
You pass the source the object the runtime exposes, the Worker env binding or the bundler's injected config, not one you write out by hand. The value is the typed read on top:
declare const : <string, unknown>; // the Cloudflare env binding
.(new ());
const = .('UPSTREAM_URL', .); // URL | undefined
const = .('CACHE_TTL', ., '5m'); // number (ms)Any object can be a source
Source is one required method, readVars(): Record<string, string>. Any object that satisfies it works, so you can adapt another config system, a secrets payload or an RPC response, without writing a provider.
declare const : <string, string>; // fetched from your secrets manager
const : = { : () => };
.();
const = .('API_BASE_URL', .);
const = .('DB_POOL_SIZE', 10);An optional supportsFiles flag marks a source as backing the file-only config APIs. Only FileSource sets it, which is why the .env cascade and envPaths work on Node and nowhere else.
For fetching that secrets object from 1Password, Vault, Doppler, AWS Secrets Manager, and others, see Using secret stores.
A function can be a source
Some sources read one key at a time and give you no way to list the keys. Pass a (key) => string | undefined reader to useSource and envapt calls it per key on the first read, caching the result. No object to assemble.
declare const : { (: string): string | undefined }; // a keyed config store
.(() => .());
const = .('REGION', .);Combining sources
merge composes several sources into one, read last-wins. A later member overrides an earlier member's key. Keep the .env cascade and layer fetched secrets on top in a single bind, with no write to process.env.
declare const : <string, string>; // fetched and awaited
.((new (), new ()));
const = .('DATABASE_URL', .);At most one member is filesystem-backed, and the .env cascade and file APIs route to it. merge throws InvalidMergedSource with no members or more than one file-backed member.
A member can also be a (key) => string | undefined reader, so a keyed store fills any key the .env files do not define.
declare const : { (: string): string | undefined };
.((new (), () => .()));
const = .('DATABASE_URL', .);When nothing is bound
Reading a value before you call useSource throws EnvaptError with code NoSourceBound. It cannot happen on Node, where the source is bound on import.
The file-only config APIs need a source that backs the filesystem. On the portable build they warn and no-op by default, see Compatibility for fileApiMode.
On the portable build there is no filesystem, so pass your values through PortableSource. An unbound read
throws NoSourceBound.
For the per-runtime flows see Workers and Browser; the build selection is in Compatibility.