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 🙏

© 2025 – Pkg Stats / Ryan Hefner

envoyage

v1.6.0

Published

A zero-dependency, type-safe environment variable management library for TypeScript applications

Readme

envoyage

npm version Test Release License: MIT semantic-release: conventionalcommits

A zero-dependency, type-safe environment variable management library for TypeScript applications. envoyage allows you to define environment configurations with multiple resolution strategies while maintaining full type safety across different deployment environments.

envoyage is completely agnostic about how your variables are loaded, it solely provides a safe way to declare your environment structure based on your specific requirements.

📖 Documentation

📚 View Full Documentation →

Complete guides, API reference, and examples are available in our documentation site.

Features

  • Type-Safe Environment Management: Define environments with their data structures and resolution methods with full TypeScript support
  • Multiple Resolution Strategies: Support for hardcoded values, environment variables, secrets, and custom resolution methods
  • Async/Sync Resolution: Handle both synchronous and asynchronous variable resolution (e.g., fetching from external APIs)
  • Dynamic Variables: Support for runtime-provided values through dynamic resolution
  • Environment Registry: Centralized management of multiple environments (local, staging, production, etc.)
  • Variable Registry: Define variables once and configure how they resolve across different environments
  • Merge Support: Combine multiple variable registries for modular configuration
  • Secure Data Handling: Built-in protection against accidental logging of sensitive values through redacted data patterns

Installation

npm install envoyage

Quick Start

import { createEnvironmentRegistry, defineType } from 'envoyage'

// Define your environments
const envReg = createEnvironmentRegistry()
  .addEnv(
    "local",
    defineType<{ env: Record<string, string> }>(),
    (env) => env
      .addResolution("hardcoded", defineType<string>(), (data) => data.payload)
      .addResolution("from-env", defineType<string | undefined>(), (data) =>
        data.envData.env[data.payload ?? data.variableName])
  )
  .addEnv(
    "production",
    defineType<{ secrets: Record<string, string> }>(),
    (env) => env
      .addResolution("hardcoded", defineType<string>(), (data) => data.payload)
      .addResolution("from-secrets", defineType<string | undefined>(), (data) =>
        data.envData.secrets[data.payload ?? data.variableName])
  )

// Define your variables
const varReg = envReg.createVariableRegistry()
  .addVar("DATABASE_URL", (v) => v
    .for("local", "from-env")
    .for("production", "from-secrets"))
  .addVar("IS_PRODUCTION", (v) => v
    .for("local", "hardcoded", "false")
    .for("production", "hardcoded", "true"))

// Create a resolver for a specific environment
const localResolver = varReg.createResolver(
  "local",
  { env: { DATABASE_URL: "localhost:5432/myapp" } }
)

console.log(localResolver.get("DATABASE_URL")) // "localhost:5432/myapp"
console.log(localResolver.get("IS_PRODUCTION")) // "false"

Core Concepts

Environment Registry

An EnvironmentRegistry manages multiple environments, each with their own data structure and resolution methods:

const envReg = createEnvironmentRegistry()
  .addEnv("local", defineType<LocalEnvData>(), configureLocalEnv)
  .addEnv("production", defineType<ProdEnvData>(), configureProdEnv)

Environments

Each environment defines:

  • Name: A unique identifier (e.g., "local", "production")
  • Data Type: The structure of environment-specific data
  • Resolutions: Methods for resolving variable values
.addEnv(
  "workflows",
  defineType<{ githubSecrets: Record<string, string> }>(),
  (env) => env
    .addResolution("hardcoded", defineType<string>(), (data) => data.payload)
    .addResolution("from-github-secrets", defineType<string | undefined>(), (data) =>
      data.envData.githubSecrets[data.payload ?? data.variableName])
    .addResolution("from-aws-secrets", defineType<undefined>(), async (data) => {
      // Async resolution example
      const secret = await fetchFromAWS(data.variableName)
      return secret
    })
)

Variable Registry

A VariableRegistry defines environment variables and how they should be resolved in each environment:

const varReg = envReg.createVariableRegistry()
  .addVar("API_KEY", (v) => v
    .for("local", "from-env")
    .for("production", "from-aws-secrets"))
  .addVar("APP_NAME", (v) => v
    .for("local", "hardcoded", "MyApp-Dev")
    .for("production", "hardcoded", "MyApp"))

Dynamic Variables

For runtime-provided values:

const varReg = envReg.createVariableRegistry()
  .addVar("DOCUMENT_BUCKET", (v) => v
    .dynamicFor("local", "bucketName")
    .for("production", "from-secrets"))

// Provide dynamic data when creating the resolver
const resolver = varReg.createResolver(
  "local",
  envData,
  { bucketName: "my-local-bucket" }
)

Resolvers

Create environment-specific resolvers to access variable values:

const resolver = varReg.createResolver("production", {
  secrets: { API_KEY: "secret-value" }
})

// Get a single variable value
const apiKey = resolver.get("API_KEY") // Type-safe access

// Get all accessible variables
const allValues = await resolver.getAll()
// { API_KEY: "secret-value", APP_NAME: "MyApp", ... }

// Get variables that use a specific resolution in another environment
const hardcodedInLocal = resolver.getAllFor("local", "hardcoded")
// Gets values from production for variables that use "hardcoded" in local

Additional Features

Async Resolution

envoyage automatically handles async resolutions:

// If the resolution is async, the return type becomes Promise<string>
const apiKey = await resolver.get("API_KEY")

Merging Variable Registries

Combine multiple variable registries for modular configuration:

const authVarReg = envReg.createVariableRegistry()
  .addVar("AUTH_SECRET", (v) => v.for("local", "from-env"))

const dbVarReg = envReg.createVariableRegistry()
  .addVar("DATABASE_URL", (v) => v.for("local", "from-env"))

const globalVarReg = envReg.createVariableRegistry()
  .mergeWith(authVarReg)
  .mergeWith(dbVarReg)
  .addVar("APP_VERSION", (v) => v.for("local", "hardcoded", "1.0.0"))

Variable Source Validation

The listVariables method enables writing validation scripts to ensure all required environment variables are properly configured:

// Get all variables that should be in your .env file
const envVars = varReg.listVariables("local", "from-env")

// Example validation script
const validateEnvFile = function () {
  const missingVars = envVars.filter(name => !process.env[name])
  if (missingVars.length > 0)
    throw new Error(
      `Missing required environment variables in .env: ${missingVars.join(", ")}`
    )
}

Dynamic Environment Selection

For runtime environment selection while maintaining type safety:

const resolver = varReg.createDynamicResolver({
  local: [{ env: process.env }],
  production: [{ secrets: await getSecrets() }]
}, () => process.env.NODE_ENV === "production" ? "production" : "local")

// Only variables defined in ALL environments are accessible
const value = resolver.get("SHARED_VARIABLE")

Type Safety

envoyage provides complete type safety across all aspects of environment configuration - from environment names and resolution methods to variable definitions and return types. The TypeScript compiler ensures your environment configuration is valid at compile time.

License

This project is licensed under the MIT License.