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

@jclem/config

v5.0.4

Published

A configuration library for JavaScript runtimes

Readme

@jclem/config

This is a configuration library for JavaScript runtimes. Inspired by the excellent Viper library for Go, Config allows one to stop relying on reading from unstructured, untyped, and unsafe environment variables for runtime configuration and instead use a structured, typed, and safe configuration schema populated by raw values, configuration files, and the environment, instead.

Config uses Zod for schema validation.

Use

$ bun add @jclem/config
import { ConfigParser, envReader } from "@jclem/config";
import z from "zod";

Bun.env["DATABASE__URL"] = "mysql://localhost:3306/mydb";
Bun.env["DATABASE__POOL_SIZE"] = "10";

// Define a configuration schema using Zod.
const Config = z.object({
  database: z.object({
    url: z.string(),
    poolSize: z.preprocess(
      (v) => (typeof v === "string" ? parseInt(v, 10) : v),
      z.number(),
    ),
  }),
});

export const config = new ConfigParser(Config).read(envReader(Bun.env)).parse();

console.log(config.database.url); // mysql://localhost:3306/mydb
console.log(config.database.poolSize); // 10

Reading Configuration Input

Reading a Raw Value

Config can read configuration input from a raw value by returning it from a reader function:

import { ConfigParser } from "@jclem/config";

const config = new ConfigParser(z.object({ foo: z.string() }))
  .read(() => ({ foo: "bar" }))
  .parse();

console.log(config.foo); // bar

A shortcut for this common case is to use valueReader:

import { ConfigParser, valueReader } from "@jclem/config";

const config = new ConfigParser(z.object({ foo: z.string() }))
  .read(valueReader({ foo: "bar" }))
  .parse();

console.log(config.foo); // bar

Reading a File

Config can read a file by passing a file reader to read:

import { ConfigParser } from "@jclem/config";
import { readFileSync } from "node:fs";

// config.json
// {"foo": "bar"}

const fileReader = (filePath: string) => () => Bun.file(filePath).json();

const config = new ConfigParser(z.object({ foo: z.string() }))
  .read(fileReader("config.json"))
  .parse();

console.log(config.foo); // bar

Reading the Environment

Config can read configuration input from environment variables by calling envReader:

import { ConfigParser, envReader } from "@jclem/config";

Bun.env.FOO = "bar";

const config = newParser(z.object({ foo: z.string() }))
  .read(envReader(Bun.env))
  .parse();

console.log(config.foo); // bar

Note that envReader converts schema paths to double-underscore-separated uppercased environment variable names. So, for example, the schema path database.url would be converted to the environment variable DATABASE__URL and the schema path database.poolSize would be converted to the environment variable DATABASE__POOL_SIZE (capital letters imply a single-underscore separation).

This means that a schema with both database.url and database__url will have both values populated from the same environment variable, DATABASE__URL.

It's relatively straightforward to create a custom reader that converts paths to keys in a different way (for example, to parse command-line flags).

Config provides a function flatReader to easily create a custom reader for these common scenarios. It accepts what it expects to be a flat dictionary of string keys to values and a function that converts schema property paths to keys in the dictionary:

import { ConfigParser, flatReader } from "@jclem/config";

// Converts a path to a flag name (`["foo", "bar"]` -> `"foo-bar"`).
const pathToFlag = (path: string[]) => path.join("-");

const config = newParser(z.object({ foo: z.object({ bar: z.string() }) }))
  .read(flatReader({ "foo-bar": "baz" }, pathToFlag))
  .parse();

console.log(config.foo.bar); // baz

Note that like envReader, flatReader parses the configuration schema and pulls values out of the input, rather than simply converting the input to a value parsed by the schema. This is because one may not want to use an entire runtime environment as an input schema, and because it's also possible to generate values on the fly or lazily using getters.

Configuration Source Precedence

Config will read configuration input in the order in which they were added to the config, with later readers taking precedence over earlier readers. For example:

import { ConfigParser, envReader, valueReader } from "@jclem/config";

const Schema = z.object({
  a: z.string(),
  b: z.string(),
  c: z.string(),
});

const value = { a: "a", b: "b", c: "c" };

// config.json
// {"b": "b from file", "c": "c from file"}

Bun.env.C = "c from env";

const fileReader = (filePath: string) => () => Bun.file(filePath).json();

const config = new ConfigParser(Schema)
  .read(valueReader(value))
  .read(fileReader("config.json"))
  .read(envReader(Bun.env))
  .parse();

console.log(config.a); // a
console.log(config.b); // b from file
console.log(config.c); // c from env