Merge nested plain objects deeply

JavaScript
// Deep merge plain objects into a new value; arrays are replaced, not merged.
function isPlainObject(value) {
  if (value === null || typeof value !== 'object') return false;
  const proto = Object.getPrototypeOf(value);
  return proto === Object.prototype || proto === null;
}

function deepMerge(...sources) {
  const result = {};
  for (const source of sources) {
    if (!isPlainObject(source)) continue;
    for (const [key, value] of Object.entries(source)) {
      const current = result[key];
      result[key] = isPlainObject(current) && isPlainObject(value)
        ? deepMerge(current, value)
        : isPlainObject(value) ? deepMerge({}, value) : value;
    }
  }
  return result;
}

const defaults = { retries: 2, headers: { accept: 'json' }, tags: ['a'] };
const custom = { headers: { 'x-trace': 'on' }, tags: ['b'] };
console.log(deepMerge(defaults, custom));
// { retries: 2, headers: { accept: 'json', 'x-trace': 'on' }, tags: ['b'] }