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

@bernierllc/config-manager

v1.0.5

Published

Environment-based configuration management with validation, hot reloading, and multi-source support

Readme

@bernierllc/config-manager

Environment-based configuration management with validation, hot reloading, and multi-source support.

Features

  • 🔄 Multiple Sources - Environment variables, JSON/YAML files, remote APIs, CLI arguments
  • 🔍 Type Safety - TypeScript interfaces with runtime validation
  • ⚡ Hot Reloading - Automatic config updates without restart
  • 📊 Hierarchical Merging - Override configs by priority
  • ✅ Validation - Schema validation with detailed error messages
  • 🔒 Secret Management - Secure handling of sensitive configuration
  • 💾 Caching - Efficient config caching with TTL
  • 🎛️ Specialized Managers - Database and API-specific configuration helpers

Installation

npm install @bernierllc/config-manager

Quick Start

import { 
  ConfigManager, 
  EnvConfigSource, 
  FileConfigSource 
} from '@bernierllc/config-manager';

interface AppConfig {
  server: {
    port: number;
    host: string;
  };
  database: {
    url: string;
    maxConnections: number;
  };
  auth: {
    jwtSecret: string;
    tokenExpiry: string;
  };
  features: {
    enableAnalytics: boolean;
    enableCache: boolean;
  };
}

const configManager = new ConfigManager<AppConfig>({
  sources: [
    new FileConfigSource('./config/default.json', { priority: 10 }),
    new FileConfigSource(`./config/${process.env.NODE_ENV}.json`, { 
      priority: 20, 
      optional: true 
    }),
    new EnvConfigSource({ prefix: 'APP_', priority: 30 })
  ],
  defaults: {
    server: {
      port: 3000,
      host: '0.0.0.0'
    },
    features: {
      enableAnalytics: false,
      enableCache: true
    }
  },
  required: ['database.url', 'auth.jwtSecret'],
  watch: true
});

const config = await configManager.load();
console.log(`Server starting on ${config.server.host}:${config.server.port}`);

// Get specific values
const port = configManager.get('server').port;
const jwtSecret = configManager.get('auth').jwtSecret;

// Check if key exists
if (configManager.has('features')) {
  const features = configManager.get('features');
}

// Update configuration dynamically
configManager.set('server', { ...config.server, port: 8080 });
configManager.merge({ features: { enableAnalytics: true } });

Configuration Sources

Environment Variables

import { EnvConfigSource } from '@bernierllc/config-manager';

// Basic environment source
const envSource = new EnvConfigSource();

// With prefix filtering
const appEnvSource = new EnvConfigSource({ 
  prefix: 'APP_',
  separator: '_' 
});

// Environment variables like APP_DATABASE_HOST become config.database.host

File Sources

import { FileConfigSource } from '@bernierllc/config-manager';

// JSON configuration
const jsonSource = new FileConfigSource('./config.json');

// YAML configuration
const yamlSource = new FileConfigSource('./config.yaml', {
  optional: true,  // Don't fail if file doesn't exist
  priority: 20
});

// With transformation
const transformSource = new FileConfigSource('./config.json', {
  transform: (data) => ({
    ...data,
    port: parseInt(data.port) // Transform string to number
  })
});

Remote Configuration

import { RemoteConfigSource } from '@bernierllc/config-manager';

const remoteSource = new RemoteConfigSource('https://config.example.com/api/config', {
  headers: {
    'Authorization': `Bearer ${process.env.CONFIG_TOKEN}`
  },
  refreshInterval: 30000, // Refresh every 30 seconds
  retries: 3,
  fallbackData: { 
    feature: { enabled: false } // Used if remote fails
  }
});

Command Line Arguments

import { CliConfigSource } from '@bernierllc/config-manager';

// Parse process.argv
const cliSource = new CliConfigSource();

// With custom args and aliases
const customCli = new CliConfigSource({
  aliases: { p: 'port', h: 'host' },
  transform: {
    timeout: (value) => parseInt(value) * 1000 // Convert to milliseconds
  }
}, ['--port=3000', '-h', 'localhost']);

Schema Validation

const configManager = new ConfigManager<AppConfig>({
  sources: [envSource, fileSource],
  schema: {
    type: 'object',
    properties: {
      server: {
        type: 'object',
        properties: {
          port: { 
            type: 'number', 
            validate: (port) => port > 0 && port < 65536,
            description: 'Server port (1-65535)'
          },
          host: { 
            type: 'string',
            default: 'localhost'
          }
        }
      },
      database: {
        type: 'object',
        properties: {
          url: { 
            type: 'string', 
            required: true,
            validate: (url) => url.startsWith('postgresql://'),
            description: 'PostgreSQL connection URL'
          },
          maxConnections: { 
            type: 'number',
            default: 10,
            validate: (n) => n > 0 && n <= 100
          }
        }
      }
    }
  },
  required: ['database.url'],
  onUpdate: (newConfig, prevConfig) => {
    console.log('Configuration updated');
  },
  onError: (error, sourceName) => {
    console.error(`Config source '${sourceName}' failed:`, error);
  }
});

