agentsclimarketplace

Config centralization

Skill JimmyBlanquet/project-forge/skills/core/config-centralization

SaaS factory: Next.js starters + spec-kit extensions + Ralph++ autonomous loop. Production-ready in 48h.

Install
npx -y skills add JimmyBlanquet/project-forge --skill config-centralization

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Type-safe, environment-aware configuration management for Next.js/TypeScript applications using a singleton pattern with runtime validation.

SKILL.md

9.9 KB, as published. Nobody here has run it

Config Centralization - Complete Documentation

Overview

The Config Centralization skill provides type-safe, environment-aware configuration management for Next.js/TypeScript applications using a singleton pattern with runtime validation.

Features

  • Type-Safe Configuration: Full TypeScript generics support for compile-time safety
  • Environment Management: Development, staging, production, test environments
  • Runtime Validation: Required field validation with clear error messages at startup
  • Singleton Pattern: Consistent configuration access across your application
  • Default Values: Sensible defaults with environment variable override support
  • Zero Dependencies: Portable implementation with no external dependencies

Installation

Prerequisites

  • TypeScript 5+
  • Node.js 18+
  • (Optional) Next.js 14+ or any TypeScript project

Install Steps

  1. Run the install script from the skill directory:

    bash install.sh
    
  2. The script will copy files to your project:

    • config-manager.ts - Core ConfigManager class
    • types.ts - TypeScript type definitions
    • index.ts - Public API exports

Usage

Basic Usage

import { ConfigManager } from '@/lib/config'

// Get singleton instance
const config = ConfigManager.getInstance()

// Access configuration values
const apiUrl = config.get('API_URL')
const dbUrl = config.get('DATABASE_URL')
const port = config.get('PORT')

// Check if key exists
if (config.has('API_KEY')) {
  const apiKey = config.get('API_KEY')
}

// Get current environment
const env = config.getEnv() // 'development' | 'production' | 'staging' | 'test'

// Get all configuration
const allConfig = config.getAll()

Type-Safe Configuration

import { ConfigManager, createConfigManager } from '@/lib/config'
import type { AppConfig } from '@/lib/config'

// Option 1: Use generic type parameter
const config = ConfigManager.getInstance<AppConfig>()
const apiUrl: string = config.get('API_URL')! // Type-safe access

// Option 2: Use createConfigManager helper
interface MyConfig {
  API_URL: string
  PORT: number
  DATABASE_URL: string
}

const schema: MyConfig = {
  API_URL: '',
  PORT: 0,
  DATABASE_URL: ''
}

const config = createConfigManager(schema)
const url = config.get('API_URL') // Fully typed

Environment-Aware Configuration

// Configuration loads from process.env automatically
// and detects environment from NODE_ENV

const config = ConfigManager.getInstance({
  env: 'production', // Override environment
  defaults: {
    PORT: 3000,
    LOG_LEVEL: 'info',
    API_TIMEOUT: 5000
  }
})

// Environment variables take precedence over defaults
// If PORT is set in process.env, it overrides the default

Required Field Validation

// Validate required fields at application startup
try {
  const config = ConfigManager.getInstance({
    required: ['DATABASE_URL', 'API_URL', 'API_KEY'],
    defaults: {
      PORT: 3000,
      LOG_LEVEL: 'info'
    }
  })

  // Application starts successfully - all required fields present
} catch (error) {
  // Throws: "Missing required configuration: DATABASE_URL, API_URL
  //         Environment: production"
  console.error(error.message)
  process.exit(1)
}

Runtime Configuration Updates

const config = ConfigManager.getInstance()

// Set configuration value (use sparingly - prefer environment variables)
config.set('FEATURE_FLAG_NEW_UI', true)

// Get updated value
const isNewUI = config.get('FEATURE_FLAG_NEW_UI')

Integration Example

// lib/config/index.ts
import { ConfigManager } from './config-manager'
import type { AppConfig } from './types'

// Initialize configuration at app startup
export function initializeConfig() {
  return ConfigManager.getInstance<AppConfig>({
    env: process.env.NODE_ENV as any || 'development',
    required: [
      'DATABASE_URL',
      'API_URL'
    ],
    defaults: {
      PORT: 3000,
      HOST: '0.0.0.0',
      LOG_LEVEL: 'info',
      DB_POOL_MIN: 2,
      DB_POOL_MAX: 10,
      API_TIMEOUT: 5000
    }
  })
}

// app/page.tsx or pages/_app.tsx
import { initializeConfig } from '@/lib/config'

const config = initializeConfig()

export default function App() {
  // Use configuration throughout your app
  const apiUrl = config.get('API_URL')
  // ...
}

Configuration Types

The skill provides predefined TypeScript interfaces:

// BaseConfig - Common configuration
interface BaseConfig {
  NODE_ENV: 'development' | 'production' | 'staging' | 'test'
  PORT?: number
  HOST?: string
  LOG_LEVEL?: 'debug' | 'info' | 'warn' | 'error'
}

