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

@beecode/msh-env

v2.0.3

Published

[![Build Status](https://beecode.semaphoreci.com/badges/msh-env/branches/main.svg?style=shields)](https://beecode.semaphoreci.com/projects/msh-env) [![codecov](https://codecov.io/gh/beecode-rs/msh-env/branch/main/graph/badge.svg?token=fHc0YaxEiB)](https:/

Readme

Build Status codecov GitHub license
NPM

msh-env

Micro-service helper for Node.js environment variable validation and typing.

This library provides type-safe environment variable access with validation, default values, and flexible configuration strategies.

Table of Contents

Install

npm i @beecode/msh-env

Usage

Basic Example

import { mshEnvResolver, mshEnv } from '@beecode/msh-env'

const env = mshEnv()

export const config = mshEnvResolver({
	apiKey: env('API_KEY').string,                       // required (default)
	port: env('PORT').number,

	debugMode: env('DEBUG_MODE').boolean.optional,       // boolean | undefined

	logLevel: env('LOG_LEVEL').string.default('info'),
	maxRetries: env('MAX_RETRIES').number.default(3),

	dbPassword: env('DB_PASSWORD').base64.default(''),

	featureFlags: env('FEATURE_FLAGS').json<{ darkMode: boolean }>().default({ darkMode: false }),
})

Resolution & errors

Building a value is lazy — chaining a converter like .string or .number only describes how to read the variable; nothing is resolved yet. Resolution happens in one of two ways:

  • mshEnvResolver({ ... }) — the headline form. Pass a config object whose leaves are builders (and/or nested objects / arrays of builders). It walks the whole tree, resolves every leaf in one pass, and returns a fully-typed, deeply-frozen, readonly object. If any leaf fails, it collects all errors and throws a single MshEnvResolverError whose message lists every failing var with its dotted path.
  • .value — the single terminator for a standalone one-off read (env('API_KEY').string.value). It returns a plain, non-frozen value (see Standalone reads).

Within a builder:

  • Required is the default. A bare builder like env('API_KEY').string is required.
  • .optional is a getter (no parens) that marks the var optional; the resolved type becomes T | undefined.
  • .default(v) is a method that sets a fallback and returns the builder (it does not return the value), so you keep chaining.

T is the type produced by the converter: string, number, boolean, the decoded string from .base64, or the generic type passed to .json<T>().

Handling the aggregated error:

import { mshEnvResolver, MshEnvResolverError, mshEnv } from '@beecode/msh-env'

const env = mshEnv()

try {
	const config = mshEnvResolver({
		apiKey: env('API_KEY').string,
		port: env('PORT').number,
		db: {
			host: env('DB_HOST').string,
		},
	})
} catch (e) {
	if (e instanceof MshEnvResolverError) {
		console.error(e.message)
	}
}

The message lists every failure, each on its own line with the dotted path, separated by ->:

msh-env: 3 environment variable(s) failed to resolve:
  • apiKey -> Env[API_KEY] must have value defined
  • port -> Env[PORT] must have value defined
  • db.host -> Env[DB_HOST] "not-a-number" is not a number

Immutability

mshEnvResolver(...) returns a deeply-frozen, readonly object, so the manual Object.freeze({ ... }) wrapper is no longer needed (and would be redundant). .value, used as a standalone terminator for a one-off read, returns a plain, non-frozen value.

Allowed Values

You can restrict a value to a specific set. Chain .allowed(...) before resolution — it validates both the env value and any .default(...) fallback:

import { mshEnvResolver, mshEnv } from '@beecode/msh-env'

const env = mshEnv()

// Inside the resolver (required is the default)
const config = mshEnvResolver({
	logLevel: env('LOG_LEVEL').string.allowed('debug', 'info', 'warn', 'error'),
	environment: env('NODE_ENV')
		.string.allowed('development', 'staging', 'production')
		.default('development'),
})

// Standalone one-off read
const logLevel = env('LOG_LEVEL').string.allowed('debug', 'info', 'warn', 'error').value

Standalone reads

If you don't want the whole-tree resolver, terminate a single builder with .value. The optional and default mode setters still apply:

import { mshEnv } from '@beecode/msh-env'

const env = mshEnv()

// Required (default)
const dbUrl = env('DATABASE_URL').string.value

// Optional
const analyticsId = env('ANALYTICS_ID').string.optional.value

// Default fallback
const nodeEnv = env('NODE_ENV').string.default('development').value

Note that .value returns a plain, non-frozen value; only mshEnvResolver(...) produces the deeply-frozen, readonly object (see Immutability).

API

Type Converters

| Converter | Input | Output | |-----------|-------|--------| | .string | Any string | string | | .number | Numeric string | number | | .boolean | 'true', 'false', '1', '0' | boolean | | .base64 | Base64-encoded string | string (decoded) | | .json<T>() | Valid JSON string | T |

mshEnv Options

mshEnv(params?: {
	locationStrategies?: LocationStrategy[]
	namingStrategies?: NamingStrategy[]
}): MshEnv

| Option | Default | Description | |--------|---------|-------------| | locationStrategies | [new LocationStrategyEnvironment()] | Defines where to look for env values (first match wins) | | namingStrategies | [new NamingStrategySimpleName()] | Defines how env names are transformed |

Strategies

msh-env is built on the Strategy Pattern. Strategies are optional arrays — supply your own to override the defaults, or combine several; the defaults below are used when an option is omitted. Each strategy type is documented in detail in its own page.

Location Strategy

Defines where values are read from. Strategies are tried in order; the first non-undefined result wins.

| Strategy | Import path | |----------|-------------| | LocationStrategyEnvironment (default) | @beecode/msh-env/location-strategy/environment | | LocationStrategyCliArgsMinimist | @beecode/msh-env/location-strategy/cli-args-minimist | | LocationStrategyDockerSecrets | @beecode/msh-env/location-strategy/docker-secrets | | LocationStrategyImportMetaEnv | @beecode/msh-env/location-strategy/import-meta-env | | LocationStrategyCustom | @beecode/msh-env/location-strategy/custom |

➡️ See Location Strategy for usage examples and how to write your own.

Naming Strategy

Defines how the names you pass to env(...) are transformed before lookup, enabling prefixes, suffixes, and namespacing.

| Strategy | Import path | |----------|-------------| | NamingStrategySimpleName (default) | @beecode/msh-env/naming-strategy/simple-name | | NamingStrategyPrefixName | @beecode/msh-env/naming-strategy/prefix-name | | NamingStrategySuffixName | @beecode/msh-env/naming-strategy/suffix-name |

➡️ See Naming Strategy for usage examples, stacking order, and how to write your own.

Logger

msh-env emits debug-level logs while resolving values. Logging is silent by default — call setEnvLogger() from @beecode/msh-env/util/logger with any @beecode/msh-logger logger to turn it on. Do this once at startup, before reading any values.

import { mshEnv } from '@beecode/msh-env'
import { setEnvLogger } from '@beecode/msh-env/util/logger'
import { LogLevel } from '@beecode/msh-logger'
import { PresetConsoleSimpleString } from '@beecode/msh-logger/controller/preset/console-simple-string'

// Human-readable console output for local dev
setEnvLogger(new PresetConsoleSimpleString({ logLevel: LogLevel.DEBUG }))

const env = mshEnv()

Other ready-made loggers from @beecode/msh-logger:

import { PresetConsoleJson } from '@beecode/msh-logger/controller/preset/console-json' // structured JSON
import { PresetPino } from '@beecode/msh-logger/controller/preset/pino'                 // pino
import { PresetVoid } from '@beecode/msh-logger/controller/preset/void'                 // silent (default)

➡️ See Logging for every preset, log levels, and how to build a fully custom logger. Learn more about the logger itself in the msh-logger project.

Architecture

Configuration is built as a tree of lazy EnvType builders: chaining a converter (.string, .number, .json<T>(), …) only describes how a variable should be read, without resolving it. mshEnvResolver(...) then walks the whole tree, resolving every leaf in one pass, aggregating all failures into a single MshEnvResolverError (so you see every missing or invalid variable at once, not just the first), and returns a deeply-frozen, fully-typed, immutable object.

License

MIT