npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cogs/config

v0.4.0

Published

Framework-agnostic layered-dotenv loader — resolves the canonical .env file chain, expands ${VAR} references, and folds values into process.env with deploy-injected variables staying authoritative. Dual ESM+CJS so it works from an ESM --import preload or

Readme

@cogs/config

The framework-agnostic loader behind the canonical environment schema. It resolves the layered .env file chain, expands ${VAR} references, and folds the result into process.env — with deploy-injected variables staying authoritative by default. One loader, one precedence, no per-repo forks.

Runtime dependencies: dotenv, dotenv-expand. Ships dual ESM + CJS so it works from an ESM --import preload, a CJS --require hook (dd-trace and other one-off tools that must load before anything else), and a plain require('@cogs/config'). Node 20+ (the ESM --import preload needs ≥ 20.6; the CJS --require path works on any Node 20).

See docs/ENVIRONMENT-CONFIG.md for the full spec and ADR 0001 for the rationale.

Load order

Files are merged low → high precedence (later overrides earlier):

.env                 base defaults        committed, non-secret
.env.local           personal overrides   gitignored   (skipped when NODE_ENV=test)
.env.${ENV}          stage config         committed, non-secret
secretsPath          injected secrets     e.g. secrets.json (optional, parsed as JSON when *.json)
.env.${ENV}.local    stage + personal     gitignored
  • ${VAR} interpolation works across every file (dotenv-expand).
  • Default override: false — a value already present in process.env (K8s ConfigMap, Vercel, CI) beats every file; dotenv files only fill gaps. Pass override: true for tooling that must force file values over the ambient environment.
  • .env.local is skipped when NODE_ENV === 'test' so personal-machine overrides never leak into a deterministic test run.
  • ${ENV} is the stage selector (dev | test | staging | production), orthogonal to NODE_ENV.

API

correctEnv(opts?)

Normalizes NODE_ENV, ENV, and BROWSERSLIST_ENV (reading BABEL_ENV), keeping them mutually consistent. Existing process.env values always win over the provided options. Run it first, before envs().

correctEnv({ nodeEnv: "development", env: "dev" });

envs({ appDirectory, secretsPath?, override?, lint? })

Resolves the file chain above relative to appDirectory, parses + expands each existing file, merges in precedence order, folds the result into process.env, and returns it. .json files (e.g. the secretsPath) are parsed as JSON. lint (default true) emits .env hygiene warnings.

envs({ appDirectory: __dirname });

Lower-level helpers

parse, parseJSON, config (from dotenv-with-expand), the dotenvCheckers / dotenvKeyFixers / dotenvLineFixers hygiene rules, processEnv, and the merge utilities (mergeDefaults, mergeAssign, resolveApp) are all exported from the package root.

Usage

Preload for dev / start scripts

Populate process.env before any application module is evaluated:

// package.json
{
  "scripts": {
    "dev": "NODE_OPTIONS=\"--import @cogs/config/preload\" next dev",
    "start": "NODE_OPTIONS=\"--import @cogs/config/preload\" next start"
  }
}

The ./preload entry side-effect-calls correctEnv() then envs({ appDirectory: process.cwd(), secretsPath }), deriving secretsPath from SECRETS_PATH (${SECRETS_PATH}secrets.json, or '' when unset). Requires Node ≥ 20.6.

CJS preload (--require) — dd-trace and one-off tools

For a CJS --require hook (or an app server-preload.cjs), use the ./register subpath — same side effect, loaded via CommonJS so it can sit alongside dd-trace/init and other tools that must run before the app:

// package.json — order matters: env first, then tracer
{
  "scripts": {
    "start": "NODE_OPTIONS=\"--require @cogs/config/register --require dd-trace/init\" node server.js"
  }
}

Or from a server-preload.cjs that also does other CJS bootstrap:

// server-preload.cjs
require("@cogs/config/register"); // folds the .env chain into process.env
require("dd-trace").init();

require('@cogs/config') also exposes the full API (correctEnv, envs, …) from the CJS build for scripts that need to call the loader directly.

next.config.ts (build time)

Load env at the top of the config so the build sees resolved values:

import { correctEnv, envs } from "@cogs/config";

correctEnv();
envs({ appDirectory: __dirname });

export default {
  /* next config */
};