Coming from dotenv
A dotenv alternative with a drop-in path: import dotenv from seniority/dotenv, graded 106 / 141 by dotenv's own test suite (the declined .env.vault is most of the gap) — 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';seniority/dotenv carries dotenv 17's config, 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. config() behaves as dotenv's does when told nothing: it reads
./.env from the working directory into process.env, and it takes dotenv's options —
path (one file or several, a URL, a leading ~), processEnv, encoding, override
and debug — returning { parsed } or { parsed, error } rather than throwing for a missing
file.
There is no dotenv/config preload entry. Where you had import 'dotenv/config' or
node -r dotenv/config, call it once at your entry point:
import { config } from 'seniority/dotenv';
config();Not built, on purpose: decrypt, DOTENV_KEY and the .env.vault format, which dotenv
deprecated in favour of dotenvx, and config's quiet banner and tips.
Is seniority compatible with dotenv?
Not fully, and the number says so. dotenv's own suite, vendored at 17.4.2 and unmodified
apart from the import specifier, runs against seniority/dotenv beside a control that runs
it against real dotenv:
| passing | rate | |
|---|---|---|
seniority/dotenv | 106 / 141 | 75.2% |
| dotenv itself (control) | 141 / 141 | 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 141 is assertions. Of the 35 not passing, the
oracle's row accounts for each: 27 are the vault and decrypt path and 2 are dotenvx tips,
both declined above, and 6 load dotenv's private lib/* modules by path, which the harness
maps to the package root. Below 100%, seniority is never described as dotenv-compatible;
this is a grade.
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 do not use
.env.vaultorDOTENV_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 186 / 243 by cosmiconfig's own suite (YAML is the gap) — 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.