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

confederation

v0.0.6

Published

Manage configuration sources in your app like a boss! `.env` files, CLI flags, database rows—into a single, predictable object. Confederation normalizes keys, nests into objects by separators (by default `__` or `--`), and deeply merges sources so your ap

Readme

Confederation

Manage configuration sources in your app like a boss! .env files, CLI flags, database rows—into a single, predictable object. Confederation normalizes keys, nests into objects by separators (by default __ or --), and deeply merges sources so your application can operate with one source of truth.

This zero-dependency helper focuses purely on normalization, nesting, and merging; it intentionally skips schema validation or type coercion. Every emitted value is either an nested object or a string.

We recommend running the unified config output through Zod or another validator/transformer afterwards.

Why another config helper?

Real-world Node.js services rarely rely on just one configuration channel. By the time you mix process.env, .env files, CLI overrides, deployment-specific secrets, and serialized preferences, you need a consistent way to normalize naming conventions and merge them without losing structure. Confederation focuses on this single job:

  • Normalize keys from any naming convention (underscores, dashes, mixed case) into predictable camelCase paths.
  • Nest hierarchical keys via configurable separators (__, --, or your own pattern).
  • Merge multiple sources deeply, while still letting later sources override earlier ones.
  • Type your configuration by passing a generic to unifyConfigurations, so the final object is fully typed in TypeScript.

Installation

npm install confederation

Quick start

Create a list of sources, optionally specify a parser per source, and let Confederation do the rest:

import { parsers, unifyConfigurations, utils } from "confederation";
import { parseArgs } from "node:util";
import dotenv from "dotenv";

type AppConfig = {
    module: {
        variable1: string;
        variable2: string;
        variable3: string;
    };
};

const configurationSources = [
    {
        value: dotenv.config().parsed,
    },
    {
        value: utils.pick(process.env, ["MODULE__VARIABLE2", "MODULE__VARIABLE3"]),
    },
    {
        value: parseArgs({
            args: process.argv.slice(2),
            strict: false,
            allowPositionals: false,
        }).values,
        parser: parsers.dashParser,
    },
];

const config = unifyConfigurations<AppConfig>(configurationSources);

console.log(config.module.variable2);
// → "from-env"

unifyConfigurations will:

  1. Run each source through utils.nestKeyValues using the provided parser (defaults to parsers.underscoreParser).
  2. Merge all normalized objects from left to right using the defaults helper, so later sources win.

Understanding sources

Each entry in the configurationSources array implements the UnifyConfiguration interface:

type UnifyConfiguration = {
    value: Record<string, unknown>;
    parser?: NestKeyValuesOptions;
};
  • value – the raw key/value map from your config source.
  • parser – optional instructions describing how to split and transform keys.

Leave parser undefined to use the default underscore parser (MODULE__FEATURE__FLAG_Amodule.feature.flagA).

Built-in parsers

Confederation ships with two helpers inside parsers:

  • underscoreParsernestifyPattern: "__" (ideal for env vars).
  • dashParsernestifyPattern: "--" (handy for CLI flags like --server--port=8080).

Parsers reuse the NestKeyValuesOptions type, so anything with a nestifyPattern string and a keyTransformer function will work.

Custom parser example

const colonParser: NestKeyValuesOptions = {
    nestifyPattern: "::",
    keyTransformer: (key) => key.toLowerCase(),
};

const config = unifyConfigurations([
    {
        value: { "db::PRIMARY::HOST": "db.internal" },
        parser: colonParser,
    },
]);

Deep merges with defaults

Under the hood, Confederation relies on a small defaults helper that deeply merges plain objects, replaces arrays/classes, and protects against prototype pollution. You can also import it directly:

import { defaults } from "confederation";

const merged = defaults(
    { cache: { ttl: 60 } },
    { cache: { ttl: 120, host: "redis" } },
    { featureFlags: { preview: true } },
);

defaults never mutates its arguments and always returns a new object.

Recommendations

  • Order your sources from lowest to highest precedence (defaults first, env vars last) so the override chain is obvious.
  • Co-locate parser definitions with the source they belong to—CLI args might require a dash parser, while env vars stick with underscores.
  • Validate the unified output with Zod, Valibot, or any other schema validator/transformer before injecting it into the rest of your app.

That’s all you need to federate configuration across your application. Have ideas or found an edge case? Issues and PRs are welcome!