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/content-config-manager

v0.1.2

Published

Configuration management for content management suite with file, database, and environment override support

Readme

@bernierllc/content-config-manager

Configuration management for content management suite with file, database, and environment override support.

Overview

This package provides a comprehensive configuration management system for the content management suite. It supports loading configuration from multiple sources with a clear priority order: environment variables > database > config file > defaults. This allows for flexible deployment configurations while maintaining sensible defaults.

Features

  • Multi-Source Configuration: Support for file, database, and environment variable configuration
  • Priority-Based Override: Clear precedence order for configuration sources
  • Configuration Validation: Built-in validation for all configuration options
  • Real-Time Updates: Update configuration at runtime with validation
  • Event System: Comprehensive event system for configuration changes
  • Statistics Tracking: Track configuration usage and validation statistics
  • TypeScript Support: Full type safety and IntelliSense

Installation

npm install @bernierllc/content-config-manager

Quick Start

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

// Create configuration manager
const configManager = new ContentConfigManager({
  configFilePath: './content-management.config.js',
  enableFileConfig: true,
  enableDatabaseConfig: true,
  enableEnvConfig: true,
  envPrefix: 'CONTENT_',
  validation: {
    enabled: true,
    validateOnLoad: true,
    validateOnUpdate: true
  }
});

// Initialize the configuration manager
await configManager.initialize();

// Get current configuration
const config = configManager.getConfig();
console.log('Editorial workflow enabled:', config.editorialWorkflow.enabled);
console.log('Auto-save debounce:', config.autoSave.debounceMs);

// Update configuration
const updateResult = await configManager.updateConfig({
  key: 'editorialWorkflow.enabled',
  value: false,
  reason: 'Disabling editorial workflow for testing',
  userId: 'admin-123'
});

if (updateResult.success) {
  console.log('Configuration updated successfully');
}

Configuration Sources

1. Default Configuration

Built-in sensible defaults for all configuration options.

2. Configuration File

JavaScript configuration file (e.g., content-management.config.js):

module.exports = {
  editorialWorkflow: {
    enabled: true,
    defaultStages: [
      {
        id: 'draft',
        name: 'Draft',
        order: 1,
        isPublishStage: false,
        allowsScheduling: false,
        nextStage: 'review'
      },
      {
        id: 'review',
        name: 'Review',
        order: 2,
        isPublishStage: false,
        allowsScheduling: false,
        nextStage: 'publish'
      },
      {
        id: 'publish',
        name: 'Publish',
        order: 3,
        isPublishStage: true,
        allowsScheduling: true
      }
    ]
  },
  autoSave: {
    enabled: true,
    debounceMs: 2000,
    storageMode: 'both'
  },
  softDelete: {
    enabled: true,
    showDeletedToUsers: false,
    permanentDeleteAfterDays: 30
  },
  contentTypes: {
    enabled: ['text', 'image', 'audio', 'video'],
    custom: []
  },
  neverhub: {
    enabled: true,
    apiUrl: 'http://localhost:3001'
  }
};

3. Database Configuration

Configuration stored in database with override capability:

CREATE TABLE content_config (
  id UUID PRIMARY KEY,
  config_key VARCHAR(255) UNIQUE NOT NULL,
  config_value JSONB NOT NULL,
  updated_at TIMESTAMP DEFAULT NOW(),
  updated_by VARCHAR(255)
);

-- Example database overrides
INSERT INTO content_config (config_key, config_value, updated_by) VALUES
('editorialWorkflow.enabled', 'false', 'admin'),
('autoSave.debounceMs', '500', 'admin'),
('softDelete.permanentDeleteAfterDays', '7', 'admin');

4. Environment Variables

Environment variables with prefix (default: CONTENT_):

# Editorial workflow
CONTENT_EDITORIAL_WORKFLOW_ENABLED=true
CONTENT_EDITORIAL_WORKFLOW_DEFAULT_STAGES='[{"id":"write","name":"Write","order":1,"isPublishStage":false}]'

