Bone is a collection of utility methods used by the BotSocket ecosystem.
Bone is available on npm:
npm install @botsocket/bone
const Bone = require('@botsocket/bone');
const cloned = Bone.clone([1, 2, 3]);
Or:
const Clone = require('@botsocket/bone/src/clone');
const cloned = Clone([1, 2, 3]);
assert(condition, [message])
clone(value, [options])
equal(value, ref, [options])
get(target, path)
isObject(value)
merge(target, source, [options])
set(target, path, value)
splitPath(path, [options])
Throws an error if a condition is falsy where:
condition
: The condition to evaluate.message
: The error message to throw.
Bone.assert(false); // Throws 'Unknown error'
Bone.assert(false, ''); // Throws 'Unknown error'
Bone.assert(false, 'x'); // Throws 'x'
Bone.assert(false, {}); // Throws '[object Object]'
Bone.assert(false, new TypeError('x')); // Throws 'TypeError: x'
Bone.assert('', 'x'); // Throws 'x'
Bone.assert(true, 'x'); // Does not throw
Bone.assert(1, 'x'); // Does not throw
Clones a value with support for circular references where:
value
: The value to clone.options
: Optional options where:shallow
: Whether the value should be cloned recursively. Defaults tofalse
.symbol
: Whether symbol keys should be cloned. Defaults totrue
.
const ref = {};
const sym = Symbol('test');
class X {}
X.prototype.immutable = true; // Immutable objects
const obj = {
a: 1,
b: [1, 2, 3],
c: new Map([[ref, 1]]),
d: new X(),
[sym]: 2,
};
const cloned = Bone.clone(obj);
obj.b === cloned.b; // false
cloned.b[1]; // 2
obj.d === cloned.d; // true
cloned.c.get(ref); // 1
const shallow = Bone.clone(obj, { shallow: true });
obj.b === shallow.b;
const noSymbol = Bone.clone(obj, { symbol: false });
noSymbol[sym]; // undefined
Compares two values recursively with support for circular references where:
value
: The first value.ref
: The reference value to compare tovalue
.options
: Optional options where:strict
: Whether to use strict equality (===
). If set tofalse
,Object.is
is used. Defaults totrue
.compareDescriptors
: Whether object descriptors are compared. Defaults tofalse
.symbol
: Whether symbol keys should be compared. Defaults totrue
.deepFunction
: Whether functions are deeply compared using their source code and properties. Defaults tofalse
.
class X {
constructor(someValue) {
this.someValue = someValue;
}
}
const fn = () => 1;
const sym = Symbol('x');
const obj = {
a: 1,
b: [1, 2, 3],
c: new Map([['x', 'y'], [1, 'z']]),
d: new X(1),
e: -0,
f: fn,
[sym]: 1,
};
const obj2 = {
a: 1,
b: [1, 2, 3],
c: new Map([[1, 'z'], ['x', 'y']]),
d: new X(1),
e: 0,
f: fn,
[sym]: 1,
};
Bone.equal(obj, obj2); // true
Bone.equal(obj, obj2, { strict: false }); // false (due to Object.is(-0, +0) returns false)
const obj3 = Bone.clone(obj2);
obj3[sym] = 2;
Bone.equal(obj, obj3); // false
Bone.equal(obj, obj3, { symbol: false }); // true
const obj4 = Bone.clone(obj2);
obj4.d = new X(2);
Bone.equal(obj, obj4); // false (due to different 'someValue's)
const obj5 = Bone.clone(obj2);
obj5.f = () => 1;
Bone.equal(obj, obj5); // false (due to different function reference)
Bone.equal(obj, obj5, { deepFunction: true }); // true
const obj6 = Bone.clone(obj);
obj6.g = 1;
const obj7 = Object.defineProperties(Bone.clone(obj), {
g: {
value: 1,
},
});
Bone.equal(obj6, obj7); // true
Bone.equal(obj6, obj7, { compareDescriptors: true }); // false
Retrieves a value nested inside an object, map or array given its path where:
target
: The target object to retrieve. Can be an object, map or array.path
: The path to the value. Can be a string of dot-separated keys or an array containing each individual key. Ifundefined
is passed, the target object is returned.
For array-like objects, undefined
will be returned if the reference key is an invalid index. The wildcard character *
can be used to retrieve all items. To escape the separator, use \
.
const obj = {
a: 1,
b: [1, 2, 3],
c: new Map([['key', 'x']]),
d: [{ a: 1 }, { a: 2 }, { a: 3 }],
'x.y.z': 1,
};
Bone.get(obj, 'a'); // 1
Bone.get(obj, 'b.1'); // 2
Bone.get(obj, 'b.-1'); // 3
Bone.get(obj, 'c.key'); // x
Bone.get(obj, 'x\\.y\\.z'); // 1
Bone.get(obj, 'd.*.a'); // [ 1, 2, 3 ]
Checks if a value is an object where:
value
: The value to check.
Bone.isObject('x'); // false
Bone.isObject(null); // false
Bone.isObject({}); // true
Bone.isObject(new Date()); // true
Merges two values recursively with support for circular references where:
target
: The target into which thesource
value is merged.source
: The source value.options
: Optional options where:symbol
: Whether symbol keys are merged. Defaults totrue
.
Note that this method mutates the target value.
const sym = Symbol('x');
const obj = {
a: 1,
c: [{ a: 'x' }, 2],
d: { x: 5 },
e: new Map([[1, 'x'], [2, 'y']]),
f: new Date(),
g: [],
};
const obj2 = {
b: 2,
c: [{ b: 'y' }, 4],
d: { x: 7 },
e: new Map([[1, 'z']]),
f: /x/i,
g: undefined,
[sym]: 'x',
};
const result = Bone.merge(obj, obj2);
Bone.equal(result, {
a: 1,
b: 2,
c: [{ a: 'x', b: 'y' }, 4],
d: { x: 7 },
e: new Map([[1, 'z'], [2, 'y']]),
f: /x/i,
g: [],
[sym]: 'x',
}); // true
Sets a value nested inside an object, map or array given its path where:
target
: The target object to modify. Can be an object, map or array.path
: The path to the value. Can be a string of dot-separated keys or an array containing each individual key. Ifundefined
is passed, the target object is returned unmodified.value
: The value to set to.
Note that this method mutates the target value. For array-like objects, the target will be returned if the reference key is a invalid index. To escape the separator, use \
.
Bone.set({}, 'a', 1); // { a: 1 }
Bone.set({}, 'a.b', 1); // { a: { b: 1 } }
Bone.set({}, 'a.0', 1); // { a: [ 1 ] }
Bone.set([], '0', 1); // [ 1 ]
Bone.set([1, 2], '-1', 3); // [ 1, 3 ]
Bone.set(new Map([['key', 'x']]), 'key', 'y'); // Map { key => y }
Bone.set({}, 'x\\.y\\.z', 1); // { 'x.y.z': 1 }
Splits a path to an array of individual keys where:
path
: The path to split. Can be a string of dot-separated keys or an array containing each individual key.options
: Optional options where:clone
: Only applies to arrays of individual keys. Whether the provided array should be cloned. Defaults tofalse
.
To escape the separator, use \
.
Bone.splitPath('x.y.z'); // [ x, y, z ]
Bone.splitPath('x\\.y.z'); // [ x.y, z ]
Bone.splitPath('x\\y.z'); // [ x\y, z ]
Bone.splitPath('x\\\\.z'); // [ x\, z ]