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

@spirex/config

v1.0.0

Published

Composable, priority-based configuration with typed access, section scoping, lazy fallbacks, and hot reload — combine multiple providers for layered app settings.

Readme

@spirex/config

Composable, priority-based configuration with typed access, section scoping, lazy fallbacks, and hot reload.

npm version License: MIT TypeScript Bundle size

@spirex/config is a lightweight, dependency-free JavaScript/TypeScript configuration library for building layered app settings. Combine multiple configuration providers — defaults, environment variables, file sources, or your own custom implementations — and read values through a clean, typed API. Providers added later override earlier ones, so deployment-specific values naturally take precedence over built-in defaults.

Why use @spirex/config?

  • First-class TypeScript support — full type declarations included out of the box.
  • Layered, priority-based config — stack providers; the last matching value wins.
  • Scoped sections — group related keys with : (colon) separators and nested sections.
  • Lazy fallbacks — defaults can be plain values or factory functions invoked only when needed.
  • Hot reload — refresh every provider at runtime with a single config.reload() call.
  • Custom providers in seconds — implement a two-method IConfigProvider interface.
  • Tiny footprint — ~1.2kb (0.6kb gzip), ~0.7kb (0.4kb gzip) in-memory provider. Tree-shakeable.
  • Zero runtime dependencies.

Installation

Install with your favorite package manager:

# npm
npm install @spirex/config

# yarn
yarn add @spirex/config

# pnpm
pnpm add @spirex/config

Quick start

import { configBuilder } from "@spirex/config";
import { InMemoryConfigProvider } from "@spirex/config/in-memory";

const config = configBuilder()
  .add(
    new InMemoryConfigProvider({
      app: { name: "MyApp" },
      db: { host: "localhost", port: "5432" },
      features: { darkMode: "true" },
    }),
  )
  .build();

const appName = config.getString("app:name");
const darkMode = config.getBoolean("features:darkMode");

// Scoped access automatically prefixes keys with "db:"
const db = config.section("db");
const host = db.getString("host"); // reads "db:host"
const port = db.getInteger("port"); // reads "db:port"

console.log({ appName, darkMode, host, port });

Documentation

configBuilder()

Creates a builder used to register configuration providers.

import { configBuilder } from "@spirex/config";

const builder = configBuilder();

builder.add(provider)

Registers a provider. Multiple providers are queried in order, and the last provider that has a value wins.

import { InMemoryConfigProvider } from "@spirex/config/in-memory";

builder.add(new InMemoryConfigProvider({ host: "fallback" }));
builder.add(new InMemoryConfigProvider({ host: "override" }));

builder.build()

Finalizes the builder, returns a ready-to-use IConfig instance, and calls load() on every registered provider.

const config = builder.build();

Reading values

The returned config object exposes typed getters. If a key is missing and no default is supplied, an error is thrown.

config.getString("app:name"); // "MyApp"
config.getInteger("db:port"); // 5432
config.getFloat("metrics:rate"); // 1.5
config.getBoolean("features:darkMode"); // true | false

Defaults

Provide a fallback value or a lazy factory:

config.getString("missing", "default value");
config.getInteger("missing", () => expensiveDefaultCalculation());

Sections

Use section(path) to scope configuration to a prefix. Nested sections compose automatically.

const db = config.section("db");
const replica = db.section("replica");

replica.getString("host"); // reads "db:replica:host"

You can also map a section to a typed struct:

const dbConfig = config.section("db").map((db) => ({
  host: db.getString("host"),
  port: db.getInteger("port"),
}));

Reloading configuration

Call reload() to re-run load() on every provider. This is useful when configuration can change at runtime.

config.reload();

Built-in provider: InMemoryConfigProvider

Stores key-value pairs in memory. Accepts a Map, a plain object, an array of [key, value] tuples (including deeply nested Object.entries–style arrays), or no initial data.

import { InMemoryConfigProvider } from "@spirex/config/in-memory";

// From a plain object (nested objects are flattened with ":" separator)
const defaults = new InMemoryConfigProvider({
  app: { name: "MyApp" },
  db: { host: "localhost", port: "5432" },
});
// → "app:name" = "MyApp", "db:host" = "localhost", "db:port" = "5432"

// Empty, then populated
const runtime = new InMemoryConfigProvider();
runtime.set("app:name", "UpdatedApp");

// Deeply nested via Object.entries-style arrays
const deep = new InMemoryConfigProvider([
  ["db", [["host", "prod.example.com"], ["port", "5432"]]],
]);
// → "db:host" = "prod.example.com", "db:port" = "5432"

// From a Map
const fromMap = new InMemoryConfigProvider(
  new Map([["app", { name: "MyApp" }]]),
);

Custom providers

Any object implementing IConfigProvider can be added to the builder:

import type { IConfigProvider } from "@spirex/config";

class FileConfigProvider implements IConfigProvider {
  private cache: Map<string, string> = new Map();

  load() {
    // Read file, populate this.cache
  }

  get(key: string): string | undefined {
    return this.cache.get(key);
  }
}

const config = configBuilder()
  .add(new InMemoryConfigProvider({ logLevel: "info" }))
  .add(new FileConfigProvider("config.json"))
  .build();

Using file-based and environment providers

In addition to the built-in InMemoryConfigProvider, a set of optional packages provide ready-to-use providers for common configuration sources:

| Package | Source | |---|---| | @spirex/config-provider-env | Environment variables | | @spirex/config-provider-json | .json files | | @spirex/config-provider-ini | .ini files | | @spirex/config-provider-yaml | .yaml / .yml files |

Install what you need and add it to the builder — all providers share the same IConfigProvider interface and work together seamlessly:

import { EnvConfigProvider } from "@spirex/config-provider-env";
import { JsonConfigProvider } from "@spirex/config-provider-json";

const config = configBuilder()
  .add(new InMemoryConfigProvider({ db: { host: "localhost" } }))
  .add(new JsonConfigProvider("./config.json"))
  .add(new EnvConfigProvider("APP_", true))
  .build();

API reference

| Member | Description | | --- | --- | | configBuilder() | Creates a new IConfigBuilder. | | builder.add(provider) | Appends a provider to the stack. | | builder.build() | Builds the final IConfig and loads providers. | | config.getString(key, def?) | Returns a string value. | | config.getInteger(key, def?) | Returns an integer value. | | config.getFloat(key, def?) | Returns a float value. | | config.getBoolean(key, def?) | Returns a boolean value. | | config.section(path) | Returns a scoped IConfigSection. | | config.reload() | Reloads all providers. | | config.providers | Read-only array of registered providers. | | SECTION_CHAR | The section separator character (:). |

TypeScript

@spirex/config is written in JavaScript and ships with type declarations. No extra @types package is required.

License

@spirex/config is released under the MIT License.