# Auto-save
CONTENT_AUTOSAVE_ENABLED=true
CONTENT_AUTOSAVE_DEBOUNCE_MS=1000
CONTENT_AUTOSAVE_STORAGE_MODE=both

# Soft delete
CONTENT_SOFT_DELETE_ENABLED=true
CONTENT_SOFT_DELETE_SHOW_DELETED_TO_USERS=false

# NeverHub
CONTENT_NEVERHUB_ENABLED=true
CONTENT_NEVERHUB_API_URL=http://localhost:3001

Configuration Priority

Configuration sources are loaded in the following order (later sources override earlier ones):

  1. Default Configuration - Built-in defaults
  2. Configuration File - content-management.config.js
  3. Database Configuration - Database overrides
  4. Environment Variables - Runtime environment overrides

API Reference

ContentConfigManager

The main configuration manager class.

Constructor

new ContentConfigManager(options?: Partial<ContentConfigManagerOptions>)

Configuration Options

interface ContentConfigManagerOptions {
  configFilePath?: string;              // Path to configuration file
  database?: {                         // Database connection options
    connectionString?: string;
    host?: string;
    port?: number;
    name?: string;
    username?: string;
    password?: string;
  };
  envPrefix?: string;                  // Environment variable prefix
  enableDatabaseConfig?: boolean;      // Whether to enable database config
  enableFileConfig?: boolean;          // Whether to enable file config
  enableEnvConfig?: boolean;           // Whether to enable env config
  validation?: {                       // Validation options
    enabled: boolean;
    validateOnLoad: boolean;
    validateOnUpdate: boolean;
  };
}

Methods

initialize()

Initialize the configuration manager and load all configuration sources.

await configManager.initialize();
getConfig()

Get the current merged configuration.

const config = configManager.getConfig();
getConfigValue(keyPath)

Get a specific configuration value by key path.

const enabled = configManager.getConfigValue('editorialWorkflow.enabled');
const debounceMs = configManager.getConfigValue('autoSave.debounceMs');
updateConfig(operation)

Update configuration at runtime.

const result = await configManager.updateConfig({
  key: 'editorialWorkflow.enabled',
  value: false,
  reason: 'Disabling for maintenance',
  userId: 'admin-123',
  validate: true
});
validateConfig(config?)

Validate configuration against schema.

const validation = configManager.validateConfig();
if (!validation.valid) {
  console.log('Validation errors:', validation.errors);
  console.log('Validation warnings:', validation.warnings);
}

Configuration Schema

interface ContentManagementConfig {
  editorialWorkflow: {
    enabled: boolean;
    defaultStages: EditorialStageConfig[];
    customWorkflows?: EditorialWorkflowConfig[];
  };
  autoSave: {
    enabled: boolean;
    debounceMs: number;
    maxRetries: number;
    storageMode: 'local' | 'server' | 'both';
    backoffConfig: {
      initialDelay: number;
      maxDelay: number;
      factor: number;
    };
  };
  softDelete: {
    enabled: boolean;
    showDeletedToUsers: boolean;
    permanentDeleteAfterDays: number;
  };
  contentTypes: {
    enabled: string[];
    custom: ContentTypeConfig[];
  };
  neverhub: {
    enabled: boolean;
    apiUrl?: string;
    apiKey?: string;
  };
  database: {
    connectionString?: string;
    host?: string;
    port?: number;
    name?: string;
    username?: string;
    password?: string;
  };
  logging: {
    level: 'debug' | 'info' | 'warn' | 'error';
    logToFile: boolean;
    logFilePath?: string;
    logToConsole: boolean;
  };
}

Examples

Basic Configuration Management

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

class ContentManagementApp {
  private configManager: ContentConfigManager;

