Coming from dotenv
A dotenv alternative with a drop-in path: import dotenv from seniority/dotenv, graded 197 / 197 by dotenv 18's own test suite, level with dotenv itself — then the same package resolves every value across flags, env, config files and defaults, with provenance as text, --json data or an agent event.
seniority is a dotenv alternative that also covers what cosmiconfig, rc and
find-up do beside it: one resolution for flags, environment, config files, a
package.json field and defaults, in a fixed order, with no dependencies.
Migrate from dotenv in one import
- import dotenv from 'dotenv';
+ import dotenv from 'seniority/dotenv';
- import 'dotenv/config';
+ import 'seniority/dotenv/config';npx burgee migrate makes both edits for you.
seniority/dotenv carries dotenv 18's config, configDotenv, parse and populate, as
named exports and on a default export shaped like dotenv's module.exports. parse is
dotenv's line grammar, character for character, and parse(src, { fast: true }) is its
character scanner. config() behaves as dotenv's does when told nothing: it reads ./.env
from the working directory into process.env. It takes dotenv's options — path (one file or
several, a URL, a leading ~), processEnv, encoding, override, debug, quiet and
fast — and their defaults from DOTENV_PATH, DOTENV_QUIET and the rest, or the older
DOTENV_CONFIG_* names. It prints ◇ injected env (n) from .env on stderr unless quiet, and
returns { parsed } or { parsed, error } rather than throwing for a missing file.
seniority/dotenv/configisimport 'dotenv/config'andnode -r dotenv/config: importing it loads the file, quietly unlessDOTENV_QUIET=false.seniority/dotenv/cliisdotenv run. Itsrun(argv)takes what follows the program name —run(['run', '-f', '.env.local', '--', 'node', 'app.js'])— loads the files, starts the command, forwards SIGINT, SIGTERM, SIGHUP and SIGQUIT to it, and exits with its code.
Not built, because dotenv 18 removed them: decrypt, DOTENV_KEY, the .env.vault
format and the log-line tips.
Is seniority compatible with dotenv?
At dotenv 18, yes, by the measure this page uses. dotenv's own suite, vendored at 18.0.9 and
unmodified apart from the import specifier, runs against seniority/dotenv beside a control
that runs it against real dotenv:
| passing | rate | |
|---|---|---|
seniority/dotenv | 197 / 197 | 100.0% |
| dotenv itself (control) | 197 / 197 | 100.0% |
From Compatibility, which npm run compat:page generates from the
oracle's last run; that page is the authority. The suite runs under node-tap, whose plan
counts assertions rather than test cases, so 191 is assertions. They include dotenv run
spawned for real, its signal forwarding, and programs that import 'dotenv/config'.
dotenv 17.4.2's suite is graded too, and not claimed: 107 / 141 against a control of
141 / 141. The 34 are what dotenv 18 changed — the vault and decrypt, the tips, and the log
line moving from console.log to console.error — so burgee migrate leaves a project on
dotenv 17 alone and says so.
The same page grades seniority (cosmiconfig's own suite), seniority/lilconfig and
seniority/rc.
What you gain over dotenv
dotenv puts a file into the environment. seniority resolves the value, and keeps the receipt:
- Provenance on every value.
resolve()returns each option's winning source and every candidate it beat.explain('region', result)printsregion = "eu-2" from config file ./app.config.jsonwith the unset flag and env candidates beneath it. - The same record for agents.
explanationJson()is that record as--jsondata, andexplanationEvent()is it as an agent event — generated by the code that picked the value, so neither can drift from the text. - An order nobody can rearrange.
flag > env > config file > package.json field > default, exported asORDER.populatealready keeps a key the environment holds unless you passoverride;resolvestates that rule once for every source, and a plugin can add a source between them but can never beat the flag the user typed. resolveis pure. No filesystem, noprocess.env: the environment is passed in, so it is testable without a process. Only the drop-in façades reach forprocess.envby default, because their incumbents do.
When to switch from dotenv
- "Why is this set to that?" is a question your users or agents ask, and the answer depends on whether a flag, the environment or a config file won.
- You load
.envand then hand-write the order in which a flag, an env var and a config file override each other. - You are on dotenv 18, or can move to it: 18 already removed
.env.vaultandDOTENV_KEY.
The API, the discovery rules and the other drop-in paths are on seniority.
Coming from cosmiconfig
A cosmiconfig alternative with zero dependencies: import { cosmiconfig } from seniority, graded 242 / 243 by cosmiconfig's own suite, level with cosmiconfig itself — with provenance, so every resolved value can say where it came from, as text, --json data or an agent event.
Coming from rc
An rc alternative with a drop-in path: import rc from seniority/rc, graded 1 / 1 by rc's own test — one exit-code bit, not a case count — then rc's file stack and merge with none of its four dependencies, and a resolver that says where every value came from.