UNPKG

i18ntk

Version:

i18n Tool Kit - Zero-dependency internationalization toolkit for setup, scanning, analysis, validation, auto translation, fixing, reporting, and runtime translation loading.

350 lines (302 loc) 10.6 kB
/** * Centralized Environment Variable Manager * * This module provides secure access to a fixed allowlist of environment variables. * Only explicitly defined environment variables are accessible, all others are ignored. * No secrets or sensitive data should ever be stored in environment variables. */ const ALLOWED_ENV_VARS = { // Logging and output 'I18NTK_LOG_LEVEL': { default: 'error', validate: (value) => ['error', 'warn', 'info', 'debug', 'silent'].includes(value.toLowerCase()), transform: (value) => value.toLowerCase() }, 'DEBUG_MODE': { default: 'false', validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()), transform: normalizeBooleanString }, 'JSON_LOG': { default: 'false', validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()), transform: normalizeBooleanString }, 'NODE_ENV': { default: 'development', validate: (value) => ['development', 'production', 'test'].includes(value.toLowerCase()), transform: (value) => value.toLowerCase() }, 'CI': { default: 'false', validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()), transform: normalizeBooleanString }, 'CONTINUOUS_INTEGRATION': { default: 'false', validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()), transform: normalizeBooleanString }, 'NO_INTERACTIVE': { default: 'false', validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()), transform: normalizeBooleanString }, 'npm_lifecycle_event': { default: '', validate: (value) => typeof value === 'string', transform: (value) => value.trim() }, 'npm_config_loglevel': { default: '', validate: (value) => typeof value === 'string', transform: (value) => value.trim().toLowerCase() }, 'I18NTK_OUTDIR': { default: './i18ntk-reports', validate: (value) => typeof value === 'string' && value.length > 0, transform: (value) => value.trim() }, // UI and interaction 'I18NTK_UI_LANGUAGE': { default: 'en', validate: (value) => ['en', 'de', 'es', 'fr', 'it', 'pt', 'nl', 'pl', 'sv', 'uk', 'cs', 'tr', 'ru', 'ja', 'ko', 'zh', 'ar', 'hi', 'th', 'vi', 'he', 'el', 'hu'].includes(value.toLowerCase()), transform: (value) => value.toLowerCase() }, 'I18NTK_SILENT': { default: 'false', validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()), transform: normalizeBooleanString }, // Debug and development 'I18NTK_DEBUG_LOCALES': { default: 'false', validate: (value) => ['0', '1', 'true', 'false'].includes(value.toLowerCase()), transform: normalizeBooleanString }, // Runtime configuration 'I18NTK_RUNTIME_DIR': { default: null, validate: (value) => typeof value === 'string', transform: (value) => value.trim() || null }, 'I18NTK_I18N_DIR': { default: null, validate: (value) => typeof value === 'string' && value.length > 0, transform: (value) => value.trim() }, 'I18NTK_SOURCE_DIR': { default: null, validate: (value) => typeof value === 'string' && value.length > 0, transform: (value) => value.trim() }, 'I18NTK_PROJECT_ROOT': { default: null, validate: (value) => typeof value === 'string' && value.length > 0, transform: (value) => value.trim() }, // Framework detection 'I18NTK_FRAMEWORK_PREFERENCE': { default: 'auto', validate: (value) => ['auto', 'vanilla', 'react', 'vue', 'angular', 'svelte', 'i18next', 'nuxt', 'next', 'django', 'flask', 'fastapi', 'spring-boot', 'laravel'].includes(value.toLowerCase()), transform: (value) => value.toLowerCase() }, 'I18NTK_FRAMEWORK_FALLBACK': { default: 'vanilla', validate: (value) => ['vanilla', 'react', 'vue', 'angular', 'svelte', 'i18next', 'nuxt', 'next', 'django', 'flask', 'fastapi', 'spring-boot', 'laravel'].includes(value.toLowerCase()), transform: (value) => value.toLowerCase() }, 'I18NTK_FRAMEWORK_DETECT': { default: 'true', validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()), transform: normalizeBooleanString }, 'I18NTK_DISABLE_AUTOSAVE': { default: 'false', validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()), transform: normalizeBooleanString }, 'I18NTK_INTERNAL_PATH_PREFIXES': { default: '', validate: (value) => typeof value === 'string', transform: (value) => value.trim() }, 'I18NTK_ENABLE_SECURITY_LOGS': { default: 'false', validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()), transform: normalizeBooleanString }, 'TERM_PROGRAM': { default: '', validate: (value) => typeof value === 'string', transform: (value) => value.trim() }, 'WT_SESSION': { default: '', validate: (value) => typeof value === 'string', transform: (value) => value.trim() }, 'PSModulePath': { default: '', validate: (value) => typeof value === 'string', transform: (value) => value.trim() }, 'POWERSHELL_EDITION': { default: '', validate: (value) => typeof value === 'string', transform: (value) => value.trim() } }; function normalizeBooleanString(value) { const lower = String(value || '').toLowerCase(); return lower === 'true' || lower === '1' || lower === 'yes' ? 'true' : 'false'; } // Security: Block access to sensitive environment variables const BLOCKED_PATTERNS = [ /^SECRET/i, /^PASSWORD/i, /^KEY/i, /^TOKEN/i, /^API_KEY/i, /^PRIVATE/i, /^AUTH/i, /^CREDENTIAL/i, /^AWS_/i, /^GITHUB_/i, /^NPM_/i, /^NODE_/i, /^PATH$/, /^HOME$/, /^USER$/, /^USERNAME$/, /^SHELL$/, /^TERM$/, /^DISPLAY$/, /^LANG$/, /^LC_/i ]; class EnvironmentManager { constructor() { this._cache = new Map(); this._validated = new Set(); } /** * Get a validated environment variable value * @param {string} name - Environment variable name * @returns {string|null} - Validated value or null if not allowed */ get(name) { // Only allow explicitly defined variables if (!ALLOWED_ENV_VARS[name]) { return null; } // Check cache first if (this._cache.has(name)) { return this._cache.get(name); } const definition = ALLOWED_ENV_VARS[name]; const rawValue = process.env[name]; // Use default if not set if (rawValue === undefined || rawValue === null || rawValue === '') { this._cache.set(name, definition.default); return definition.default; } // Validate and transform try { const transformed = definition.transform(rawValue); if (!definition.validate(transformed)) { console.warn(`[i18ntk] Invalid value for ${name}: "${rawValue}". Using default: ${definition.default}`); this._cache.set(name, definition.default); return definition.default; } this._cache.set(name, transformed); this._validated.add(name); return transformed; } catch (error) { console.warn(`[i18ntk] Error processing ${name}: ${error.message}. Using default: ${definition.default}`); this._cache.set(name, definition.default); return definition.default; } } getBoolean(name) { const value = this.get(name); if (typeof value === 'boolean') return value; if (value === 'true' || value === '1' || value === 'yes') return true; return false; } /** * Check if an environment variable is allowed * @param {string} name - Environment variable name * @returns {boolean} - True if allowed */ isAllowed(name) { return !!ALLOWED_ENV_VARS[name]; } /** * Get all allowed environment variables with their current values * @returns {Object} - Object with variable names as keys and values */ getAll() { const result = {}; for (const name of Object.keys(ALLOWED_ENV_VARS)) { result[name] = this.get(name); } return result; } /** * Get documentation for all allowed environment variables * @returns {Array} - Array of documentation objects */ getDocumentation() { return Object.entries(ALLOWED_ENV_VARS).map(([name, definition]) => ({ name, default: definition.default, description: this._getDescription(name) })); } /** * Clear the cache (for testing) */ clearCache() { this._cache.clear(); this._validated.clear(); } /** * Check if a variable name matches blocked patterns * @param {string} name - Variable name to check * @returns {boolean} - True if blocked */ isBlocked(name) { return BLOCKED_PATTERNS.some(pattern => pattern.test(name)); } /** * Get human-readable description for an environment variable * @param {string} name - Environment variable name * @returns {string} - Description */ _getDescription(name) { const descriptions = { 'I18NTK_LOG_LEVEL': 'Logging level (error, warn, info, debug, silent)', 'I18NTK_OUTDIR': 'Output directory for reports and generated files', 'I18NTK_UI_LANGUAGE': 'UI language (en, de, es, fr, ru, ja, zh)', 'I18NTK_SILENT': 'Run in silent mode without interactive prompts', 'I18NTK_DEBUG_LOCALES': 'Enable debug logging for locale loading', 'I18NTK_RUNTIME_DIR': 'Custom runtime directory path', 'I18NTK_I18N_DIR': 'Directory containing i18n/locale files', 'I18NTK_SOURCE_DIR': 'Source directory for scanning', 'I18NTK_PROJECT_ROOT': 'Project root directory', 'I18NTK_FRAMEWORK_PREFERENCE': 'Preferred framework (auto, react, vue, etc.)', 'I18NTK_FRAMEWORK_FALLBACK': 'Fallback framework when auto-detection fails', 'I18NTK_FRAMEWORK_DETECT': 'Enable automatic framework detection' }; return descriptions[name] || 'Configuration option'; } } // Create singleton instance const envManager = new EnvironmentManager(); module.exports = { EnvironmentManager, envManager, ALLOWED_ENV_VARS, BLOCKED_PATTERNS };