blob: 8d4586b019aa92c5e27820dfaf265cdbb6364865 [file] [log] [blame]
jason westoverd2483622025-08-18 09:26:41 -05001/**
2 * Deeply merge two plain objects (or arrays) without mutating the inputs.
3 *
4 * Rules:
5 * - Arrays from the source replace arrays on the target.
6 * - Plain objects are merged recursively.
7 * - Primitive values from the source overwrite the target.
8 */
9export function deepMerge(target, source) {
10 if (typeof target !== 'object' || target === null) return source;
11 if (typeof source !== 'object' || source === null) return target;
12 const output = Array.isArray(target) ? target.slice() : { ...target };
13 Object.keys(source).forEach((key) => {
14 const sourceValue = source[key];
15 const targetValue = output[key];
16 if (Array.isArray(sourceValue)) {
17 output[key] = sourceValue.slice();
18 } else if (
19 typeof sourceValue === 'object' &&
20 sourceValue !== null &&
21 typeof targetValue === 'object' &&
22 targetValue !== null &&
23 !Array.isArray(targetValue)
24 ) {
25 output[key] = deepMerge(targetValue, sourceValue);
26 } else {
27 output[key] = sourceValue;
28 }
29 });
30 return output;
31}