seniority
API reference

seniority/dotenv

Every export of seniority/dotenv, with its signature and doc comment: parse, populate, config, module.exports, dotenv, plus 3 types.

import dotenv from 'seniority/dotenv';
import { parse, populate, config, … } from 'seniority/dotenv';

Functions

config

Read, parse and populate. Returns { parsed } or { error } and never throws for a missing file — dotenv is loaded at import time, where a throw takes the program down before it can say anything useful.

function config(options?: Partial<ConfigOptions>): ConfigResult;
ParameterType
options (optional)Partial<ConfigOptions>

Returns ConfigResult

parse

Every KEY=value in a .env, as an object. Accepts the Buffer readFileSync hands back.

function parse(src: string | Buffer): Record<string, string>;
ParameterType
srcstring | Buffer

Returns Record<string, string>

populate

Copy parsed into target and return what was actually set. Without override a key the target already holds is left alone — the same rule seniority's own ORDER states as env > config (R1), arrived at independently by dotenv and worth noticing.

Two things are dotenv's and not ours. The parsed check comes first, because that is the one it makes; the guard on target is kept after it, because dotenv reaching hasOwnProperty.call(undefined, …) throws a TypeError about converting undefined, which tells its caller nothing. And debug prints through console.log, which is what _debug does upstream — not a stream this package owns, and not process.stdout, which it may not name (R11). The text names seniority rather than a dotenv version: the suite asserts that something was logged, never what.

function populate(target: Record<string, string | undefined>, parsed: Record<string, string>, options?: PopulateOptions): Record<string, string>;
ParameterType
targetRecord<string, string | undefined>
parsedRecord<string, string>
options (optional)PopulateOptions

Returns Record<string, string>

Constants

default

The default export, declared as dotenv.

The default export, and why a drop-in subpath needs one.

dotenv is CJS: require('dotenv') hands back a plain, mutable module.exports, and its own suite depends on that — test-populate.js opens with sinon.stub(dotenv, 'parse') in a top-level beforeEach. An ES module namespace cannot be stubbed: every property is non-configurable and the object is not extensible, so sinon refuses with ES Modules cannot be stubbed, the beforeEach throws, and every case in the file fails before its first assertion. Measured 2026-09-20: 12 failing entries in the raw TAP against the control's plan of 6, and not one of them reached a populate call.

So the subpath publishes the same shape its incumbent does — one mutable object carrying the three functions — and the host's import declares reexportDefault, which makes the generated shim re-export it under the 'module.exports' name Node's require() of an ES module returns whole. That is the mechanism commander's and yargs' CJS fixtures already run on; dotenv's row simply never declared it. Nothing about seniority changed to make those cases pass — what changed is that the suite can now reach the functions the way it reaches dotenv's.

const dotenv: {
    config: typeof config;
    parse: typeof parse;
    populate: typeof populate;
};

module.exports

The default export, and why a drop-in subpath needs one.

dotenv is CJS: require('dotenv') hands back a plain, mutable module.exports, and its own suite depends on that — test-populate.js opens with sinon.stub(dotenv, 'parse') in a top-level beforeEach. An ES module namespace cannot be stubbed: every property is non-configurable and the object is not extensible, so sinon refuses with ES Modules cannot be stubbed, the beforeEach throws, and every case in the file fails before its first assertion. Measured 2026-09-20: 12 failing entries in the raw TAP against the control's plan of 6, and not one of them reached a populate call.

So the subpath publishes the same shape its incumbent does — one mutable object carrying the three functions — and the host's import declares reexportDefault, which makes the generated shim re-export it under the 'module.exports' name Node's require() of an ES module returns whole. That is the mechanism commander's and yargs' CJS fixtures already run on; dotenv's row simply never declared it. Nothing about seniority changed to make those cases pass — what changed is that the suite can now reach the functions the way it reaches dotenv's.

const dotenv: {
    config: typeof config;
    parse: typeof parse;
    populate: typeof populate;
};

Interfaces

ConfigOptions

interface ConfigOptions extends Omit<PopulateOptions, 'debug'> {
    /** Log what it does, through `console.log`; a string is read as dotenv reads it (`'false'`, `'0'`, … are false). */
    debug?: boolean | string;
    /** One file or several, highest priority first — an earlier file's key is not overwritten by a later one. `./.env` when omitted; a leading `~` is the home directory; a `URL` is read as one. */
    path: string | URL | readonly (string | URL)[];
    /** The object to populate; the process's own environment when omitted, as dotenv does (D-135). */
    processEnv: Record<string, string | undefined>;
    encoding?: BufferEncoding;
}

ConfigResult

interface ConfigResult {
    parsed?: Record<string, string>;
    error?: Error;
}

PopulateOptions

interface PopulateOptions {
    /** Replace a key the target already has. Off by default: the real environment outranks a file. */
    override?: boolean;
    /** Report each key that was already defined, and whether it was overwritten. dotenv's own `_debug`. */
    debug?: boolean;
}

On this page