"use strict"; // A strict deep-merging implementation that *only* merges regular objects, and prevents prototype pollution function isObject(value) { // TODO: Disallow special object types, for statically defined values (or just disallow specifying static values in the root schema in dlayer?) return (value != null && typeof value === "object" && !Array.isArray(value)); } module.exports = function deepMerge(a, b) { let merged = Object.create(null); let keys = new Set([ ... Object.keys(a), ... Object.keys(b) ]); for (let key of keys) { // Technically over-blocks *any* 'constructor' key if (key === "__proto__" || key === "constructor") { continue; } let valueA = a[key]; let valueB = b[key]; if (isObject(valueA) && valueB === undefined) { merged[key] = valueA; } else if (isObject(valueB) && valueA === undefined) { merged[key] = valueB; } else if (isObject(valueA) && isObject(valueB)) { merged[key] = deepMerge(valueA, valueB); } else if (!isObject(valueA) && !isObject(valueB)) { merged[key] = valueB ?? valueA; } else { // FIXME: Identifiable error type, and include the error path as well throw new Error("Cannot merge non-object into object"); } } return merged; };