@breadstone/ziegel-platform-configuration ​
Configuration management and settings handling for the ziegel platform. Provides decorator-based settings management, multiple storage providers, reactive configuration, and type-safe settings for enterprise applications.
Configuration: Enterprise settings management with decorators, multiple storage providers, and reactive configuration updates.
🚀 Overview ​
@breadstone/ziegel-platform-configuration provides:
- Settings Decorators: Decorator-based settings management with automatic persistence
- Multiple Storage Providers: Local storage, session storage, memory storage, and custom providers
- Settings Manager: Centralized settings management and caching
- Reactive Settings: RxJS integration for reactive settings updates
- Namespace Support: Organized settings with namespace decorators
- Type Safety: Strongly typed settings with automatic serialization/deserialization
📦 Installation ​
bash
npm install @breadstone/ziegel-platform-configuration
# or
yarn add @breadstone/ziegel-platform-configuration🧩 Features & Usage Examples ​
Decorator-Based Settings ​
typescript
import { Setting_, SettingNamespace } from '@breadstone/ziegel-platform-configuration';
@SettingNamespace('user-preferences')
class UserPreferences {
@Setting_('theme', 'light')
theme: 'light' | 'dark' = 'light';
@Setting_('language', 'en')
language: string = 'en';
@Setting_('notifications', true)
enableNotifications: boolean = true;
@Setting_('autoSave', false)
autoSave: boolean = false;
}
// Settings are automatically persisted and loaded
const userPrefs = new UserPreferences();
userPrefs.theme = 'dark'; // Automatically saved to storage
console.log(userPrefs.language); // Automatically loaded from storageSettings Manager ​
typescript
import {
SettingsManager,
SettingsManagerLocator,
LocalStorageSettingsProvider
} from '@breadstone/ziegel-platform-configuration';
// Setup settings manager with provider
const provider = new LocalStorageSettingsProvider();
const settingsManager = new SettingsManager(provider);
// Register globally
SettingsManagerLocator.setManager(settingsManager);
// Use settings manager directly
await settingsManager.setSetting('app.theme', 'dark');
const theme = await settingsManager.getSetting<string>('app.theme');Multiple Storage Providers ​
typescript
import {
LocalStorageSettingsProvider,
SessionStorageSettingsProvider,
MemoryStorageSettingsProvider,
SettingsManager
} from '@breadstone/ziegel-platform-configuration';
// Local storage for persistent settings
const localProvider = new LocalStorageSettingsProvider();
const persistentSettings = new SettingsManager(localProvider);
// Session storage for temporary settings
const sessionProvider = new SessionStorageSettingsProvider();
const sessionSettings = new SettingsManager(sessionProvider);
// Memory storage for runtime settings
const memoryProvider = new MemoryStorageSettingsProvider();
const runtimeSettings = new SettingsManager(memoryProvider);
// Configure different settings for different storage types
await persistentSettings.setSetting('user.preferences', userPrefs);
await sessionSettings.setSetting('ui.state', uiState);
await runtimeSettings.setSetting('cache.config', cacheConfig);Custom Settings Provider ​
typescript
import { SettingsProviderBase, ISetting } from '@breadstone/ziegel-platform-configuration';
class DatabaseSettingsProvider extends SettingsProviderBase {
constructor(private db: Database) {
super();
}
async getSetting<T>(key: string): Promise<ISetting<T> | null> {
const result = await this.db.query(
'SELECT value, created_at, updated_at FROM settings WHERE key = ?',
[key]
);
if (!result) return null;
return {
key,
value: JSON.parse(result.value),
createdAt: result.created_at,
updatedAt: result.updated_at
};
}
async setSetting<T>(key: string, value: T): Promise<void> {
const serialized = JSON.stringify(value);
const now = new Date();
await this.db.query(
'INSERT OR REPLACE INTO settings (key, value, created_at, updated_at) VALUES (?, ?, ?, ?)',
[key, serialized, now, now]
);
}
async removeSetting(key: string): Promise<boolean> {
const result = await this.db.query('DELETE FROM settings WHERE key = ?', [key]);
return result.affectedRows > 0;
}
async getAllSettings(): Promise<ISetting<any>[]> {
const results = await this.db.query('SELECT key, value, created_at, updated_at FROM settings');
return results.map(row => ({
key: row.key,
value: JSON.parse(row.value),
createdAt: row.created_at,
updatedAt: row.updated_at
}));
}
}
// Use custom provider
const dbProvider = new DatabaseSettingsProvider(database);
const settingsManager = new SettingsManager(dbProvider);Reactive Settings with RxJS ​
typescript
import { fromSetting } from '@breadstone/ziegel-platform-configuration';
import { map, distinctUntilChanged } from 'rxjs/operators';
@SettingNamespace('app-config')
class AppConfig {
@Setting_('theme', 'light')
theme: 'light' | 'dark' = 'light';
@Setting_('apiUrl', 'https://api.example.com')
apiUrl: string = 'https://api.example.com';
}
const appConfig = new AppConfig();
// Create observable from setting
const theme$ = fromSetting(appConfig, 'theme').pipe(
distinctUntilChanged(),
map(theme => theme === 'dark' ? 'dark-mode' : 'light-mode')
);
// Subscribe to theme changes
theme$.subscribe(cssClass => {
document.body.className = cssClass;
});
// Setting changes automatically emit to subscribers
appConfig.theme = 'dark'; // Triggers theme$ emissionSettings Caching ​
typescript
import { SettingsCache, ISettingsCache } from '@breadstone/ziegel-platform-configuration';
class CachedSettingsManager {
private cache: ISettingsCache;
private manager: SettingsManager;
constructor(manager: SettingsManager) {
this.manager = manager;
this.cache = new SettingsCache();
}
async getSetting<T>(key: string): Promise<T | null> {
// Check cache first
const cached = this.cache.get<T>(key);
if (cached !== null) {
return cached;
}
// Load from manager and cache
const setting = await this.manager.getSetting<T>(key);
if (setting) {
this.cache.set(key, setting);
return setting;
}
return null;
}
async setSetting<T>(key: string, value: T): Promise<void> {
await this.manager.setSetting(key, value);
this.cache.set(key, value);
}
clearCache(): void {
this.cache.clear();
}
}📚 Package Exports ​
typescript
import {
// Reactive Extensions
fromSetting,
// Decorators
Setting_,
SettingNamespace,
// Core Interfaces
ISetting,
ISettingsCache,
ISettingsManager,
// Settings Providers
SettingsProviderBase,
ISettingsProvider,
LocalStorageSettingsProvider,
MemoryStorageSettingsProvider,
SessionStorageSettingsProvider,
// Settings Management
SettingsCache,
SettingsManager,
SettingsManagerLocator,
// Storage Interface
IStorage
} from '@breadstone/ziegel-platform-configuration';📚 API Documentation ​
For detailed API documentation, visit: API Docs
Related Packages ​
- @breadstone/ziegel-platform: Core platform services
- @breadstone/ziegel-core: Foundation utilities
- @breadstone/ziegel-rx: Reactive extensions
- @breadstone/ziegel-platform-caching: Caching infrastructure
License ​
MIT
Issues ​
Please report bugs and feature requests in the Issue Tracker
Part of the ziegel Enterprise TypeScript Framework
// Get all configuration const allConfig = config.getAll();
### Configuration Schema Validation
```typescript
import { ConfigurationSchema, ValidationRule } from '@ziegel/platform-configuration';
const appConfigSchema: ConfigurationSchema<AppConfig> = {
database: {
type: 'object',
required: true,
properties: {
host: {
type: 'string',
required: true,
format: 'hostname'
},
port: {
type: 'number',
required: true,
minimum: 1,
maximum: 65535
},
name: {
type: 'string',
required: true,
minLength: 1,
maxLength: 64
},
credentials: {
type: 'object',
required: true,
properties: {
username: { type: 'string', required: true },
password: { type: 'string', required: true, sensitive: true }
}
}
}
},
api: {
type: 'object',
required: true,
properties: {
baseUrl: {
type: 'string',
required: true,
format: 'url'
},
timeout: {
type: 'number',
default: 5000,
minimum: 1000,
maximum: 30000
},
retries: {
type: 'number',
default: 3,
minimum: 0,
maximum: 10
}
}
},
features: {
type: 'object',
required: true,
properties: {
enableAnalytics: { type: 'boolean', default: true },
enableCaching: { type: 'boolean', default: true },
debugMode: { type: 'boolean', default: false }
}
}
};
// Apply schema to configuration manager
configManager.setSchema(appConfigSchema);
// Validate configuration
const validationResult = configManager.validate();
if (!validationResult.isValid) {
console.error('Configuration validation errors:', validationResult.errors);
}Advanced Features ​
Environment-Specific Configuration ​
typescript
import { EnvironmentConfigurationProvider } from '@ziegel/platform-configuration';
const envProvider = new EnvironmentConfigurationProvider({
environments: {
development: {
sources: [
{ type: 'file', path: './config/dev.json' },
{ type: 'environment', prefix: 'DEV_' }
],
overrides: {
'features.debugMode': true,
'api.timeout': 10000
}
},
production: {
sources: [
{ type: 'file', path: './config/prod.json' },
{ type: 'environment', prefix: 'PROD_' },
{ type: 'vault', url: 'https://vault.company.com' }
],
overrides: {
'features.debugMode': false,
'api.timeout': 3000
},
requiredSecrets: ['database.credentials.password', 'api.apiKey']
},
staging: {
sources: [
{ type: 'file', path: './config/staging.json' },
{ type: 'environment', prefix: 'STAGING_' }
]
}
},
currentEnvironment: process.env.NODE_ENV || 'development'
});
const config = await envProvider.getConfiguration();Hot Reloading ​
typescript
import { HotReloadableConfiguration } from '@ziegel/platform-configuration';
const hotConfig = new HotReloadableConfiguration({
watchFiles: ['./config/*.json'],
watchEnvironment: true,
debounceInterval: 1000,
onConfigChange: (changes) => {
console.log('Configuration changed:', changes);
// Notify application components
eventBus.emit('config:changed', changes);
},
onValidationError: (errors) => {
console.error('Configuration validation failed:', errors);
// Keep old configuration
}
});
// Subscribe to configuration changes
hotConfig.onChange().subscribe(changes => {
if (changes.includes('database.host')) {
// Reconnect to database with new host
databaseService.reconnect();
}
if (changes.includes('api.timeout')) {
// Update HTTP client timeout
httpClient.updateTimeout(config.get('api.timeout'));
}
});Secure Configuration ​
typescript
import { SecureConfigurationManager, EncryptionProvider } from '@ziegel/platform-configuration';
const encryptionProvider = new EncryptionProvider({
algorithm: 'aes-256-gcm',
keyDerivation: 'pbkdf2',
iterations: 100000,
saltLength: 32
});
const secureConfig = new SecureConfigurationManager({
encryptionProvider,
sensitiveFields: [
'database.credentials.password',
'api.apiKey',
'thirdParty.secretKey'
],
keySource: {
type: 'environment',
variable: 'CONFIG_ENCRYPTION_KEY'
}
});
// Encrypted storage and retrieval
await secureConfig.setSecure('database.credentials.password', 'secretPassword');
const password = await secureConfig.getSecure('database.credentials.password');Remote Configuration ​
typescript
import { RemoteConfigurationProvider } from '@ziegel/platform-configuration';
const remoteProvider = new RemoteConfigurationProvider({
sources: [
{
type: 'http',
url: 'https://config.mycompany.com/api/config',
headers: {
'Authorization': `Bearer ${process.env.CONFIG_API_TOKEN}`
},
refreshInterval: 300000, // 5 minutes
timeout: 10000
},
{
type: 'consul',
host: 'consul.mycompany.com',
port: 8500,
prefix: 'myapp/config/',
watch: true
},
{
type: 'etcd',
endpoints: ['http://etcd1:2379', 'http://etcd2:2379'],
prefix: '/myapp/config',
watch: true
}
],
fallbackToLocal: true,
localCachePath: './cache/remote-config.json'
});
// Load remote configuration
const remoteConfig = await remoteProvider.load();
// Watch for remote changes
remoteProvider.watch().subscribe(changes => {
console.log('Remote configuration updated:', changes);
});Configuration Sources ​
File-Based Configuration ​
typescript
import { FileConfigurationSource } from '@ziegel/platform-configuration';
const fileSource = new FileConfigurationSource({
files: [
{
path: './config/default.yaml',
format: 'yaml',
required: true
},
{
path: './config/override.json',
format: 'json',
optional: true,
watch: true
},
{
path: './config/secrets.env',
format: 'env',
encrypted: true
}
],
mergeStrategy: 'deep', // 'shallow', 'deep', 'replace'
interpolation: {
enabled: true,
variables: {
NODE_ENV: process.env.NODE_ENV,
HOME: process.env.HOME
}
}
});
// Example configuration file with interpolation:
// config/default.yaml
/*
database:
host: ${DB_HOST:localhost}
port: ${DB_PORT:5432}
logPath: ${HOME}/logs/app.log
environment: ${NODE_ENV}
*/Environment Variable Configuration ​
typescript
import { EnvironmentConfigurationSource } from '@ziegel/platform-configuration';
const envSource = new EnvironmentConfigurationSource({
prefix: 'MYAPP_',
separator: '__', // For nested properties: MYAPP_DATABASE__HOST
transformKeys: 'camelCase', // 'camelCase', 'snake_case', 'kebab-case'
typeCoercion: {
boolean: ['true', 'false', '1', '0', 'yes', 'no'],
number: true,
array: {
separator: ',',
trimWhitespace: true
}
},
mappings: {
'DATABASE_URL': 'database.connectionString',
'REDIS_URL': 'cache.connectionString'
}
});
// Environment variables:
// MYAPP_DATABASE__HOST=localhost
// MYAPP_DATABASE__PORT=5432
// MYAPP_FEATURES__ENABLE_ANALYTICS=true
// MYAPP_API__ALLOWED_ORIGINS=localhost:3000,example.comDatabase Configuration Source ​
typescript
import { DatabaseConfigurationSource } from '@ziegel/platform-configuration';
const dbSource = new DatabaseConfigurationSource({
connectionString: process.env.CONFIG_DB_URL,
tableName: 'application_config',
keyColumn: 'config_key',
valueColumn: 'config_value',
environmentColumn: 'environment',
versionColumn: 'version',
polling: {
enabled: true,
interval: 60000, // 1 minute
onChange: (changes) => {
console.log('Database configuration changed:', changes);
}
},
caching: {
enabled: true,
ttl: 300000, // 5 minutes
invalidateOnChange: true
}
});Configuration Validation ​
Custom Validators ​
typescript
import { CustomValidator, ValidationContext } from '@ziegel/platform-configuration';
class DatabaseConnectionValidator implements CustomValidator {
async validate(value: any, context: ValidationContext): Promise<boolean> {
if (typeof value !== 'object') return false;
const { host, port, name } = value;
try {
// Test database connection
const connection = await createConnection({ host, port, database: name });
await connection.ping();
await connection.close();
return true;
} catch (error) {
context.addError(`Database connection failed: ${error.message}`);
return false;
}
}
}
class URLValidator implements CustomValidator {
validate(value: any, context: ValidationContext): boolean {
if (typeof value !== 'string') {
context.addError('Value must be a string');
return false;
}
try {
new URL(value);
return true;
} catch {
context.addError('Value must be a valid URL');
return false;
}
}
}
// Register custom validators
configManager.registerValidator('database-connection', new DatabaseConnectionValidator());
configManager.registerValidator('url', new URLValidator());
// Use in schema
const schema = {
database: {
type: 'object',
validator: 'database-connection'
},
api: {
baseUrl: {
type: 'string',
validator: 'url'
}
}
};Conditional Validation ​
typescript
import { ConditionalValidator } from '@ziegel/platform-configuration';
const conditionalValidator = new ConditionalValidator({
rules: [
{
condition: (config) => config.features?.enableSSL === true,
validation: {
'ssl.certificatePath': { required: true, type: 'string' },
'ssl.keyPath': { required: true, type: 'string' }
}
},
{
condition: (config) => config.environment === 'production',
validation: {
'logging.level': { enum: ['warn', 'error'] },
'features.debugMode': { equals: false }
}
},
{
condition: (config) => config.database?.type === 'mongodb',
validation: {
'database.replicaSet': { required: true, type: 'string' },
'database.ssl': { required: true, type: 'boolean' }
}
}
]
});
configManager.addValidator(conditionalValidator);Configuration Templates ​
Template System ​
typescript
import { ConfigurationTemplate, TemplateEngine } from '@ziegel/platform-configuration';
const appTemplate = new ConfigurationTemplate({
name: 'web-application',
description: 'Standard web application configuration',
parameters: {
appName: { type: 'string', required: true },
environment: { type: 'string', enum: ['dev', 'staging', 'prod'] },
databaseType: { type: 'string', enum: ['postgres', 'mysql', 'mongodb'] },
enableRedis: { type: 'boolean', default: false }
},
template: {
application: {
name: '{{appName}}',
environment: '{{environment}}',
port: '{{#if (eq environment "prod")}}80{{else}}3000{{/if}}'
},
database: {
type: '{{databaseType}}',
host: '{{databaseType}}-{{environment}}.company.com',
'{{#if (eq databaseType "postgres")}}': {
port: 5432,
ssl: '{{#if (eq environment "prod")}}true{{else}}false{{/if}}'
},
'{{#if (eq databaseType "mongodb")}}': {
port: 27017,
replicaSet: '{{appName}}-rs'
}
},
'{{#if enableRedis}}': {
redis: {
host: 'redis-{{environment}}.company.com',
port: 6379
}
}
}
});
// Generate configuration from template
const config = appTemplate.generate({
appName: 'my-app',
environment: 'prod',
databaseType: 'postgres',
enableRedis: true
});Configuration Profiles ​
typescript
import { ConfigurationProfile } from '@ziegel/platform-configuration';
const profiles = {
microservice: new ConfigurationProfile({
name: 'microservice',
extends: 'base',
defaults: {
server: { port: 3000 },
logging: { level: 'info' },
monitoring: { enabled: true },
tracing: { enabled: true }
}
}),
webapi: new ConfigurationProfile({
name: 'webapi',
extends: 'microservice',
defaults: {
cors: { enabled: true },
rateLimit: { enabled: true, requests: 100, window: 60000 },
swagger: { enabled: true }
}
}),
worker: new ConfigurationProfile({
name: 'worker',
extends: 'base',
defaults: {
queue: { concurrency: 5 },
logging: { level: 'warn' },
monitoring: { enabled: false }
}
})
};
// Apply profile
const configWithProfile = configManager.applyProfile('webapi');Integration Examples ​
Express.js Integration ​
typescript
import { createConfigMiddleware } from '@ziegel/platform-configuration/express';
const configMiddleware = createConfigMiddleware({
configManager,
exposeEndpoint: '/api/config',
allowedKeys: ['api.version', 'features.*'], // Whitelist exposed config
excludeKeys: ['database.credentials.*'], // Blacklist sensitive config
enableReload: process.env.NODE_ENV === 'development'
});
app.use(configMiddleware);
// Access configuration in route handlers
app.get('/api/status', (req, res) => {
const config = req.config;
res.json({
status: 'ok',
environment: config.get('environment'),
version: config.get('api.version'),
features: config.get('features')
});
});Dependency Injection Integration ​
typescript
import { ConfigurationContainer } from '@ziegel/platform-configuration/di';
const container = new ConfigurationContainer();
// Register configuration-based services
container.register('DatabaseService', (config) => {
return new DatabaseService(config.get('database'));
});
container.register('ApiClient', (config) => {
return new ApiClient({
baseURL: config.get('api.baseUrl'),
timeout: config.get('api.timeout'),
retries: config.get('api.retries')
});
});
container.register('CacheService', (config) => {
if (config.get('features.enableCaching')) {
return new RedisCache(config.get('redis'));
}
return new MemoryCache();
});
// Get configured services
const dbService = container.get('DatabaseService');
const apiClient = container.get('ApiClient');Configuration Testing ​
typescript
import { ConfigurationTestBuilder } from '@ziegel/platform-configuration/testing';
describe('Application Configuration', () => {
let testConfig: ConfigurationManager;
beforeEach(() => {
testConfig = new ConfigurationTestBuilder()
.withDefaults({
database: { host: 'localhost', port: 5432 },
api: { timeout: 5000 }
})
.withEnvironment('test')
.withOverrides({
'features.enableAnalytics': false,
'logging.level': 'error'
})
.build();
});
test('should load test configuration', () => {
expect(testConfig.get('database.host')).toBe('localhost');
expect(testConfig.get('features.enableAnalytics')).toBe(false);
});
test('should validate required configuration', () => {
const result = testConfig.validate();
expect(result.isValid).toBe(true);
});
test('should handle missing configuration gracefully', () => {
expect(() => testConfig.get('nonexistent.key')).toThrow();
expect(testConfig.get('nonexistent.key', 'default')).toBe('default');
});
});Best Practices ​
Configuration Organization ​
typescript
// Organize configuration by domain
interface AppConfiguration {
// Application metadata
app: {
name: string;
version: string;
environment: string;
};
// Server configuration
server: {
port: number;
host: string;
ssl: {
enabled: boolean;
certificatePath?: string;
keyPath?: string;
};
};
// Database configuration
database: {
type: 'postgres' | 'mysql' | 'mongodb';
host: string;
port: number;
name: string;
pool: {
min: number;
max: number;
acquireTimeoutMillis: number;
};
};
// External services
services: {
redis: {
host: string;
port: number;
password?: string;
};
elasticsearch: {
nodes: string[];
auth?: {
username: string;
password: string;
};
};
};
// Feature flags
features: {
enableAnalytics: boolean;
enableCaching: boolean;
enableRateLimiting: boolean;
maxUploadSize: number;
};
}Security Best Practices ​
typescript
// Separate sensitive configuration
const sensitiveConfig = new SecureConfigurationManager({
sensitiveFields: [
'**.password',
'**.secret',
'**.key',
'**.token',
'**.credentials'
],
encryption: {
enabled: true,
algorithm: 'aes-256-gcm'
},
audit: {
enabled: true,
logAccess: true,
logChanges: true
}
});
// Use environment-specific encryption keys
const encryptionKey = process.env.CONFIG_ENCRYPTION_KEY;
if (!encryptionKey) {
throw new Error('CONFIG_ENCRYPTION_KEY environment variable is required');
}Performance Optimization ​
typescript
// Cache frequently accessed configuration
const cachedConfig = new CachedConfigurationManager({
cache: {
enabled: true,
ttl: 300000, // 5 minutes
maxSize: 1000
},
preload: [
'database.*',
'api.*',
'features.*'
]
});
// Use lazy loading for large configurations
const lazyConfig = new LazyConfigurationManager({
loadOnDemand: true,
cacheLoaded: true,
unloadUnused: true,
unloadThreshold: 1000
});Migration Guide ​
From node-config ​
typescript
// Before (node-config)
const config = require('config');
const dbHost = config.get('database.host');
const apiTimeout = config.get('api.timeout');
// After (ziegel-platform-configuration)
import { config } from '@ziegel/platform-configuration';
const dbHost = config.get('database.host');
const apiTimeout = config.get('api.timeout', 5000); // With defaultFrom dotenv ​
typescript
// Before (dotenv)
require('dotenv').config();
const dbHost = process.env.DB_HOST;
const dbPort = parseInt(process.env.DB_PORT || '5432');
// After (ziegel-platform-configuration)
import { ConfigurationManager } from '@ziegel/platform-configuration';
const config = new ConfigurationManager({
sources: [
{ type: 'environment', prefix: '' },
{ type: 'file', path: '.env' }
]
});
const dbHost = config.get('DB_HOST');
const dbPort = config.get('DB_PORT', 5432); // Automatic type conversionAPI Reference ​
For complete API documentation, see the API Reference.
Related Packages ​
ziegel-platform- Core platform servicesziegel-platform-logging- Logging infrastructureziegel-platform-serialization- Data serialization