seniority
API reference

seniority

Every export of seniority, with its signature and doc comment: explain, ConfigError, envBoolean, envName, ORDER, RANK and 34 more, plus 31 types.

seniority — which source outranks the others.

One resolution for flags, environment, config files, a package.json field and declared defaults, in a fixed order, with provenance: every value can say where it came from. That last part is the whole reason this is a package rather than a function — --explain is only trustworthy if the thing that picked the value is the thing that reports it.

flag  >  env  >  config file  >  package.json field  >  default

The order is not configurable. A precedence a program can rearrange is a precedence nobody can reason about from the outside.

resolve is pure — layers in, values and provenance out — so it can be tested without a filesystem, an environment, or a process. discover is the half that does touch the disk, and it is a separate import for exactly that reason.

Zero dependencies; Node builtins only.

import { explain, ConfigError, envBoolean, … } from 'seniority';

Functions

check

The throwing form: the resolved values, or one CONFIG-class error listing every violation. validate is the record; this is the rendering a program that just wants to exit reaches for (E1, E3 — a message and a hint, never a stack).

function check(shape: Record<string, Shape>, resolution: Resolution): Record<string, unknown>;
ParameterType
shapeRecord<string, Shape>
resolutionResolution

Returns Record<string, unknown>

decodeFileContent

A config file saved as UTF-16 — PowerShell's Out-File default on Windows — is not valid UTF-8, and reading it as UTF-8 produces text no parser can use. A UTF-8 BOM is deliberately left where it is: that is what 10.0.1 does, and one of its four cases asserts it.

function decodeFileContent(buffer: Buffer): string;
ParameterType
bufferBuffer

Returns string

explanationEvent

The agent rendering: the family's { event, data } envelope over the same projection.

function explanationEvent(e: Explanation): ExplanationEvent;
ParameterType
eExplanation

Returns ExplanationEvent

explanationJson

The --json rendering: the same record, with set spelled out rather than inferred from a missing key.

function explanationJson(e: Explanation): ExplanationJson;
ParameterType
eExplanation

Returns ExplanationJson

getDefaultSearchPlaces

.foorc, .foorc.json, foo.config.js, … — the async explorer's twenty-one places, in order.

function getDefaultSearchPlaces(moduleName: string): string[];
ParameterType
moduleNamestring

Returns string[]

getDefaultSearchPlacesSync

The same list minus every .mjs: a synchronous load cannot import an ES module.

function getDefaultSearchPlacesSync(moduleName: string): string[];
ParameterType
moduleNamestring

Returns string[]

getPropertyByPath

A property name, or a period-delimited path, or an array of names.

The literal key wins: getPropertyByPath(source, 'ant.beetle.cootie') returns source['ant.beetle.cootie'] when that key exists, and only otherwise splits on periods. A name with a period inside a path can therefore only be expressed as an array, which is what the array form is for.

The walk guards undefined and nothing else, as 10.0.1's does. A path through a string reads the string's own properties (packageProp: 'name.length' is a number), and a path through a null — "foo": null under packageProp: 'foo.bar' — throws the TypeError upstream throws, which the loader then annotates with the file. lilconfig.ts reproduces the same throw on purpose; a guard here would make the two façades disagree about one file.

function getPropertyByPath(source: unknown, path: string | readonly string[]): unknown;
ParameterType
sourceunknown
pathstring | readonly string[]

Returns unknown

loaderFor

The loader for a path, or the USAGE refusal that names what is missing.

function loaderFor(filepath: string, loaders?: Readonly<Record<string, Loader>>): Loader;
ParameterType
filepathstring
loaders (optional)Readonly<Record<string, Loader>>

Returns Loader

loadPath

Read one file and parse it. The object check is here rather than in each loader so a caller's loader cannot be the reason a non-object reaches the resolver.

function loadPath(filepath: string, options?: LoadOptions): Promise<Record<string, unknown>>;
ParameterType
filepathstring
options (optional)LoadOptions

Returns Promise<Record<string, unknown>>

The nearest match, or undefined.

Proximity outranks the name. Every name is checked in one directory before the walk steps up, so a .apprc beside you wins over an app.config.json two directories above even when the caller listed the latter first. That is the question an upward walk is being asked; ordering by name instead would answer a different one silently.

function search(names: string | readonly string[], options: SearchOptions): Found | undefined;
ParameterType
namesstring | readonly string[]
optionsSearchOptions

Returns Found \| undefined

searchAll

Every match on the way up, nearest directory first and, within a directory, in the order given.

function searchAll(names: string | readonly string[], options: SearchOptions): Found[];
ParameterType
namesstring | readonly string[]
optionsSearchOptions

Returns Found[]

validate

Every violation, in the order the shape declares its options — never the first one alone. A config file with three mistakes should be fixable in one pass, not three runs.

function validate(shape: Record<string, Shape>, resolution: Resolution): Violation[];
ParameterType
shapeRecord<string, Shape>
resolutionResolution

Returns Violation[]

Classes

LoaderError

A format nobody declared a loader for.

USAGE and not CONFIG on purpose. CONFIG (3) tells the user their configuration file is wrong, and sends them to read a file that may be perfectly good; the mistake is the program's, one line up, where it did not declare the loader it needs. Carrying the code on the error rather than choosing it at the exit is what lets a façade report it correctly without this package owning a process.

class LoaderError extends Error {
    readonly extension: string;
    readonly hint: string;
    readonly exitCode = 2;
    constructor(message: string, extension: string, hint: string);
}

Constants

builtinLoaders

The four Node can read unaided. Frozen: one caller's loaders['.json'] = … must not reach another's.

const defaultLoaders: Readonly<Record<string, Loader>>;

defaultLoaders