// Validate configuration
const validation = configManager.validate();
if (!validation.valid) {
  validation.errors.forEach(error => {
    console.error(`${error.path}: ${error.message}`);
  });
}

Hot Reloading

const configManager = new ConfigManager({
  sources: [
    new FileConfigSource('./config.json'),
    new RemoteConfigSource('https://api.example.com/config', {
      refreshInterval: 60000 // Check every minute
    })
  ],
  watch: true, // Enable hot reloading
  onUpdate: async (newConfig, prevConfig) => {
    // Handle configuration changes
    if (newConfig.features.enableNewFeature !== prevConfig.features.enableNewFeature) {
      if (newConfig.features.enableNewFeature) {
        await enableNewFeature();
      } else {
        await disableNewFeature();
      }
    }
  }
});

// Start watching for changes
configManager.startWatching();

// Stop watching when done
process.on('exit', () => {
  configManager.stopWatching();
});

Specialized Configuration Managers

Database Configuration

import { DatabaseConfig, EnvConfigSource } from '@bernierllc/config-manager';

const dbConfig = new DatabaseConfig({
  sources: [new EnvConfigSource({ prefix: 'DB_' })],
  schema: {
    type: 'object',
    properties: {
      host: { type: 'string', required: true },
      port: { type: 'number', default: 5432 },
      database: { type: 'string', required: true },
      username: { type: 'string', required: true },
      password: { type: 'string', required: true },
      ssl: { type: 'boolean', default: false },
      pool: {
        type: 'object',
        properties: {
          min: { type: 'number', default: 2 },
          max: { type: 'number', default: 10 }
        }
      }
    }
  }
});

await dbConfig.load();

// Get connection string
const connectionString = dbConfig.getConnectionString();
// postgresql://user:pass@host:port/database

// Get pool configuration
const poolConfig = dbConfig.getPoolConfig();
// { min: 2, max: 10, acquireTimeoutMillis: 60000, ... }

// Get engine-specific config
const pgConfig = dbConfig.getEngineConfig('postgresql');

API Configuration

import { ApiConfig, FileConfigSource } from '@bernierllc/config-manager';

const apiConfig = new ApiConfig({
  sources: [new FileConfigSource('./api-config.json')]
});

await apiConfig.load();

// Get resolved endpoints (relative URLs combined with baseUrl)
const endpoints = apiConfig.getEndpoints();
// { users: 'https://api.example.com/users', posts: 'https://api.example.com/posts' }

// Get authentication config
const auth = apiConfig.getAuth();
// { type: 'bearer', token: 'abc123' }

// Get request configuration for HTTP client
const requestConfig = apiConfig.getRequestConfig();
// { timeout: 30000, retries: 3, headers: { Authorization: 'Bearer abc123' } }

// Test API connectivity
const results = await apiConfig.testConnectivity();
// [{ endpoint: 'users', success: true }, { endpoint: 'posts', success: false, error: 'timeout' }]

Configuration Export and Debugging

// Export current configuration (with sensitive data redacted)
console.log('JSON:', configManager.export('json'));
console.log('YAML:', configManager.export('yaml'));
console.log('ENV:', configManager.export('env'));

// Debug configuration sources
const sourceInfo = configManager.getSourceInfo();
sourceInfo.forEach(info => {
  console.log(`Source: ${info.name} (priority: ${info.priority})`);
  console.log(`  Status: ${info.loaded ? 'loaded' : 'failed'}`);
  console.log(`  Keys: ${info.keys.join(', ')}`);
  if (info.error) {
    console.log(`  Error: ${info.error}`);
  }
});

Advanced Usage

Custom Configuration Sources

import { ConfigSource, ConfigData } from '@bernierllc/config-manager';

class CustomConfigSource implements ConfigSource {
  name = 'custom';
  priority = 50;

  async load(): Promise<ConfigData> {
    // Your custom loading logic
    return { customKey: 'customValue' };
  }

  watch(callback: (data: ConfigData) => void): void {
    // Optional: implement watching
    setInterval(async () => {
      const data = await this.load();
      callback(data);
    }, 10000);
  }

  stop(): void {
    // Optional: cleanup watching
  }
}

Configuration Transforms

const configManager = new ConfigManager({
  sources: [fileSource],
  transform: {
    // Transform relative URLs to absolute
    'api.endpoints': (endpoints) => {
      const baseUrl = configManager.get('api').baseUrl;
      return Object.fromEntries(
        Object.entries(endpoints).map(([key, url]) => [
          key, 
          url.startsWith('http') ? url : `${baseUrl}${url}`
        ])
      );
    },
    // Parse connection strings
    'database.url': (url) => {
      // Parse and validate database URL
      return new URL(url);
    }
  }
});