  constructor() {
    this.configManager = new ContentConfigManager({
      configFilePath: './config/content-management.config.js',
      enableFileConfig: true,
      enableDatabaseConfig: true,
      enableEnvConfig: true,
      envPrefix: 'CONTENT_'
    });

    // Set up event handling
    this.configManager.onEvent((event) => {
      this.handleConfigEvent(event);
    });
  }

  async initialize() {
    await this.configManager.initialize();
    
    const config = this.configManager.getConfig();
    console.log('Configuration loaded:', {
      editorialWorkflow: config.editorialWorkflow.enabled,
      autoSave: config.autoSave.enabled,
      softDelete: config.softDelete.enabled
    });
  }

  async updateEditorialWorkflow(enabled: boolean, userId: string) {
    const result = await this.configManager.updateConfig({
      key: 'editorialWorkflow.enabled',
      value: enabled,
      reason: `Editorial workflow ${enabled ? 'enabled' : 'disabled'}`,
      userId,
      validate: true
    });

    if (result.success) {
      // Notify other services of configuration change
      await this.notifyServices('editorialWorkflow.changed', { enabled });
    }

    return result;
  }

  private handleConfigEvent(event: any) {
    switch (event.type) {
      case 'config_updated':
        console.log(`Configuration updated: ${event.key} = ${event.data.value}`);
        break;
      case 'config_error':
        console.error(`Configuration error: ${event.data.error}`);
        break;
    }
  }
}

Environment-Specific Configuration

class EnvironmentAwareConfigManager {
  constructor(environment: 'development' | 'staging' | 'production') {
    this.configManager = new ContentConfigManager({
      configFilePath: `./config/content-management.${environment}.config.js`,
      enableFileConfig: true,
      enableDatabaseConfig: environment !== 'development',
      enableEnvConfig: true,
      envPrefix: 'CONTENT_',
      validation: {
        enabled: true,
        validateOnLoad: true,
        validateOnUpdate: environment === 'production'
      }
    });
  }

  async getEnvironmentConfig() {
    await this.configManager.initialize();
    
    const config = this.configManager.getConfig();
    
    // Environment-specific overrides
    if (process.env.NODE_ENV === 'production') {
      // Production-specific settings
      config.logging.level = 'warn';
      config.logging.logToConsole = false;
      config.logging.logToFile = true;
    } else if (process.env.NODE_ENV === 'development') {
      // Development-specific settings
      config.logging.level = 'debug';
      config.logging.logToConsole = true;
      config.autoSave.debounceMs = 500; // Faster auto-save in dev
    }

    return config;
  }
}

Database-Driven Configuration

class DatabaseConfigManager {
  constructor() {
    this.configManager = new ContentConfigManager({
      enableFileConfig: true,
      enableDatabaseConfig: true,
      enableEnvConfig: false, // Disable env for database-driven config
      database: {
        host: process.env.DB_HOST,
        port: parseInt(process.env.DB_PORT || '5432'),
        name: process.env.DB_NAME,
        username: process.env.DB_USER,
        password: process.env.DB_PASSWORD
      }
    });
  }

  async updateWorkflowStages(stages: any[], userId: string) {
    const result = await this.configManager.updateConfig({
      key: 'editorialWorkflow.defaultStages',
      value: stages,
      reason: 'Updated workflow stages',
      userId,
      validate: true
    });

    if (result.success) {
      // Update workflow engine with new stages
      await this.workflowEngine.updateStages(stages);
    }

    return result;
  }

  async addCustomContentType(contentType: any, userId: string) {
    const currentConfig = this.configManager.getConfig();
    const customTypes = currentConfig.contentTypes.custom || [];
    
    const result = await this.configManager.updateConfig({
      key: 'contentTypes.custom',
      value: [...customTypes, contentType],
      reason: `Added custom content type: ${contentType.name}`,
      userId,
      validate: true
    });

    return result;
  }
}

Configuration Validation and Error Handling

class ValidatedConfigManager {
  constructor() {
    this.configManager = new ContentConfigManager({
      validation: {
        enabled: true,
        validateOnLoad: true,
        validateOnUpdate: true
      }
    });
  }