// DatabaseConfig - Database settings
interface DatabaseConfig extends BaseConfig {
  DATABASE_URL: string
  DB_POOL_MIN?: number
  DB_POOL_MAX?: number
  DB_SSL?: boolean
}

// APIConfig - API settings
interface APIConfig extends BaseConfig {
  API_URL: string
  API_KEY?: string
  API_TIMEOUT?: number
  API_RETRY_ATTEMPTS?: number
}

// AppConfig - Full application configuration
interface AppConfig extends BaseConfig, DatabaseConfig, APIConfig {
  // Extend with your custom fields
}

Environment Variables

This skill loads configuration from process.env. Set environment variables in:

  • .env.local - Local development (not committed)
  • .env.development - Development defaults
  • .env.production - Production defaults
  • .env.test - Test environment

Example .env.local:

NODE_ENV=development
DATABASE_URL=postgresql://localhost:5432/myapp
API_URL=https://api.example.com
API_KEY=your_api_key_here
PORT=3000
LOG_LEVEL=debug

Note: process.env values are always strings. The ConfigManager preserves types from defaults but returns strings for environment variables.

Best Practices

  1. Initialize Early: Create ConfigManager instance at app startup to catch validation errors early
  2. Use Required Fields: Validate critical configuration with required option to fail fast
  3. Provide Defaults: Supply sensible defaults for optional configuration
  4. Type Your Config: Define TypeScript interfaces for full type safety
  5. Never Commit Secrets: Use .env.local for sensitive data (add to .gitignore)
  6. Prefer Environment Variables: Use config.set() sparingly - environment variables are more maintainable
  7. Singleton Reset: Use ConfigManager.reset() in tests to ensure clean state between test runs

Troubleshooting

Error: "Missing required configuration"

Cause: Required environment variables are not set.

Solution:

  1. Check .env.local file exists and contains required variables
  2. Verify variable names match exactly (case-sensitive)
  3. Check the error message for specific missing fields
  4. Ensure .env.local is loaded (Next.js loads it automatically)

Type Errors with get()

Cause: TypeScript cannot infer the return type.

Solution:

// Add type parameter to getInstance
const config = ConfigManager.getInstance<AppConfig>()

// Or use non-null assertion if you know it exists
const url: string = config.get('API_URL')!

Configuration Values are Strings

Cause: process.env always returns strings, even for numbers.

Solution: Use defaults to set proper types, or parse values:

const config = ConfigManager.getInstance({
  defaults: {
    PORT: 3000 // Number type from default
  }
})

// Or parse manually
const port = parseInt(config.get('PORT') || '3000', 10)

Singleton State in Tests

Cause: Singleton instance persists between tests.

Solution: Reset instance in test setup:

import { beforeEach } from 'vitest'
import { ConfigManager } from '@/lib/config'

beforeEach(() => {
  ConfigManager.reset()
  // Clear test environment variables
  delete process.env.TEST_VAR
})

API Reference

ConfigManager Class

class ConfigManager<T extends ConfigSchema = ConfigSchema> {
  /**
   * Get singleton instance with optional configuration
   */
  static getInstance<T extends ConfigSchema = ConfigSchema>(
    options?: ConfigOptions
  ): ConfigManager<T>

  /**
   * Reset singleton instance (useful for testing)
   */
  static reset(): void

  /**
   * Get configuration value by key
   */
  get<K extends keyof T>(key: K): T[K]

  /**
   * Get all configuration as immutable copy
   */
  getAll(): Readonly<T>

  /**
   * Set configuration value (use sparingly)
   */
  set<K extends keyof T>(key: K, value: T[K]): void

  /**
   * Check if configuration key exists and is defined
   */
  has(key: keyof T): boolean

  /**
   * Get current environment
   */
  getEnv(): string
}

ConfigOptions Interface

interface ConfigOptions {
  // Environment name (default: process.env.NODE_ENV || 'development')
  env?: 'development' | 'production' | 'staging' | 'test'

  // Required configuration fields (validates at startup)
  required?: string[]

  // Default values (applied if not in process.env)
  defaults?: ConfigSchema
}

createConfigManager Helper

function createConfigManager<T extends ConfigSchema>(
  schema: T,
  options?: ConfigOptions
): ConfigManager<T>

Testing

The skill includes comprehensive tests (26 tests, >70% coverage). Run tests:

npm test

Example test patterns:

import { describe, it, expect, beforeEach } from 'vitest'
import { ConfigManager } from './config-manager'

describe('My App Config', () => {
  beforeEach(() => {
    ConfigManager.reset()
    delete process.env.TEST_VAR
  })

  it('should load configuration with required fields', () => {
    process.env.DATABASE_URL = 'postgresql://localhost/test'

    const config = ConfigManager.getInstance({
      required: ['DATABASE_URL']
    })

    expect(config.get('DATABASE_URL')).toBe('postgresql://localhost/test')
  })
})

License

MIT

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.