Partial Updates

// Validate partial configuration before applying
const partialConfig = { server: { port: 8080 } };
const validation = configManager.validatePartial(partialConfig);

if (validation.valid) {
  configManager.merge(partialConfig);
} else {
  console.error('Invalid partial config:', validation.errors);
}

Error Handling

const configManager = new ConfigManager({
  sources: [
    new RemoteConfigSource('https://unreliable-config-api.com'),
    new FileConfigSource('./fallback-config.json') // Fallback
  ],
  onError: (error, sourceName) => {
    // Log error but continue with other sources
    console.warn(`Config source '${sourceName}' failed: ${error.message}`);
    
    // Could also emit metrics, send alerts, etc.
    metrics.increment('config.source.error', { source: sourceName });
  }
});

try {
  const config = await configManager.load();
} catch (error) {
  // All sources failed
  console.error('Failed to load configuration:', error);
  process.exit(1);
}

Caching

const configManager = new ConfigManager({
  sources: [remoteSource],
  cache: true,
  cacheTTL: 5 * 60 * 1000, // 5 minutes
  onUpdate: (newConfig) => {
    console.log('Configuration cache refreshed');
  }
});

// First load - fetches from sources
const config1 = await configManager.load(); 

// Second load - uses cache (if within TTL)
const config2 = await configManager.load();

// Force reload - bypasses cache
const config3 = await configManager.reload();

TypeScript Support

Full TypeScript support with type-safe configuration access:

interface MyConfig {
  server: {
    port: number;
    host: string;
  };
  database: {
    url: string;
  };
}

const configManager = new ConfigManager<MyConfig>({
  // ... sources
});

await configManager.load();

// Type-safe access
const port: number = configManager.get('server').port;
const host: string = configManager.get('server').host;

// TypeScript will catch invalid keys
// configManager.get('invalid'); // ✗ TypeScript error

Performance

  • Fast Configuration Access: < 1ms for cached configurations
  • Efficient Merging: Optimized deep merge algorithms
  • Smart Caching: TTL-based caching with automatic invalidation
  • Minimal Memory Usage: Efficient data structures and cleanup
  • Hot Reload Optimization: Debounced file watching and change detection

Integration Status

Logger Integration

Status: Optional (recommended for configuration monitoring)

Justification: While this package can function without logging, it's highly recommended to integrate @bernierllc/logger for configuration change monitoring and debugging. Config managers handle sensitive data and configuration changes, and logging these events helps with security auditing, debugging configuration issues, and tracking configuration drift. However, the package is designed to work without logging for environments where logging isn't available.

Pattern: Optional integration - package works without logger, but logger enhances configuration monitoring capabilities.

Example Integration:

import { Logger } from '@bernierllc/logger';
import { ConfigManager } from '@bernierllc/config-manager';

const logger = new Logger({ service: 'config-manager' });
const configManager = new ConfigManager({
  // ... config
});

// Log configuration changes
configManager.on('change', (changes) => {
  logger.info('Configuration changed', { keys: Object.keys(changes) });
});

NeverHub Integration

Status: Optional (recommended for distributed configuration)

Justification: While this package can function without NeverHub, it's highly recommended to integrate @bernierllc/neverhub-adapter for distributed configuration management. Config managers often need to coordinate configuration across services, and NeverHub provides a service mesh for publishing configuration changes that other services can subscribe to. However, the package is designed to work without NeverHub for simpler use cases.

Pattern: Optional integration - package works without NeverHub, but NeverHub enhances distributed configuration capabilities.

Example Integration:

import { NeverHubAdapter } from '@bernierllc/neverhub-adapter';
import { ConfigManager } from '@bernierllc/config-manager';

const neverhub = new NeverHubAdapter({ service: 'config-manager' });
const configManager = new ConfigManager({ /* config */ });

// Publish configuration changes to NeverHub
configManager.on('change', async (changes) => {
  await neverhub.publish('config.changed', {
    service: 'config-manager',
    changes,
    timestamp: Date.now()
  });
});

Docs-Suite Integration

Status: Ready

Format: TypeDoc-compatible JSDoc comments are included throughout the source code. All public APIs are documented with examples and type information.

Security

  • Sensitive Data Protection: Automatic redaction of passwords, keys, tokens
  • Source Validation: Validate configuration sources and data integrity
  • Secure Remote Fetching: Timeout protection and retry limits
  • No Secret Logging: Sensitive values never appear in logs or exports

Dependencies

  • chokidar - File watching for hot reload
  • yaml - YAML configuration file support
  • dotenv - Environment file loading

License

Copyright (c) 2025 Bernier LLC. All rights reserved.