core-functions
Advanced tools
Comparing version 3.0.10 to 3.0.11
@@ -25,5 +25,2 @@ 'use strict'; | ||
//const numbers = require('./numbers'); | ||
//const isNumber = numbers.isNumber; | ||
/** | ||
@@ -30,0 +27,0 @@ * Attempts to convert the given data object or value into a JSON string and then encodes that to a base 64 string (if |
@@ -11,2 +11,3 @@ 'use strict'; | ||
// noinspection JSUnusedGlobalSymbols | ||
/** | ||
@@ -28,2 +29,4 @@ * Module containing utilities for working with objects. | ||
getPropertyValueByCompoundName: getPropertyValueByCompoundName, | ||
hasOwnPropertyWithKeys: hasOwnPropertyWithKeys, | ||
hasOwnPropertyWithCompoundName: hasOwnPropertyWithCompoundName, | ||
/** @deprecated */ | ||
@@ -167,2 +170,34 @@ copy: copy, | ||
/** | ||
* Traverses the given list of property keys starting from the given object to determine whether the targeted property | ||
* exists according to `hasOwnProperty` or not. | ||
* @param {Object} object - the object from which to start traversing | ||
* @param {PropertyKey[]} keys - the list of property keys to be traversed to check if the targeted property exists | ||
* @returns {boolean} true if the targeted property exists; otherwise false | ||
*/ | ||
function hasOwnPropertyWithKeys(object, keys) { | ||
let next = object; | ||
const last = keys.length - 1; | ||
for (let i = 0; i < last; ++i) { | ||
if (!next || typeof next !== 'object') { | ||
return false; | ||
} | ||
next = next[keys[i]]; | ||
} | ||
return next && typeof next === 'object' && last >= 0 ? next.hasOwnProperty(keys[last]) : false; | ||
} | ||
/** | ||
* Traverses the components of the given compound or simple property name starting from the given object to determine | ||
* whether the targeted property exists according to `hasOwnProperty` or not. A compound property name is one that | ||
* contains multiple property names separated by fullstops. | ||
* @param {Object} object - the object from which to start traversing | ||
* @param {string} compoundOrSimpleName - the compound or simple name of the property to check | ||
* @returns {boolean} true if the targeted property exists; otherwise false | ||
*/ | ||
function hasOwnPropertyWithCompoundName(object, compoundOrSimpleName) { | ||
const names = compoundOrSimpleName.split(".").map(n => trim(n)).filter(name => isNotBlank(name)); | ||
return hasOwnPropertyWithKeys(object, names); | ||
} | ||
/** | ||
* Creates & returns a copy of the given object by copying its properties into a new object of a similar type if the | ||
@@ -273,2 +308,3 @@ * given object is copyable (e.g. non-null, non-Promise object); otherwise simply returns the given object. Executes a | ||
} | ||
if (!isTraversable(object)) return []; | ||
@@ -275,0 +311,0 @@ |
{ | ||
"name": "core-functions", | ||
"version": "3.0.10", | ||
"version": "3.0.11", | ||
"description": "Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including strings, booleans, Promises, base 64, Arrays, Objects, standard AppErrors, etc.", | ||
@@ -5,0 +5,0 @@ "author": "Byron du Preez", |
101
promises.js
@@ -32,3 +32,5 @@ 'use strict'; | ||
Object.defineProperty(this, 'unresolvedInputs', {value: unresolvedInputs, enumerable: false}); | ||
Object.defineProperty(this, 'completed', {value: unresolvedInputs ? incompleteCount === 0 : undefined, enumerable: false}); | ||
Object.defineProperty(this, 'completed', | ||
{value: unresolvedInputs ? incompleteCount === 0 : undefined, enumerable: false} | ||
); | ||
// Alias for unresolvedInputs for backward-compatibility | ||
@@ -67,5 +69,5 @@ Object.defineProperty(this, 'unresolvedPromises', {value: unresolvedInputs, enumerable: false}); | ||
wrap: wrap, | ||
/** Returns a function that will wrap and convert a node-style method into a Promise-returning function */ | ||
/** @deprecated Use `wrap` instead - see OPTION 1 or 2 in its JDoc comments */ | ||
wrapMethod: wrapMethod, | ||
/** Returns a function that will wrap and convert a named node-style method into a Promise-returning function */ | ||
/** @deprecated Use `wrap` instead - see OPTION 1 or 2 in its JDoc comments */ | ||
wrapNamedMethod: wrapNamedMethod, | ||
@@ -132,13 +134,13 @@ /** Triggers execution of the given (typically synchronous) no-arg function, which may throw an error, within a new promise and returns the new promise */ | ||
return promiseLike instanceof Promise ? promiseLike : isPromiseLike(promiseLike) ? | ||
new Promise((resolve, reject) => { | ||
try { | ||
// Assumption: "then-able" accepts the same arguments as Promise.then | ||
promiseLike.then( | ||
result => resolve(result), | ||
error => reject(error) | ||
); | ||
} catch (err) { | ||
reject(err) | ||
} | ||
}) : Promise.resolve(promiseLike); | ||
new Promise((resolve, reject) => { | ||
try { | ||
// Assumption: "then-able" accepts the same arguments as Promise.then | ||
promiseLike.then( | ||
result => resolve(result), | ||
error => reject(error) | ||
); | ||
} catch (err) { | ||
reject(err) | ||
} | ||
}) : Promise.resolve(promiseLike); | ||
} | ||
@@ -167,3 +169,3 @@ | ||
* // example asynchronous invocation of callback with data | ||
* setTimeout(function () { | ||
* setTimeout(() => { | ||
* callback(null, 'Completed successfully'); | ||
@@ -173,15 +175,63 @@ * }, 5000); | ||
* | ||
* Promises.wrap(nodeStyleFunction)(arg1, arg2, ..., argN) | ||
* .then(result => ...) | ||
* .catch(err => ...); | ||
* const promiseStyleFunction = Promises.wrap(nodeStyleFunction); | ||
* ... | ||
* promiseStyleFunction(arg1, arg2, ..., argN) | ||
* .then(result => ...) | ||
* .catch(err => ...); | ||
* | ||
* NB: If the function passed is actually a method call on an object then EITHER call wrap using a bind on the method: | ||
* Example: | ||
* Promises.wrap(obj.nodeStyleMethod.bind(obj))(arg1, arg2, ..., argN) | ||
* NB: If the function passed is actually a method on an object: | ||
* | ||
* // crude example of a node-style method | ||
* class Abc { | ||
* nodeStyleMethod(arg1, arg2, ..., argN, callback) { | ||
* // example synchronous invocation of callback with error | ||
* if (!arg1) { | ||
* callback(new Error('arg1 undefined')); | ||
* } | ||
* // example asynchronous invocation of callback with data | ||
* setTimeout(function () { | ||
* callback(null, 'Completed successfully); | ||
* }, 5000); | ||
* } | ||
* } | ||
* | ||
* ... then ensure that you invoke the wrapped method on the target object by using one of the following approaches: | ||
* | ||
* OPTION 1 - Use `call` (or `apply`) and pass the target object as its `thisArg`: | ||
* Example: | ||
* const promiseStyleMethod = Promises.wrap(Abc.prototype.nodeStyleMethod); | ||
* ... | ||
* const abc = new Abc(); | ||
* // OR: const promiseStyleMethod = Promises.wrap(abc.nodeStyleMethod); | ||
* | ||
* promiseStyleMethod.call(abc, arg1, arg2, ..., argN) | ||
* .then(result => ...) | ||
* .catch(err => ...); | ||
* | ||
* OR instead use the Promises.wrapMethod function below. | ||
* OPTION 2 - Install the wrapped method on the target object or on its prototype (with a different name to the method | ||
* being wrapped) and then invoke the installed method on the target object: | ||
* Example: | ||
* Abc.prototype.promiseStyleMethod = Promises.wrap(Abc.prototype.nodeStyleMethod); | ||
* ... | ||
* const abc = new Abc(); | ||
* // OR: abc.promiseStyleMethod = Promises.wrap(abc.nodeStyleMethod); | ||
* | ||
* @param {Function} fn - a Node-callback style function to promisify | ||
* abc.promiseStyleMethod(arg1, arg2, ..., argN) | ||
* .then(result => ...) | ||
* .catch(err => ...); | ||
* | ||
* OPTION 3 - Call `wrap` using a bind on the method, but note that this will permanently bind the wrapped method to | ||
* the bound object: | ||
* Example: | ||
* const abc = new Abc(); | ||
* Promises.wrap(abc.nodeStyleMethod.bind(abc))(arg1, arg2, ..., argN) | ||
* .then(result => ...) | ||
* .catch(err => ...); | ||
* | ||
* WORST OPTION 4 - Use the DEPRECATED `wrapMethod` function below, but note that this will ALSO permanently bind the | ||
* wrapped method to the passed object. | ||
* | ||
* NOTE: Prefer the more flexible OPTION 1 or 2 over the less flexible OPTION 3 or 4 | ||
* | ||
* @param {Function} fn - a Node-callback style function to "promisify" | ||
* @returns {function(...args: *): Promise.<R>} a function, which when invoked will return a new Promise that will | ||
@@ -193,2 +243,3 @@ * resolve or reject based on the outcome of the callback | ||
return function () { | ||
const self = this; | ||
//var args = [].slice.call( arguments ); | ||
@@ -206,3 +257,3 @@ // potentially faster than slice | ||
}; | ||
fn.apply(null, args); | ||
fn.apply(self, args); | ||
} | ||
@@ -214,2 +265,3 @@ ); | ||
/** | ||
* @deprecated Use `wrap` instead - see OPTION 1 or 2 in its JDoc comments | ||
* Wraps and converts the given callback-last Node-style method into a promise-returning function, which will later | ||
@@ -268,2 +320,3 @@ * apply the method to the given object and which accepts all of the wrapped method's arguments other than its last | ||
/** | ||
* @deprecated Use `wrap` instead - see OPTION 1 or 2 in its JDoc comments | ||
* Returns a function that will wrap and convert a named callback-last Node-style method into a Promise-returning | ||
@@ -270,0 +323,0 @@ * function. |
@@ -1,2 +0,2 @@ | ||
# core-functions v3.0.10 | ||
# core-functions v3.0.11 | ||
@@ -3,0 +3,0 @@ Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including |
## Changes | ||
### 3.0.11 | ||
- Changes to `strings` module: | ||
- Changed `stringify` to survive a getter that throws an error | ||
- Changes to `objects` module: | ||
- Added new `hasOwnPropertyWithKeys` & `hasOwnPropertyWithCompoundName` functions | ||
- Changes to `promises` module: | ||
- Changed `wrap` function to also support wrapping of Node-style methods | ||
- Deprecated now redundant `wrapMethod` & `wrapNamedMethod` functions | ||
### 3.0.10 | ||
@@ -4,0 +13,0 @@ - Changes to `errors` module: |
'use strict'; | ||
// noinspection JSUnusedGlobalSymbols | ||
/** | ||
@@ -131,3 +132,3 @@ * Module containing utilities for working with strings. | ||
// Resolve any options requested | ||
const useJSONStringify = !!opts && opts.useJSONStringify == true; | ||
const useJSONStringify = !!opts && opts.useJSONStringify === true; | ||
if (useJSONStringify) { | ||
@@ -139,5 +140,5 @@ const replacer = opts && opts.replacer ? opts.replacer : undefined; | ||
const avoidErrorToString = !!opts && opts.avoidErrorToString == true; | ||
const avoidToJSONMethods = !!opts && opts.avoidToJSONMethods == true; | ||
const quoteStrings = !!opts && opts.quoteStrings == true; | ||
const avoidErrorToString = !!opts && opts.avoidErrorToString === true; | ||
const avoidToJSONMethods = !!opts && opts.avoidToJSONMethods === true; | ||
const quoteStrings = !!opts && opts.quoteStrings === true; | ||
@@ -257,7 +258,12 @@ const history = new WeakMap(); | ||
const propertyName = names[i]; | ||
const propertyValue = value[propertyName]; | ||
if (i > 0) { | ||
result += ','; | ||
} | ||
result += `"${propertyName}":${stringifyWithHistory(propertyValue, `${name}.${propertyName}`, true)}` | ||
// Avoid failing if an error is thrown from a getter | ||
try { | ||
const propertyValue = value[propertyName]; | ||
result += `"${propertyName}":${stringifyWithHistory(propertyValue, `${name}.${propertyName}`, true)}`; | ||
} catch (err) { | ||
result += `"${propertyName}":[Getter failed - ${err}]`; | ||
} | ||
} | ||
@@ -264,0 +270,0 @@ result += suffix; |
@@ -13,2 +13,4 @@ 'use strict'; | ||
const getOwnPropertyNamesRecursively = Objects.getOwnPropertyNamesRecursively; | ||
const getPropertyValueByCompoundName = Objects.getPropertyValueByCompoundName; | ||
const hasOwnPropertyWithCompoundName = Objects.hasOwnPropertyWithCompoundName; | ||
@@ -90,3 +92,3 @@ const strings = require('../strings'); | ||
const onlyE = true; | ||
const skip = true; | ||
// const skip = true; | ||
@@ -123,17 +125,51 @@ const o1 = {a: 1, b: 2, c: {d: 4, e: {f: {g: 7}, h: 8}}, i: {j: 10, k: 11}}; | ||
// ===================================================================================================================== | ||
// getPropertyValueByCompoundName | ||
// ===================================================================================================================== | ||
test('getPropertyValueByCompoundName', t => { | ||
t.equal(Objects.getPropertyValueByCompoundName(undefined, 'a.b.c'), undefined, `undefined.a.b.c must be undefined`); | ||
t.equal(Objects.getPropertyValueByCompoundName(null, 'a.b.c'), undefined, `null.a.b.c must be undefined`); | ||
t.equal(Objects.getPropertyValueByCompoundName({}, 'a.b.c'), undefined, `{}.a.b.c must be undefined`); | ||
t.equal(Objects.getPropertyValueByCompoundName([], 'a.b.c'), undefined, `[].a.b.c must be undefined`); | ||
t.equal(getPropertyValueByCompoundName(undefined, 'a.b.c'), undefined, `undefined.a.b.c must be undefined`); | ||
t.equal(getPropertyValueByCompoundName(null, 'a.b.c'), undefined, `null.a.b.c must be undefined`); | ||
t.equal(getPropertyValueByCompoundName({}, 'a.b.c'), undefined, `{}.a.b.c must be undefined`); | ||
t.equal(getPropertyValueByCompoundName([], 'a.b.c'), undefined, `[].a.b.c must be undefined`); | ||
const o = {a: 1, b: {c: 'c', d: {e: 'e'}}}; | ||
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'a'), 1, 'o.a must be 1'); | ||
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'b'), {c: 'c', d: {e: 'e'}}, `o.b must be {c: 'c', d: {e: 'e'}}`); | ||
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'b.c'), 'c', `o.b.c must be 'c'`); | ||
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'b.d'), {e: 'e'}, `o.b.d must be {e: 'e'}`); | ||
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'b.d.e'), 'e', `o.b.d.e must be 'e'`); | ||
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'x.y.z'), undefined, `o.x.y.z must be undefined`); | ||
t.deepEqual(getPropertyValueByCompoundName(o, 'a'), 1, 'o.a must be 1'); | ||
t.deepEqual(getPropertyValueByCompoundName(o, 'b'), {c: 'c', d: {e: 'e'}}, `o.b must be {c: 'c', d: {e: 'e'}}`); | ||
t.deepEqual(getPropertyValueByCompoundName(o, 'b.c'), 'c', `o.b.c must be 'c'`); | ||
t.deepEqual(getPropertyValueByCompoundName(o, 'b.d'), {e: 'e'}, `o.b.d must be {e: 'e'}`); | ||
t.deepEqual(getPropertyValueByCompoundName(o, 'b.d.e'), 'e', `o.b.d.e must be 'e'`); | ||
t.deepEqual(getPropertyValueByCompoundName(o, 'x.y.z'), undefined, `o.x.y.z must be undefined`); | ||
t.end(); | ||
}); | ||
// ===================================================================================================================== | ||
// hasOwnPropertyWithCompoundName | ||
// ===================================================================================================================== | ||
test('hasOwnPropertyWithCompoundName', t => { | ||
t.equal(hasOwnPropertyWithCompoundName(undefined, 'a.b.c'), false, `undefined.a.b.c must not exist`); | ||
t.equal(hasOwnPropertyWithCompoundName(null, 'a.b.c'), false, `null.a.b.c must not exist`); | ||
t.equal(hasOwnPropertyWithCompoundName({}, 'a.b.c'), false, `{}.a.b.c must not exist`); | ||
t.equal(hasOwnPropertyWithCompoundName([], 'a.b.c'), false, `[].a.b.c must not exist`); | ||
const o = {a: 1, b: {c: 'c', d: {e: 'e', n: null}, u: undefined}, u: undefined, n: null}; | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'a'), true, 'o.a must exist'); | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'b'), true, `o.b must exist`); | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'b.c'), true, `o.b.c must exist`); | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'b.d'), true, `o.b.d must exist`); | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'b.d.e'), true, `o.b.d.e must exist`); | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'x.y.z'), false, `o.x.y.z must not exist`); | ||
// properties containing undefined or null must still exist | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'u'), true, 'o.u must exist'); | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'n'), true, 'o.n must exist'); | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'b.u'), true, 'o.b.u must exist'); | ||
t.equal(hasOwnPropertyWithCompoundName(o, 'b.d.n'), true, 'o.b.d.n must exist'); | ||
const p = Object.create(o); | ||
t.equal(hasOwnPropertyWithCompoundName(p, 'a'), false, 'p.a must not exist'); | ||
t.equals(p.a, o.a, 'p.a must be o.a'); | ||
t.end(); | ||
}); |
@@ -832,1 +832,17 @@ 'use strict'; | ||
test('stringify must survive a broken getter', t => { | ||
const o = {x: {reading: true}, get y() { return this.x.reading; }}; | ||
let expected = '{"x":{"reading":true},"y":true}'; | ||
t.equal(Strings.stringify(o), expected, `stringify({x: {reading: true}, get y() { return this.x.reading; }}) must be '${expected}'`); | ||
// Now break the 'y' getter | ||
o.x = null; | ||
t.throws(() => o.y, TypeError, 'o.y must throw a TypeError'); | ||
t.throws(() => o['y'], TypeError, `o['y'] must throw a TypeError`); | ||
// but regardless, stringify must NOT break | ||
expected = '{"x":null,"y":[Getter failed - TypeError: Cannot read property \'reading\' of null]}'; | ||
t.equal(Strings.stringify(o), expected, `stringify({x: null, get y() { return this.x.reading; }}) must be '${expected}'`); | ||
t.end(); | ||
}); |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
957725
15314