seniority
Coming from…

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.

seniority is a cosmiconfig alternative that also covers what dotenv, 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 cosmiconfig in one import

- import { cosmiconfig } from 'cosmiconfig';
+ import { cosmiconfig } from 'seniority';

The root export carries cosmiconfig's surface: cosmiconfig, cosmiconfigSync, Explorer, ExplorerSync, defaultLoaders, defaultLoadersSync, getDefaultSearchPlaces, globalConfigSearchPlaces and metaSearchPlaces — all three search strategies, both caches, $import, and the meta-config merge.

YAML reads as it does in cosmiconfig. cosmiconfig takes js-yaml for .yaml, .yml and extensionless files; seniority reads them with seniority/yaml, its own parser, loaded on the first YAML file and never before. It returns what js-yaml 5 returns, and its errors carry js-yaml's reason and position. npx burgee migrate makes the import swap for you.

Is seniority compatible with cosmiconfig?

Level with cosmiconfig on its own suite. cosmiconfig's own suite, vendored at 10.0.1 and unmodified apart from the import specifier, runs against seniority beside a control that runs it against real cosmiconfig:

passingrate
seniority242 / 24399.6%
cosmiconfig itself (control)242 / 24399.6%

From Compatibility, which npm run compat:page generates from the oracle's last run; that page is the authority, and it names every case that leaves the gate. The one case not passing fails for cosmiconfig too: the harness reaching for a file path the vendored copy does not have. The XDG global-directory pair, which registers only on Linux, passes for both: a global search ends in $XDG_CONFIG_HOME/<name> when that is set, as cosmiconfig's does. On macOS the pair does not register, and both read 240 of 241. Level with the control is the claim, and it is a grade, not the word compatible.

The same page grades seniority/lilconfig (lilconfig's own suite), seniority/dotenv and seniority/rc.

What you gain over cosmiconfig

cosmiconfig finds a file. 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) prints region = "eu-2" from config file ./app.config.json with the unset flag and env candidates beneath it.
  • The same record for agents. explanationJson() is that record as --json data, and explanationEvent() 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 as ORDER. A plugin can add a source between them and can never beat the flag the user typed — so the agent reading your --schema and the person reading your --help can reason about it from the outside.
  • resolve is pure. No filesystem, no process.env: the environment is passed in, so it is testable without a process.

When to switch from cosmiconfig

  • "Why is this set to that?" is a question your users or agents ask, and nobody can answer it without reading the source.
  • You want config, env and flags in one precedence rather than three hand-written ones.
  • You do not need YAML, or you are content to pass the parser in.

The API, the discovery rules and the other drop-in paths are on seniority.

On this page