* Merge existing config with template to add missing fields * @param {object} existingConfig - Current config object * @returns {object} Object with { config: mergedConfig, modified: boolean }
(existingConfig)
| 167 | * @returns {object} Object with { config: mergedConfig, modified: boolean } |
| 168 | */ |
| 169 | mergeWithTemplate(existingConfig) { |
| 170 | const examplePath = this.getConfigExamplePath(); |
| 171 | const templateContent = fs.readFileSync(examplePath, 'utf8'); |
| 172 | const templateConfig = JSON.parse(templateContent); |
| 173 | |
| 174 | // Remove comment-only keys from template |
| 175 | delete templateConfig['//comment']; |
| 176 | |
| 177 | // Deep merge to handle nested objects |
| 178 | const mergeResult = this.deepMerge(templateConfig, existingConfig); |
| 179 | const mergedConfig = mergeResult.merged; |
| 180 | const modified = mergeResult.fieldsAdded.length > 0; |
| 181 | |
| 182 | // Log all fields that were added |
| 183 | if (modified) { |
| 184 | mergeResult.fieldsAdded.forEach(fieldPath => { |
| 185 | logger.info({ field: fieldPath }, 'Adding missing config field from template'); |
| 186 | }); |
| 187 | } |
| 188 | |
| 189 | // Preserve UUID if it exists, generate if not |
| 190 | if (existingConfig.uuid) { |
| 191 | mergedConfig.uuid = existingConfig.uuid; |
| 192 | } else { |
| 193 | mergedConfig.uuid = uuidv4(); |
| 194 | logger.info({ uuid: mergedConfig.uuid }, 'Generated new UUID for config'); |
| 195 | // Don't set modified=true here since UUID is expected to be missing on first run |
| 196 | } |
| 197 | |
| 198 | return { config: mergedConfig, modified }; |
| 199 | } |
| 200 | |
| 201 | ensureConfigExists() { |
| 202 | // If config already exists, nothing to do |
no test coverage detected