Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
A toolkit for deep structure manipulations, provides deep merge/clone functionality out of the box, and exposes hooks and custom adapters for more control and greater flexibility.
A toolkit for deep structure manipulations, provides deep merge/clone functionality out of the box, and exposes hooks and custom adapters for more control and greater flexibility.
compression | size |
---|---|
deeply.js | 16.35 kB |
deeply.min.js | 5.36 kB |
deeply.min.js.gz | 1.63 kB |
$ npm install deeply --save
By default it provides interface for immutable operations,
also available via explicit require require('deeply/immutable')
or require('deeply').immutable
property.
Deeply merges two or more objects.
var merge = require('deeply');
var result = merge({a: {a1: 1}}, {a: {a2: 2}}, {b: {b3: 3}});
assert.equal(result, {a: {a1: 1, a2: 2}, b: {b3: 3}});
Due to Prototype Pollution security vulnerability concerns, default behavior of when merging objects is to skip unsafe keys, like __proto__
, please refer to the test/compatability.js file for code examples.
If there is a use case where such behavior is desired, pass allowDangerousObjectKeys
flag to the context to skip keys safety checks.
var merge = require('deeply');
var result;
var context = { allowDangerousObjectKeys: merge.behaviors.allowDangerousObjectKeys };
result = merge.call(context, {}, JSON.parse('{"__proto__": {"a0": true}}'));
// end of the world, cats live with dogs...
As degenerated case of merging one object on itself, it's possible to use deeply as deep clone function.
var merge = require('deeply');
var clone = merge;
var x = {a: {b: {c: 1}}};
var y = clone(x);
y.a.b.c = 2;
assert.equal(x.a.b.c, 1);
By default array treated as primitive values and being replaced upon conflict, for more meaningful array merge strategy, provide one of the pre-built array merge helpers or a custom reduce function within invocation context.
var merge = require('deeply');
var result = merge({ a: { b: [0, 2, 4, {a: 'A'}], c: 'first' }}, { a: {b: [1, 3, 5, {b: 'B'}], d: 'second' }});
assert.equal(result, { a: { b: [1, 3, 5, {b: 'B'}], c: 'first', d: 'second' }});
var merge = require('deeply');
var result;
var context =
{
useCustomAdapters: merge.behaviors.useCustomAdapters,
'array' : merge.adapters.arraysCombine
};
// it might be useful when you have array of objects
result = merge.call(context, { a: { b: [0, {a: 'A1', b: 'B1'}, 4] }}, { a: {b: [1, {a: 'A2', c: 'C2'}, 5] }});
assert.equal(result, { a: { b: [1, {a: 'A2', b: 'B1', c: 'C2'}, 5] }});
var merge = require('deeply');
var context =
{
useCustomAdapters: merge.behaviors.useCustomAdapters,
'array' : merge.adapters.arraysAppend
};
var result = merge.call(context, { a: { b: [0, 2, 4, 4, 2, 0] }}, { a: {b: [1, 3, 5, 5, 3, 1] }});
assert.equal(result, { a: { b: [0, 2, 4, 4, 2, 0, 1, 3, 5, 5, 3, 1] }});
var merge = require('deeply');
var context =
{
useCustomAdapters: merge.behaviors.useCustomAdapters,
'array' : merge.adapters.arraysAppendUnique
};
var result = merge.call(context, { a: { b: [0, 2, 4, 4, 2, 0] }}, { a: {b: [1, 3, 5, 5, 3, 1] }});
assert.equal(result, { a: { b: [0, 2, 4, 1, 3, 5] }});
For example we need to have merging arrays to be appended, with only unique elements and sort the result array.
var merge = require('deeply');
var context =
{
useCustomAdapters: merge.behaviors.useCustomAdapters,
'array' : appendUniqueAndSort
};
var result = merge.call(context, { a: { b: [0, 2, 4, 4, 2, 0] }}, { a: {b: [1, 3, 5, 5, 3, 1] }});
assert.equal(result, { a: { b: [0, 1, 2, 3, 4, 5] }});
// Custom array adapter
function appendUniqueAndSort(to, from, merge)
{
// append only if new element isn't present yet
from.forEach(function(v) { to.indexOf(v) == -1 && to.push(merge(undefined, v)); });
// and sort
return to.sort();
}
By default, functions copied as is to the result object, basically being treated as primitive values.
You can use functionsClone
adapter to clone functions,
creating new function object with the same signature as original.
var clone = require('deeply');
function subj(a, b)
{
return a + b + 10;
}
subj.customProp = 13;
subj.prototype.A = 1;
subj.prototype.B = {isB: 'true'};
var context =
{
useCustomAdapters: clone.behaviors.useCustomAdapters,
'function' : clone.adapters.functionsClone
};
var result = clone.call(context, { a: { b: subj}});
// cloned object function named subj
assert.equal(result, { a: { b: subj}});
// same signature
assert.equal(subj.name, result.a.b.name);
assert.equal(subj.length, result.a.b.length);
assert.equal(subj.customProp, result.a.b.customProp);
// separate objects
subj.isOriginal = true;
result.a.b.isCopy = true;
assert.equal(subj.isOriginal, true);
assert.equal(subj.isCopy, undefined);
assert.equal(result.a.b.isOriginal, undefined);
assert.equal(result.a.b.isCopy, true);
// same output
assert.equal(subj(3, 4), result.a.b(3, 4));
It will also clone prototype objects, so use this option with caution.
var clone = require('deeply');
function Subj()
{
this.boom = 'Zap!';
}
Subj.prototype.A = 1;
Subj.prototype.B = {isB: 'true'};
var context =
{
useCustomAdapters: clone.behaviors.useCustomAdapters,
'function' : clone.adapters.functionsClone
};
var result = clone.call(context, { class: Subj });
// cloned object function named Subj
assert.equal(result, { class: Subj });
// has prototype properties
assert.equal(result.class.prototype.A, 1);
assert.equal(result.class.prototype.B.isB, 'true');
// prototypes are decoupled
Subj.prototype.C = 2;
assert.equal(Subj.prototype.C, 2);
assert.equal(result.class.prototype.C, undefined);
// instances
var s1 = new Subj();
var s2 = new result.class();
assert.equal(s1.A, s2.A);
assert.equal(s1.B, s2.B);
assert.equal(s1.C, 2);
assert.equal(s2.C, undefined);
assert.equal(s1.boom, s2.boom);
// but reported instanceof isn't the same
assert.equal(s1 instanceof Subj, true);
assert.equal(s2 instanceof Subj, false);
When having proper instanceof
results matters,
you can use functionsExtend
helper instead.
var clone = require('deeply');
function Subj()
{
this.boom = 'Zap!';
return this.boom;
}
Subj.customProp = 13;
Subj.prototype.A = 1;
Subj.prototype.B = {isB: 'true'};
var context =
{
useCustomAdapters: clone.behaviors.useCustomAdapters,
'function' : clone.adapters.functionsExtend
};
var result = clone.call(context, { class: Subj });
// cloned object function named Subj
assert.equal(result, { class: Subj });
// same signature
assert.equal(Subj.name, result.class.name);
assert.equal(Subj.length, result.class.length);
assert.equal(Subj.customProp, result.class.customProp);
// separate objects
Subj.isOriginal = true;
result.class.isCopy = true;
assert.equal(Subj.isOriginal, true);
assert.equal(Subj.isCopy, undefined);
assert.equal(result.class.isOriginal, undefined);
assert.equal(result.class.isCopy, true);
// same output
assert.equal(Subj(), result.class());
// has prototype properties
assert.equal(result.class.prototype.A, 1);
assert.equal(result.class.prototype.B.isB, 'true');
// prototypes are extended
Subj.prototype.X = 67;
assert.equal(Subj.prototype.X, 67);
assert.equal(result.class.prototype.X, 67);
// instances
var s1 = new Subj();
var s2 = new result.class();
assert.equal(s1.A, s2.A);
assert.equal(s1.B, s2.B);
assert.equal(s1.boom, s2.boom);
// but reported instanceof isn't the same
assert.equal(s1 instanceof Subj, true);
assert.equal(s2 instanceof Subj, true);
allowDangerousObjectKeys
As shown in (Security Concerns)[#security-concerns] section,
you can skip safety checks for unsafe object keys (e.g. __proto__
) by passing allowDangerousObjectKeys
flag to the context.
merge.call({ allowDangerousObjectKeys: merge.behaviors.allowDangerousObjectKeys }, {}, JSON.parse('{"__proto__": {"a0": true}}'));
useCustomAdapters
As shown in Custom Merge Function example, you can add custom adapters for any data type that supported by precise-typeof.
For this example we will combine arrays of number, by performing addition operation on array elements.
var merge = require('deeply');
var context =
{
useCustomAdapters: merge.behaviors.useCustomAdapters,
'array' : merge.adapters.arraysCombine,
'number' : addNumbers
};
var result = merge.call(context, { a: { b: [0, 2, 4, 4, 2, 0] }}, { a: {b: [1, 3, 5, 5, 3, 1] }}, { a: {b: [7, 8, 9, 10, 11, 12] }});
assert.equal(result, { a: { b: [8, 13, 18, 19, 16, 13] }});
// Custom number adapter
function addNumbers(to, from)
{
return (to || 0) + from;
}
useCustomTypeOf
In some cases you might need to have more control over type detection, for that you can supply your own type detection function.
In following example we'd use same precise-typeof
library,
but with pojoOnly: true
flag:
var merge = require('deeply');
var typeOf = require('precise-typeof');
var moment = require('moment');
var context =
{
useCustomTypeOf: merge.behaviors.useCustomTypeOf,
'typeof' : (input) => typeOf(input, {pojoOnly: true})
};
var result = merge.call(context, { a: {someField: 'value'}, b: 'other thing'}, { a: moment.utc('2018-11-27') });
assert.equal(result, { a: moment.utc('2018-11-27'), b: 'other thing' });
In the above example, it would treat moment
object as atomic,
and won't mix it's properties with other properties.
Mutable interface supports all the described operations,
and available via explicit require require('deeply/mutable')
or require('deeply').mutable
property.
var merge = require('deeply/mutable');
var myObj = {a: {a1: 1, a2: 2}, b: {b1: 11, b2: 12}};
merge(myObj, {c: 'c', d: 'd'}, {x: {y: {z: -Infinity}}});
assert.equal(myObj, {a: {a1: 1, a2: 2}, b: {b1: 11, b2: 12}, c: 'c', d: 'd', x: {y: {z: -Infinity}}});
Also as shortcut and a homage to Tesla, ludicrous mode is available, that will clone functions and it's prototype objects by default. :) Details could be found in Cloning Functions examples.
var ludicrous = require('deeply/ludicrous');
var scopeVar = 6;
function original(a, b)
{
return a + b + scopeVar;
}
var cloned = ludicrous({ func: original });
// cloned object function named subj
assert.equal(cloned, { func: original });
// same signature
assert.equal(original.name, cloned.func.name);
assert.equal(original.length, cloned.func.length);
// separate objects
original.isOriginal = true;
cloned.func.isCopy = true;
assert.equal(original.isOriginal, true);
assert.equal(original.isCopy, undefined);
assert.equal(cloned.func.isOriginal, undefined);
assert.equal(cloned.func.isCopy, true);
// same output
assert.equal(original(1, 2), cloned.func(1, 2));
Note: ludicrous
isn't included into the main deeply
package, so it won't be automatically pulled in,
if you're bundling using browserify deeply/index.js
, to use ludicrous in the browser you'd need to explicitly
require it in your modules or specify direct path in your bundler config.
More examples can be found in test/compatability.js.
Or open an issue with questions and/or suggestions.
Deeply is released under the MIT license.
FAQs
A toolkit for deep structure manipulations, provides deep merge/clone functionality out of the box, and exposes hooks and custom adapters for more control and greater flexibility.
The npm package deeply receives a total of 957 weekly downloads. As such, deeply popularity was classified as not popular.
We found that deeply demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.