| /** |
| * Deeply merge two plain objects (or arrays) without mutating the inputs. |
| * |
| * Rules: |
| * - Arrays from the source replace arrays on the target. |
| * - Plain objects are merged recursively. |
| * - Primitive values from the source overwrite the target. |
| */ |
| export function deepMerge(target, source) { |
| if (typeof target !== 'object' || target === null) return source; |
| if (typeof source !== 'object' || source === null) return target; |
| const output = Array.isArray(target) ? target.slice() : { ...target }; |
| Object.keys(source).forEach((key) => { |
| const sourceValue = source[key]; |
| const targetValue = output[key]; |
| if (Array.isArray(sourceValue)) { |
| output[key] = sourceValue.slice(); |
| } else if ( |
| typeof sourceValue === 'object' && |
| sourceValue !== null && |
| typeof targetValue === 'object' && |
| targetValue !== null && |
| !Array.isArray(targetValue) |
| ) { |
| output[key] = deepMerge(targetValue, sourceValue); |
| } else { |
| output[key] = sourceValue; |
| } |
| }); |
| return output; |
| } |