import { Env } from './Env'; /** * Configuration template with placeholder support */ export class ConfigTemplate { public static readonly Empty: ConfigTemplate = new ConfigTemplate(); private _contentText: string; private _params: string[]; constructor(contentText: string = '') { this._contentText = contentText; this._params = this.extractParams(); } public get content(): string { return this._contentText; } /** * Backward compatibility getter */ public get Params(): string[] { return this.params; } public get params(): string[] { return [...this._params]; } /** * Extracts @placeholder@ patterns from template content */ private extractParams(): string[] { const regex = /@(\w+)@/g; const paramsSet = new Set(); let match; while ((match = regex.exec(this._contentText)) !== null) { paramsSet.add(match[1]); } return Array.from(paramsSet); } } /** * Main configuration container */ export class Config { public static readonly ENV_NAME_PARAM = 'env_name'; public envs: Env[] = []; public template: ConfigTemplate = ConfigTemplate.Empty; /** * Sets environments (backward compatibility) */ public addEnvs(envs: Env[]): void { this.envs = envs; } /** * Sets environments */ public setEnvs(envs: Env[]): void { this.envs = envs; } /** * Sets template content (backward compatibility) */ public addTemplate(content: string): void { this.setTemplate(content); } /** * Sets template content */ public setTemplate(content: string): void { this.template = new ConfigTemplate(content); } /** * Gets template as JSON string */ public getTemplateAsJson(): string { try { return this.template.content; } catch { return '{}'; } } /** * Updates template by adding placeholders for environment params */ public updateTemplateFromEnv(env: Env): void { let templateObj: Record = {}; // Try to parse existing template try { if (this.template.content.trim()) { templateObj = JSON.parse(this.template.content); } } catch { // Start fresh if invalid JSON } // Add placeholders for params that don't exist yet for (const param of env.params) { if (param.name && param.name.trim()) { const placeholder = `@${param.name}@`; if (!this.template.content.includes(placeholder)) { templateObj[`!!! ${param.name}`] = placeholder; } } } this.template = new ConfigTemplate(JSON.stringify(templateObj, null, 4)); } /** * Validates that all template placeholders have corresponding params (backward compatibility) */ public validateParams(): string[] { return this.validatePlaceholders(); } /** * Validates that all template placeholders have corresponding params */ public validatePlaceholders(): string[] { const defaultEnv = this.envs.find(e => e.name === 'DEFAULT'); const customEnvs = this.envs.filter(e => e.name !== 'DEFAULT'); // Collect param names from DEFAULT const defaultParamNames = new Set( defaultEnv?.getParamNames() || [] ); // Collect param names from all custom envs const customParamNames = new Set( customEnvs.flatMap(e => e.getParamNames()) ); // Find missing placeholders const missingParams: string[] = []; for (const placeholder of this.template.params) { if (placeholder === Config.ENV_NAME_PARAM) continue; const inDefault = defaultParamNames.has(placeholder); const inCustom = customParamNames.has(placeholder); if (!inDefault && !inCustom) { missingParams.push(placeholder); } } return missingParams; } /** * Creates a deep copy of the config */ public clone(): Config { const cloned = new Config(); cloned.envs = [...this.envs]; cloned.template = this.template; return cloned; } }