Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Get or set a deep value using a keypath string. Supports bracket and dot notation
Get, set, or delete a deep value using a keypath string.
A collection of keypath utilities: get, set, delete, in, has, flatten, expand, and immutable set/delete.
Lightweight and parses keypaths using vanilla JS - No eval
or new Function
hacks!
npm install keypather
// modular imports, so you can keep your bundle lean
const get = require('keypather/get')
const set = require('keypather/set')
const del = require('keypather/del')
const immutableSet = require('keypather/immutable-set')
const immutableDel = require('keypather/immutable-del')
const keypathIn = require('keypather/in')
const hasKeypath = require('keypather/has')
const expand = require('keypather/expand')
const flatten = require('keypather/flatten')
const get = require('keypather/get')
const set = require('keypather/set')
const del = require('keypather/del')
let obj
// Objects
obj = { foo: { bar: 100 } }
get(obj, 'foo.bar') // returns 100
del(obj, '["foo"]["bar"]') // returns true, obj becomes { foo: {} }
set(obj, 'foo.bar.qux', 200) // returns 200, obj becomes { foo: { bar: { qux: 200 } } }
get(obj, 'foo["bar"].qux') // returns 200
// Arrays
obj = {}
set(obj, 'foo[0]', 100) // obj is { foo: [ 100 ] }
const set = require('keypather/immutable-set')
const del = require('keypather/immutable-del')
let obj
let out
// Objects
obj = { foo: { bar: 100 } }
out = set(obj, 'foo.bar', 100) // returns obj
// out === obj,
// since it was not modified
out = set(obj, 'foo.bar.qux', 200) // returns { foo: { bar: { qux: 200 } } }
// out !== obj,
// obj is still { foo: { bar: 100 } }
out = del(obj, 'one.two.three') // returns obj
// out === obj,
// since it was not modified
out = del(obj, 'foo.bar.qux') // returns { foo: { bar: {} } }
// out !== obj,
// obj is still { foo: { bar: { qux: 200 } } }
// Arrays
obj = {}
out = set(obj, 'foo[0]', 100) // returns { foo: [ 100 ] } (new)
// out !== obj, obj is still { foo: { bar: 100 } }
const hasKeypath = require('keypather/has')
const keypathIn = require('keypather/in')
const obj = { foo: Object.create({ bar: 100 }) }
hasKeypath(obj, 'foo.bar') // returns false (bar is on proto)
keypathIn(obj, 'foo.bar') // returns true
hasKeypath(obj, 'foo') // returns true
const expand = require('keypather/expand')
const flatten = require('keypather/flatten')
const obj = expand({
'foo.bar': 1,
'foo.qux[0]': 100,
'foo["qux"][1]': 200,
'foo.qux.wut': 'val'
})
// obj is { foo { bar: 1, qux: [ 100, 200, wut: 'val' ] } }
const flat = flatten(obj)
// flat is { 'foo.bar': 1, 'foo.qux': 2 } }
/* Missing deep values w/ "force: false" */
get({}, 'foo.bar', { force: false })
set({}, 'foo.bar', 100, { force: false })
del({}, 'foo.bar', { force: false })
immutableSet({}, 'foo.bar', 100, { force: false })
immutableDel({}, 'foo.bar', { force: false })
// TypeError: Cannot read property 'bar' of undefined (at keypath 'foo' of 'foo.bar')
get({ foo: {} }, 'foo.bar', { force: false })
set({ foo: {} }, 'foo.bar', 100, { force: false })
del({ foo: {} }, 'foo.bar', { force: false })
immutableSet({ foo: {} }, 'foo.bar', 100, { force: false })
immutableDel({ foo: {} }, 'foo.bar', { force: false })
// TypeError: Cannot read property 'bar' of undefined (at keypath 'foo.bar' of 'foo.bar.qux')
hasKeypath({}, 'foo.bar', { force: false })
// TypeError: Cannot read property 'hasOwnProperty' of undefined (hasOwnProperty('bar') errored at keypath 'foo' of 'foo.bar')
keypathIn({}, 'foo.bar', { force: false })
// TypeError: Cannot use 'in' operator to search for 'bar' in undefined (at 'foo' of 'foo.bar')
keypathIn({}, 'foo.bar.qux', { force: false })
hasKeypath({}, 'foo.bar.qux', { force: false })
// TypeError: Cannot read property 'bar' of undefined (at keypath 'foo' of 'foo.bar.qux')
/* Warnings for set and immutable-set */
// by default, set will overwrite primitives (string, number or regexp) to an object or array.
// when overwritePrimitives is set to false, sets will warn when settings a key on a primitive
// to disable all warnings use the option { warn: false }
set({}, '[0]', 'val', { overwritePrimitives: false })
// log: Setting number key (0) on object at keypath '' of '[0]')
set([], 'key', 'val', { overwritePrimitives: false })
// log: Setting string key 'foo' on array at keypath '' of 'foo')
set({ foo: 1 }, 'foo.qux', 'val', { overwritePrimitives: false })
// log: Setting key 'qux' on number 1 at keypath 'foo' of 'foo.qux')
set({ foo: 1 }, 'foo[0]', 'val', { overwritePrimitives: false })
// log: Setting number key (0) on number 1 at keypath 'foo' of 'foo[0]')
set({ foo: 'str' }, 'foo.bar', 'val', { overwritePrimitives: false })
// log: Setting key 'bar' on string 'str' at keypath 'foo' of 'foo.bar')
set({ foo: {} }, 'foo[0]', 'val', { overwritePrimitives: false })
// log: Setting number key (0) on object at keypath 'foo' of 'foo[0]')
/* Invalid keypaths */
get({}, 'foo.1bar')
// Error: Unexpected token '1' in keypath 'foo.1bar' at position 4 (invalid dot key)
get({}, 'foo[]')
// Error: Unexpected token ']' in keypath 'foo[]' at position 4 (invalid bracket key)
get({}, 'foo["]')
// Error: Unexpected token ']' in keypath 'foo[]' at position 5 (invalid bracket string key)
get({}, 'foo.')
// Error: Unexpected end of keypath 'foo.' (invalid dot key)
get({}, 'foo[')
// Error: Unexpected end of keypath 'foo[' (invalid bracket key)
get({}, "foo['")
// Error: Unexpected end of keypath 'foo['' (invalid bracket string key)
Returns value at keypath in obj
if false, `get` will error when reading a key on a non-existant keypath.
const get = require('keypather/get');
const obj = {
foo: {
bar: {
baz: 'val'
}
}
};
get(obj, "foo.bar.baz"); // returns 'val'
get(obj, "foo['bar'].baz"); // returns 'val'
get(obj, "['foo']['bar']['baz']"); // returns 'val'
get({}, 'foo.two.three', { force: false }) // throws error
// TypeError: Cannot read property 'three' of undefined (at keypath 'foo.two' of 'foo.two.three')
Sets a value in obj at keypath. If force=true, set will create objects at non-existant keys in the keypath. If the non-existant key is a number, its value will be initialized as an array.
if false, `set` will error when reading a key on a non-existant keypath.
setting a key on a primitive will convert it to an object or array (if key is string or number).
if false, `set` will log a warning when setting keys on primitives.
const set = require('keypather/set');
let obj = {
foo: {
bar: {
baz: 'val'
}
}
};
set(obj, "foo['bar'].baz", 'val'); // returns 'val'
set(obj, "foo.bar.baz", 'val'); // returns 'val'
set(obj, "['foo']['bar']['baz']", 'val'); // returns 'val'
/* By default, set forces creation of non-existant keys */
obj = {}
set(obj, "foo.bar.baz", 'val'); // returns 'val'
// obj becomes:
// {
// foo: {
// bar: {
// baz: 'val'
// }
// }
// };
/* By default, overwrites primitives when setting a key on one */
obj = { foo: 1 }
set(obj, "foo.bar.baz", 'val'); // returns 'val'
// obj becomes:
// {
// foo: {
// bar: {
// baz: 'val'
// }
// }
// };
obj = { foo: 1 }
set(obj, "foo[0].baz", 'val'); // returns 'val'
// obj becomes:
// {
// foo: [{
// baz: 'val'
// }]
// };
/* Errors, force=false */
set({}, "foo.bar.baz", 'val', { force: false }); // throw's an error
// TypeError: Cannot read property 'bar' of undefined (at keypath 'foo' of 'foo.bar.baz')
// see more errors above in the 'Errors' section
/* Warnings, overwritePrimitives=false */
set({ foo: 'str' }, 'foo.bar', 'val', { overwritePrimitives: false })
// log: Setting key 'bar' on string 'str' at keypath 'foo' of 'foo.bar')
// see more warnings above in the 'Errors' section
Deletes value a keypath in obj. Similar to delete obj.key
.
if false, `del` will error when reading a key on a non-existant keypath.
const del = require('keypather/del');
const obj = {
foo: {
bar: {
baz: 'val'
}
}
};
del(obj, "foo['bar'].baz"); // true
del(obj, "foo.bar.baz"); // true
del(obj, "['foo']['bar']['baz']"); // true
// obj becomes:
// {
// foo: {
// bar: {}
// }
// }
/* Errors, force=false */
del(obj, "one.two.three", 'val', { force: false }); // throw's an error
// TypeError: Cannot read property 'two' of undefined (at keypath 'one' of 'one.two.three')
// see more errors above in the 'Errors' section
Sets a value in obj at keypath. If force=true, set will create objects at non-existant keys in the keypath. If the non-existant key is a number, its value will be initialized as an array.
if false, `immutable-set` will error when reading a key on a non-existant keypath.
setting a key on a primitive will convert it to an object or array (if key is string or number).
if false, `immutable-set` will log a warning when setting keys on primitives.
const set = require('keypather/immutable-set');
let obj = {
foo: {
bar: {
baz: 'val'
}
}
};
let out
out = set(obj, "foo['bar'].baz", 'val'); // returns SAME object, since the value was unchanged
// out === obj
out = set(obj, "foo.bar.baz", 'val2'); // returns { foo: { bar: { baz: 'val2' } } } (new object)
// out !== obj
out = set(obj, "['foo']['bar']['baz']", 'val3'); // returns { foo: { bar: { baz: 'val3' } } } (new object)
// out !== obj
/* By default, overwrites primitives when setting a key on one */
obj = { foo: 1 }
out = set(obj, "foo.bar.baz", 'val'); // returns new object
// out !== obj
// out is:
// {
// foo: {
// bar: {
// baz: 'val'
// }
// }
// };
obj = { foo: 1 }
out = set(obj, "foo[0].baz", 'val'); // returns new object
// out !== obj
// out is:
// {
// foo: [{
// baz: 'val'
// }]
// };
/* Errors, force=false */
obj = {}
set(obj, "foo.bar.baz", 'val', { force: false }); // throws error
// Error: Cannot read property 'bar' of undefined (at keypath 'foo' of 'foo.bar.baz')
/* Warnings, force=false */
obj = { foo: 'str' }
out = set(obj, 'foo.bar', 'val', { overwritePrimitives: false })
// out === obj, since keys cannot be set on strings or numbers
// log: Setting key 'bar' on string 'str' at keypath 'foo' of 'foo.bar')
Deletes value a keypath in obj. Similar to delete obj.key
.
if false, `del` will error when reading a key on a non-existant keypath.
const del = require('keypather/immutable-del');
const obj = {
foo: {
bar: {
baz: 'val'
}
}
};
let out
out = del(obj, "foo['bar'].baz"); // true
out = del(obj, "foo.bar.baz"); // true
out = del(obj, "['foo']['bar']['baz']"); // true
// obj becomes:
// {
// foo: {
// bar: {}
// }
// }
/* Errors, force=false */
del(obj, "one.two.three", 'val', { force: false }); // throw's an error
// Error: Cannot read property 'two' of undefined (at keypath 'one' of 'one.two.three')
Returns true if keypath is "in" the obj at the keypath. Similar to "in" operator.
const keypathIn = require('keypather/in');
const obj = {
foo: {
bar: {
baz: 'val'
__proto__: {
qux: 'val'
}
}
}
};
keypathIn(obj, "foo.bar.baz"); // true
keypathIn(obj, "foo.bar.qux"); // true
keypathIn(obj, "foo.bar.bing"); // false
keypathIn(obj, "foo['bar'].baz"); // true
keypathIn(obj, "one.two.three"); // false
// Errors, force=false
keypathIn(obj, "one.two.three", { force: false });
// Error: Cannot read property 'two' of undefined (at keypath 'two' of 'one.two.three')
keypathIn(obj, "foo.two.three", { force: false });
// TypeError: Cannot use 'in' operator to search for 'three' in undefined (at 'foo.two' of 'foo.two.three')
Returns true if the obj has the keypath. Similar to obj.hasOwnProperty
.
const hasKeypath = require('keypather/has');
const obj = {
foo: {
bar: {
baz: 'val'
__proto__: {
qux: 'val'
}
}
}
};
hasKeypath(obj, "foo.bar.baz"); // true
hasKeypath(obj, "foo.bar.qux"); // false
hasKeypath(obj, "['foo']['bar']['baz']"); // true
hasKeypath(obj, "one.two.three"); // false
// Errors, force=false
hasKeypath(obj, "one.two.three", { force: false }); // throw's an error
// Error: Cannot read property 'two' of undefined (at keypath 'two' of 'one.two.three
hasKeypath(obj, "foo.two.three", { force: false });
// Error: Cannot read property 'hasOwnProperty' of undefined (hasOwnProperty('three') errored at keypath 'foo.two' of 'foo.two.three')
Flatten an object or array into a keypath object
const flatten = require('keypather/flatten');
flatten({
foo: {
qux: 'hello'
},
bar: [
1,
{
yolo: [1]
}
]
});
// returns:
// {
// 'foo.qux': 'hello',
// 'bar[0]': 1,
// 'bar[1].yolo[0]': 1
// }
/* accepts a delimiter other than '.' as second arg */
flatten({
foo: {
qux: 'hello'
}
}, '_');
// returns:
// {
// 'foo_qux': 'hello',
// }
Expand a flattened object back into an object or array
const expand = require('keypather/expand');
expand({
'foo.qux': 'hello',
'bar[0]': 1,
'bar[1].yolo[0]': 1
});
// returns:
// {
// foo: {
// qux: 'hello'
// },
// bar: [
// 1,
// {
// yolo: [1]
// }
// ]
// }
/* expand will assume an object is an array if any of the keys are numbers */
expand({
'[0]': 1,
'[1].yolo[0]': 1
});
// returns:
// [
// 1,
// {
// yolo: [1]
// }
// ]
/* accepts a delimiter other than '.' as second arg */
expand({
'foo_qux': 'hello'
}, '_');
// returns:
// {
// foo: {
// qux: 'hello'
// }
// }
3.1.0
FAQs
Get or set a deep value using a keypath string. Supports bracket and dot notation
The npm package keypather receives a total of 26,553 weekly downloads. As such, keypather popularity was classified as popular.
We found that keypather demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers 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.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.