  async updateConfigWithValidation(operation: any) {
    // Pre-validate the operation
    const tempConfig = this.createTempConfig(operation);
    const preValidation = this.configManager.validateConfig(tempConfig);
    
    if (!preValidation.valid) {
      return {
        success: false,
        error: `Pre-validation failed: ${preValidation.errors.join(', ')}`,
        validation: preValidation
      };
    }

    // Proceed with update
    const result = await this.configManager.updateConfig({
      ...operation,
      validate: true
    });

    if (!result.success) {
      // Handle validation errors
      this.handleValidationErrors(result.validation);
    }

    return result;
  }

  private createTempConfig(operation: any) {
    const currentConfig = this.configManager.getConfig();
    const tempConfig = { ...currentConfig };
    
    // Apply the operation to temp config
    this.setNestedValue(tempConfig, operation.key, operation.value);
    
    return tempConfig;
  }

  private handleValidationErrors(validation: any) {
    if (validation && !validation.valid) {
      console.error('Configuration validation failed:');
      validation.errors.forEach((error: string) => {
        console.error(`  - ${error}`);
      });
      
      if (validation.warnings.length > 0) {
        console.warn('Configuration warnings:');
        validation.warnings.forEach((warning: string) => {
          console.warn(`  - ${warning}`);
        });
      }
    }
  }

  private setNestedValue(obj: any, keyPath: string, value: any) {
    const keys = keyPath.split('.');
    let current = obj;

    for (let i = 0; i < keys.length - 1; i++) {
      if (!(keys[i] in current)) {
        current[keys[i]] = {};
      }
      current = current[keys[i]];
    }

    current[keys[keys.length - 1]] = value;
  }
}

Configuration Statistics and Monitoring

class ConfigMonitoringManager {
  constructor() {
    this.configManager = new ContentConfigManager();
  }

  getConfigurationReport() {
    const stats = this.configManager.getStatistics();
    const config = this.configManager.getConfig();
    
    return {
      summary: {
        totalEntries: stats.totalEntries,
        lastLoadTime: stats.lastLoadTime,
        lastUpdateTime: stats.lastUpdateTime,
        databaseConnected: stats.databaseStatus.connected
      },
      sources: {
        default: stats.configBySource.default,
        file: stats.configBySource.file,
        database: stats.configBySource.database,
        environment: stats.configBySource.environment
      },
      validation: {
        totalValidations: stats.validationStats.totalValidations,
        successfulValidations: stats.validationStats.successfulValidations,
        failedValidations: stats.validationStats.failedValidations,
        successRate: stats.validationStats.successfulValidations / stats.validationStats.totalValidations
      },
      configuration: {
        editorialWorkflow: config.editorialWorkflow.enabled,
        autoSave: config.autoSave.enabled,
        softDelete: config.softDelete.enabled,
        contentTypes: config.contentTypes.enabled.length,
        neverhub: config.neverhub.enabled
      }
    };
  }

  async monitorConfigurationChanges() {
    this.configManager.onEvent((event) => {
      this.logConfigurationEvent(event);
      
      if (event.type === 'config_updated') {
        this.notifyConfigurationChange(event);
      }
    });
  }

  private logConfigurationEvent(event: any) {
    console.log(`[CONFIG] ${event.type}:`, {
      timestamp: event.timestamp,
      key: event.key,
      userId: event.userId,
      data: event.data
    });
  }

  private notifyConfigurationChange(event: any) {
    // Notify other services of configuration changes
    this.eventBus.publish('config.changed', {
      key: event.key,
      value: event.data.value,
      userId: event.userId,
      timestamp: event.timestamp
    });
  }
}

TypeScript Support

This package is written in TypeScript and provides full type definitions. All interfaces and types are exported for use in your own code.

License

UNLICENSED - See LICENSE file for details.

Contributing

Contributions are welcome! Please see the contributing guidelines for more information.

Support

For support and questions, please open an issue on GitHub.