[ADD] objectDeepMerge function

Recursively merges properties from source objects into a target object. If a property at the current level is an object,
and both target and source have it, the property is merged. Otherwise, the source property overwrites the target property.

This function does not modify the source objects and prevents prototype pollution by not allowing `__proto__`, `constructor`,
and `prototype` property names.
This commit is contained in:
Peter Pfeufer 2025-08-09 15:54:39 +02:00
parent c1cd7ca64f
commit 099a39a2a2
No known key found for this signature in database
2 changed files with 72 additions and 0 deletions

View File

@ -217,3 +217,53 @@ const fetchPost = async ({
responseIsJson: responseIsJson
});
};
/**
* Recursively merges properties from source objects into a target object. If a property at the current level is an object,
* and both target and source have it, the property is merged. Otherwise, the source property overwrites the target property.
* This function does not modify the source objects and prevents prototype pollution by not allowing __proto__, constructor,
* and prototype property names.
*
* @usage
* ```javascript
* const target = {a: 1, b: {c: 2}};
* const source1 = {b: {d: 3}, e: 4 };
* const source2 = {a: 5, b: {c: 6}};
*
* const merged = objectDeepMerge(target, source1, source2);
*
* console.log(merged); // {a: 5, b: {c: 6, d: 3}, e: 4}
* ```
*
* @param {Object} target The target object to merge properties into.
* @param {...Object} sources One or more source objects from which to merge properties.
* @returns {Object} The target object after merging properties from sources.
*/
function objectDeepMerge (target, ...sources) {
if (!sources.length) {
return target;
}
// Iterate through each source object without modifying the `sources` array.
sources.forEach(source => {
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue; // Skip potentially dangerous keys to prevent prototype pollution.
}
if (!target[key] || !isObject(target[key])) {
target[key] = {};
}
objectDeepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
});
return target;
}

View File

@ -100,3 +100,25 @@ fetchPost({
- `csrfToken`: The CSRF token to include in the request headers.
- `payload`: The data as JS object to send with the request.
- `responseIsJson`: Optional boolean indicating if the response should be parsed as JSON (default is `true`).
### objectDeepMerge()
Recursively merges properties from source objects into a target object. If a property at the current level is an object,
and both target and source have it, the property is merged. Otherwise, the source property overwrites the target property.
This function does not modify the source objects and prevents prototype pollution by not allowing `__proto__`, `constructor`,
and `prototype` property names.
Usage:
```javascript
/* global objectDeepMerge */
const target = {a: 1, b: {c: 2}};
const source1 = {b: {d: 3}, e: 4 };
const source2 = {a: 5, b: {c: 6}};
const merged = objectDeepMerge(target, source1, source2);
console.log(merged); // {a: 5, b: {c: 6, d: 3}, e: 4}
```