The async loader table. Frozen: index.test.ts asserts that deleting a key throws.

const defaultLoaders: Readonly<Record<string, Loader>>;

defaultLoadersSync

The sync table: no .mjs, no .mts, and loadJsSync in place of loadJs.

const defaultLoadersSync: Readonly<Record<string, Loader>>;

globalConfigSearchPlaces

Tried in the user's global config directory, which is always the last directory a global search visits.

const globalConfigSearchPlaces: string[];

globalConfigSearchPlacesSync

The same, minus .mjs.

const globalConfigSearchPlacesSync: string[];

metaSearchPlaces

Where cosmiconfig looks for its own configuration — the cosmiconfig key of a .config/config.* or a package.json. Internal to 10.0.1 and exported here because the meta explorer is the one part of the option pipeline a caller can observe.

const metaSearchPlaces: string[];

NOT_BUNDLED

The formats a caller may supply and this package will not bundle, listed so the refusal can name them and so a reader can see the 747 M/wk that is being declined rather than missed.

const NOT_BUNDLED: readonly string[];

WALK_LIMIT

How many directories one walk may visit.

A number rather than "until the root" alone, because the root is not the only way a walk ends badly: a bind mount, a container overlay or a deliberately deep fixture tree all present as an ancestor chain that is long rather than infinite. Sixty-four is far past any real repository — a monorepo package sits six or seven directories down — and small enough that the worst case is imperceptible.

const WALK_LIMIT = 64;

Interfaces

ExplanationEvent

interface ExplanationEvent {
    event: 'config.explain';
    data: ExplanationJson;
}

ExplanationJson

interface ExplanationJson {
    option: string;
    value?: unknown;
    source?: string;
    location?: string;
    line?: number;
    candidates: ExplanationCandidateJson[];
}

Found

interface Found {
    /** The file, absolute. */
    path: string;
    /** The directory it was found in, absolute. */
    dir: string;
    /** How many steps up from `cwd` — `0` is `cwd` itself. What `--explain` prints as "found N levels up". */
    depth: number;
}

LoadOptions

interface LoadOptions {
    /** Merged over `defaultLoaders`; a caller may add a format or replace a builtin. */
    loaders?: Readonly<Record<string, Loader>>;
}

SearchOptions

interface SearchOptions {
    /** Where the walk starts. The first directory checked, not the first one above it. */
    cwd: string;
    /** The highest directory the walk may look in, inclusive. Defaults to the filesystem root. */
    stopAt?: string;
    /** Directories to visit at most. Defaults to `WALK_LIMIT`; a smaller number wins. */
    limit?: number;
    /** Injected for tests and for a caller with its own filesystem. Defaults to `existsSync`. */
    exists?: (path: string) => boolean;
    /** Injected the same way. Defaults to `realpathSync`, and a path that cannot be resolved is used as written. */
    realpath?: (path: string) => string;
}

Shape

What validation reads from a manifest, and no more.

type is checked when it is one of the four JavaScript kinds and ignored otherwise: a shape that says nothing asserts nothing, which is what lets a richer spec pass through a field this package has never heard of.

interface Shape {
    type?: string;
    choices?: readonly unknown[];
    required?: boolean;
    default?: unknown;
}

Violation

interface Violation {
    key: string;
    /** The whole sentence, ready to print: the rule, then where the value came from. */
    message: string;
    /** What the value should have been — `'a string'`, `'one of fast, safe'`. */
    expected: string;
    value: unknown;
    /** Absent only for a required option that no source set: there is no origin to name. */
    provenance?: Provenance;
}

Types

Loader

What a loader is handed and what it gives back. The same shape cosmiconfig uses, so a caller's loader ports unchanged.

type Loader = (filepath: string, content: string) => unknown;

Re-exported

Documented on the page of the entry point that declares them.

ExportKindDocumented in
candidatesfunctionseniority/config
cosmiconfigfunctionseniority/cosmiconfig
cosmiconfigSyncfunctionseniority/cosmiconfig
deepMergefunctionseniority/config
discoverfunctionseniority/config
envBooleanfunctionseniority/precedence
envNamefunctionseniority/precedence
explainfunctionseniority/explain
explanationfunctionseniority/explain
lineOffunctionseniority/config
loadWithExtendsfunctionseniority/config
renderExplanationfunctionseniority/explain
resolvefunctionseniority/precedence
screamingfunctionseniority/precedence
ConfigErrorclassseniority/precedence
Explorerclassseniority/cosmiconfig
ExplorerSyncclassseniority/cosmiconfig
ORDERconstseniority/precedence
RANKconstseniority/precedence
Candidateinterfaceseniority/precedence
CommonOptionsinterfaceseniority/cosmiconfig
Discoveryinterfaceseniority/config
Explanationinterfaceseniority/explain
Layerinterfaceseniority/precedence
Layersinterfaceseniority/precedence
LoadConfigOptionsinterfaceseniority/config
Loadedinterfaceseniority/config
Optionsinterfaceseniority/cosmiconfig
OptionSpecinterfaceseniority/precedence
Provenanceinterfaceseniority/precedence
PublicExplorerinterfaceseniority/cosmiconfig
PublicExplorerSyncinterfaceseniority/cosmiconfig
Resolutioninterfaceseniority/precedence
SourceLayerinterfaceseniority/precedence
BuiltinSourcetypeseniority/precedence
Configtypeseniority/cosmiconfig
CosmiconfigResulttypeseniority/cosmiconfig
Loaderstypeseniority/cosmiconfig
OptionsSynctypeseniority/cosmiconfig
SearchStrategytypeseniority/cosmiconfig
Sourcetypeseniority/precedence
Transformtypeseniority/cosmiconfig

On this page