deep-props
Advanced tools
Comparing version 0.0.8 to 0.1.0
@@ -23,2 +23,3 @@ { | ||
"node/no-unsupported-features": 0, | ||
"node/no-unpublished-require": 0, | ||
"require-jsdoc": [ | ||
@@ -49,3 +50,2 @@ "error", { | ||
"jsdoc/require-description-complete-sentence": 1, | ||
"jsdoc/require-example": 1, | ||
"jsdoc/require-hyphen-before-param-description": 1, | ||
@@ -52,0 +52,0 @@ "jsdoc/require-param": 1, |
@@ -1,944 +0,106 @@ | ||
# Index | ||
### Functions | ||
# deep-props | ||
<dl> | ||
<dt><a href="#isPrimitive">isPrimitive(x)</a> ⇒ <code>boolean</code></dt> | ||
<dd><p>Determines if x is a JS primitive. | ||
Used to determine if a value should be unpacked.</p> | ||
</dd> | ||
<dt><a href="#getObjectPermissions">getObjectPermissions(target)</a> ⇒ <code><a href="#Permissions">Permissions</a></code></dt> | ||
<dd><p>Gets the frozen, sealed, and extensible statuses of an object.</p> | ||
</dd> | ||
<dt><a href="#genPropsFromDescriptorEntries">genPropsFromDescriptorEntries(descriptorEntries, permissions, opt)</a> ⇒ <code><a href="#Prop">Array.<Prop></a></code></dt> | ||
<dd><p>Converts list of descriptors to prop Array. | ||
Attaches information based on options.</p> | ||
</dd> | ||
<dt><a href="#genProtoProp">genProtoProp(target, permissions, opt)</a> ⇒ <code><a href="#Prop">Array.<Prop></a></code></dt> | ||
<dd><p>Generates a prop for a target object's prototype.</p> | ||
</dd> | ||
<dt><a href="#getOwnProps">getOwnProps(target, permissions, opt)</a> ⇒ <code><a href="#Prop">Array.<Prop></a></code></dt> | ||
<dd><p>Generates a list of non-inherited properties of a target object.</p> | ||
</dd> | ||
<dt><a href="#getMapProps">getMapProps(target, permissions, opt)</a> ⇒ <code><a href="#Prop">Array.<Prop></a></code></dt> | ||
<dd><p>Gets a list of properties within a target Map.</p> | ||
</dd> | ||
<dt><a href="#getSetProps">getSetProps(target, permissions, opt)</a> ⇒ <code><a href="#Prop">Array.<Prop></a></code></dt> | ||
<dd><p>Gets a list of properties within a target Set. | ||
Uses insertion order as keys.</p> | ||
</dd> | ||
<dt><a href="#getSpecialProps">getSpecialProps(target, permissions, opt)</a> ⇒ <code><a href="#Prop">Array.<Prop></a></code></dt> | ||
<dd><p>Gets any special object properties. | ||
If propsCustomizer is supplied, and returns a defined value from target, then getSpecialProps will return this value.</p> | ||
</dd> | ||
<dt><a href="#getProps">getProps(target, opt)</a> ⇒ <code><a href="#Prop">Array.<Prop></a></code></dt> | ||
<dd><p>Returns all inherited properties, own properties, special properties, and object permissions.</p> | ||
</dd> | ||
<dt><a href="#assignReferencePoints">assignReferencePoints(props, [host], [path])</a> ⇒ <code><a href="#PropAt">Array.<PropAt></a></code></dt> | ||
<dd><p>Assigns reference points to a list of properties.</p> | ||
</dd> | ||
<dt><a href="#search">search(host, opt)</a> ⇒ <code>undefined</code></dt> | ||
<dd><p>Non-recursively searches through the host object by queueing its children. | ||
Attaches information based on options. | ||
Determines whether child should be unpacked by checking if it is a primitive. | ||
Keeps track of all object references encountered to avoid circular looping. | ||
Explores object keys via creation of a new Host.</p> | ||
</dd> | ||
<dt><a href="#mergeOptions">mergeOptions(opt)</a> ⇒ <code><a href="#Options">Options</a></code></dt> | ||
<dd><p>Merges supplied options with defaults.</p> | ||
</dd> | ||
</dl> | ||
Provides a collection of tools for performing operations on deeply nested object properties, prototypes, and object keys. Avoids stack limit violations by using task queues rather than recursion. Allows for custom execution settings including non-native dataset handling. | ||
### Typedefs | ||
Source: | ||
<dl> | ||
<dt><a href="#Search">Search</a> : <code>Object</code></dt> | ||
<dd><p>Instance of search generator with loaded parameters. Used when execution of an entire search is not necessary. A <code>for .. of</code> loop on this object will iterate over every result from the search.</p> | ||
</dd> | ||
<dt><a href="#DescriptorEntries">DescriptorEntries</a> : <code>Array</code></dt> | ||
<dd><p>An Array of Arrays with Key at index 0 and a property descriptors object at index 1.</p> | ||
<p><li>Equivalent to the result of <code>Object.entries(Object.getOwnPropertyDescriptors(Target))</code></p> | ||
<p><li> Only relevant property descriptors should be added.</p> | ||
</dd> | ||
<dt><a href="#Custom">Custom</a> : <code>*</code></dt> | ||
<dd><p>Any kind of container that can be used as a target. | ||
Only applicable if an adequate PropsCustomizer is supplied.</p> | ||
</dd> | ||
<dt><a href="#PropsCustomizer">PropsCustomizer</a> ⇒ <code><a href="#DescriptorEntries">DescriptorEntries</a></code></dt> | ||
<dd><p>Function supplied in Options that handles Target objects and returns a descriptor matrix of any children within a Custom container. Returns undefined if not applicable.</p> | ||
</dd> | ||
<dt><a href="#Target">Target</a> : <code>Object</code> | <code>Array</code> | <code>Map</code> | <code>Set</code> | <code><a href="#Custom">Custom</a></code></dt> | ||
<dd><p>Unpacking target for finding children. | ||
Retrieved from the first value in the queue. | ||
Used to supply the queue with more potential targets within Prop objects.</p> | ||
</dd> | ||
<dt><a href="#Permissions">Permissions</a> : <code>Object</code></dt> | ||
<dd><p>The result of the Object permissions tests for a Target object.</p> | ||
</dd> | ||
<dt><a href="#Key">Key</a> : <code>*</code></dt> | ||
<dd><p>Key used for accessing a child value from a Target object.</p> | ||
</dd> | ||
<dt><a href="#Path">Path</a> : <code><a href="#Key">Array.<Key></a></code></dt> | ||
<dd><p>Array of keys used to describe the children of a Host at each level above a given Prop.</p> | ||
</dd> | ||
<dt><a href="#Host">Host</a> : <code><a href="#Target">Target</a></code></dt> | ||
<dd><p>A non-primitive container which represents the root of a given Path.</p> | ||
</dd> | ||
<dt><a href="#Ref">Ref</a> : <code>Object</code></dt> | ||
<dd><p>Describes the location of a previously encountered target.</p> | ||
</dd> | ||
<dt><a href="#Prop">Prop</a> : <code>Object</code></dt> | ||
<dd><p>Description of the properties found for a given value during the search,</p> | ||
</dd> | ||
<dt><a href="#PropAt">PropAt</a> : <code>Object</code></dt> | ||
<dd><p>Description of a given level of the chain. Transformed Prop Object with location attched.</p> | ||
</dd> | ||
<dt><a href="#Options">Options</a> : <code>Object</code></dt> | ||
<dd><p>Execution-wide settings supplied to the module. | ||
Modifies types of data attached to results. | ||
Modifies types of children to extract.</p> | ||
</dd> | ||
</dl> | ||
* [index.js](/index.js), [line 7](/index.js#L7) | ||
# Global Functions | ||
### Namespaces | ||
<a name="isPrimitive"></a> | ||
[extract](/libs/extract/docs/global.md) | ||
## isPrimitive(x) ⇒ <code>boolean</code> | ||
Determines if x is a JS primitive. | ||
Used to determine if a value should be unpacked. | ||
### Type Definitions | ||
**Returns**: <code>boolean</code> - True if primitive, false if not. | ||
<a name="~Container"></a> | ||
#### Container | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| x | <code>\*</code> | Test value. | | ||
Container object used as a target for child property extraction. | ||
**Example** | ||
```js | ||
// returns true | ||
##### Type: | ||
isPrimitive('foo') | ||
``` | ||
**Example** | ||
```js | ||
// returns false | ||
* Object | Array | Map | WeakMap | Set | WeakSet | [deep-props~Custom](/docs/global.md#~Custom) | ||
isPrimitive({}) | ||
``` | ||
<a name="getObjectPermissions"></a> | ||
Source: | ||
## getObjectPermissions(target) ⇒ [<code>Permissions</code>](#Permissions) | ||
Gets the frozen, sealed, and extensible statuses of an object. | ||
* [index.js](/index.js), [line 38](/index.js#L38) | ||
**Returns**: [<code>Permissions</code>](#Permissions) - Result of the three Object permissions tests. | ||
<a name="~Custom"></a> | ||
#### Custom | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| target | [<code>Target</code>](#Target) | Target object. | | ||
Custom dataset for use as a [Container](#~Container). May be accessed via valid customizer functions. | ||
**Example** | ||
```js | ||
// returns { parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true } | ||
##### Type: | ||
getObjectPermissions({}) | ||
``` | ||
<a name="genPropsFromDescriptorEntries"></a> | ||
* \* | ||
## genPropsFromDescriptorEntries(descriptorEntries, permissions, opt) ⇒ [<code>Array.<Prop></code>](#Prop) | ||
Converts list of descriptors to prop Array. | ||
Attaches information based on options. | ||
Source: | ||
**Returns**: [<code>Array.<Prop></code>](#Prop) - Converted 1D Array of properties. | ||
* [index.js](/index.js), [line 15](/index.js#L15) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| descriptorEntries | [<code>DescriptorEntries</code>](#DescriptorEntries) | Matrix of keys and descriptors. | | ||
| permissions | [<code>Permissions</code>](#Permissions) | Permissions list. | | ||
| opt | [<code>Options</code>](#Options) | Execution settings. | | ||
##### Example | ||
**Example** | ||
```js | ||
// returns [ | ||
// { | ||
// key: 'foo', | ||
// value: 'bar', | ||
// writable: true, | ||
// enumerable: true, | ||
// configurable: true, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
genPropsFromDescriptorEntries( | ||
[ | ||
[ | ||
'foo', | ||
{ | ||
value: 'bar', | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
} | ||
] | ||
], | ||
{ parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
{ descriptors: true, permissions: true } | ||
) | ||
``` | ||
<a name="genProtoProp"></a> | ||
## genProtoProp(target, permissions, opt) ⇒ [<code>Array.<Prop></code>](#Prop) | ||
Generates a prop for a Target object's prototype. Will attach Target object's permission statuses using the same 'parentIs' language, even though it is not technically a child. | ||
**Returns**: [<code>Array.<Prop></code>](#Prop) - Array with single entry of '\_\_proto__' key and value. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| target | [<code>Target</code>](#Target) | Target object. | | ||
| permissions | [<code>Permissions</code>](#Permissions) | Object permission statuses. | | ||
| opt | [<code>Options</code>](#Options) | Execution settings. | | ||
**Example** | ||
```js | ||
// returns [ | ||
// { | ||
// key: '__proto__', | ||
// value: {}, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
genProtoProp( | ||
{}, | ||
{ parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
{ descriptors: true, permissions: true } | ||
) | ||
``` | ||
<a name="getOwnProps"></a> | ||
## getOwnProps(target, permissions, opt) ⇒ [<code>Array.<Prop></code>](#Prop) | ||
Generates a list of non-inherited properties of a target object. | ||
**Returns**: [<code>Array.<Prop></code>](#Prop) - Array of associated properties. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| target | [<code>Target</code>](#Target) | Target object. | | ||
| permissions | [<code>Permissions</code>](#Permissions) | Object permission statuses. | | ||
| opt | [<code>Options</code>](#Options) | Execution settings. | | ||
**Example** | ||
```js | ||
// returns [ | ||
// { | ||
// key: 'foo', | ||
// value: 'bar', | ||
// writable: true, | ||
// enumerable: true, | ||
// configurable: true, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// }, | ||
// { | ||
// key: 'baz', | ||
// value: 'beh', | ||
// writable: true, | ||
// enumerable: true, | ||
// configurable: true, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
getOwnProps( | ||
{ foo: 'bar', baz: 'beh' }, | ||
{ parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
{ descriptors: true, permissions: true } | ||
) | ||
``` | ||
<a name="getMapProps"></a> | ||
## getMapProps(target, permissions, opt) ⇒ [<code>Array.<Prop></code>](#Prop) | ||
Gets a list of properties within a target Map. | ||
**Returns**: [<code>Array.<Prop></code>](#Prop) - Array of associated properties. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| target | [<code>Target</code>](#Target) | Target object. | | ||
| permissions | [<code>Permissions</code>](#Permissions) | Object permission statuses. | | ||
| opt | [<code>Options</code>](#Options) | Execution settings. | | ||
**Example** | ||
```js | ||
// returns [ | ||
// { | ||
// key: 'foo', | ||
// value: 'bar', | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// }, | ||
// { | ||
// key: { baz: 'beh' }, | ||
// value: { qux: 'quz' }, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
getMapProps( | ||
new Map([ | ||
[ 'foo', 'bar' ], | ||
[ { baz: 'beh' }, { qux: 'quz' } ] | ||
]), | ||
{ parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
{ descriptors: true, permissions: true } | ||
) | ||
``` | ||
<a name="getSetProps"></a> | ||
## getSetProps(target, permissions, opt) ⇒ [<code>Array.<Prop></code>](#Prop) | ||
Gets a list of properties within a target Set. | ||
Uses insertion order as keys. | ||
**Returns**: [<code>Array.<Prop></code>](#Prop) - Array of associated properties. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| target | [<code>Target</code>](#Target) | Target object. | | ||
| permissions | [<code>Permissions</code>](#Permissions) | Object permission statuses. | | ||
| opt | [<code>Options</code>](#Options) | Execution settings. | | ||
**Example** | ||
```js | ||
// returns [ | ||
// { | ||
// key: '0', | ||
// value: 1, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// }, | ||
// { | ||
// key: '1', | ||
// value: 2, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
getSetProps( | ||
new Set([ 1, 2 ]), | ||
{ parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
{ descriptors: true, permissions: true } | ||
) | ||
``` | ||
<a name="getSpecialProps"></a> | ||
## getSpecialProps(target, permissions, opt) ⇒ [<code>Array.<Prop></code>](#Prop) | ||
Gets any special object properties. | ||
If propsCustomizer is supplied, and returns a defined value from target, then getSpecialProps will return this value. | ||
**Returns**: [<code>Array.<Prop></code>](#Prop) - Array of associated properties. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| target | [<code>Target</code>](#Target) | Target object. | | ||
| permissions | [<code>Permissions</code>](#Permissions) | Object permission statuses. | | ||
| opt | [<code>Options</code>](#Options) | Options. | | ||
**Example** | ||
```js | ||
const map = new Map( | ||
[ | ||
[ 'foo', 'bar' ] | ||
] | ||
) | ||
// returns [ | ||
// { | ||
// key: 'foo', | ||
// value: 'bar', | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
getSpecialProps( | ||
map, | ||
{ parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
{ descriptors: true, permissions: true } | ||
) | ||
``` | ||
**Example** | ||
```js | ||
const s = new Set( | ||
[ 'baz', 'beh' ] | ||
) | ||
// returns [ | ||
// { | ||
// key: '0', | ||
// value: 'baz', | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// }, | ||
// { | ||
// key: '1', | ||
// value: 'beh', | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
getSpecialProps( | ||
s, | ||
{ parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
{ descriptors: true, permissions: true } | ||
) | ||
``` | ||
**Example** | ||
```js | ||
class NonNativeDataStructure { | ||
constructor(arr) { | ||
const values = arr | ||
this.get = i => values[i] | ||
this.getValues = () => values | ||
this.push = x => values.push(x) | ||
} | ||
} | ||
const custom = new NonNativeDataStructure([ 'qux', 'quz' ]) | ||
const propsCustomizer = target => { | ||
if (target instanceof NonNativeDataStructure) { | ||
return ( | ||
Object.entries( | ||
Object.getOwnPropertyDescriptors( | ||
target.getValues() | ||
) | ||
).filter( | ||
entry => entry[1].enumerable !== false | ||
) | ||
) | ||
} | ||
} | ||
// returns [ | ||
// { | ||
// key: '0', | ||
// value: 'qux', | ||
// writable: true, | ||
// enumerable: true, | ||
// configurable: true, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// }, | ||
// { | ||
// key: '1', | ||
// value: 'quz', | ||
// writable: true, | ||
// enumerable: true, | ||
// configurable: true, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
getSpecialProps( | ||
custom, | ||
{ parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
{ descriptors: true, permissions: true, propsCustomizer } | ||
) | ||
``` | ||
<a name="getProps"></a> | ||
## getProps(target, opt) ⇒ [<code>Array.<Prop></code>](#Prop) | ||
Returns all inherited properties, own properties, special properties, and object permissions. | ||
**Returns**: [<code>Array.<Prop></code>](#Prop) - Array of associated properties. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| target | [<code>Target</code>](#Target) | Target object. | | ||
| opt | [<code>Options</code>](#Options) | Execution settings. | | ||
**Example** | ||
```js | ||
const map = new Map( | ||
[ | ||
[ 'foo', 'bar' ] | ||
] | ||
) | ||
// returns [ | ||
// { | ||
// key: '__proto__', | ||
// value: Map {}, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// }, | ||
// { | ||
// key: 'foo', | ||
// value: 'bar', | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
getProps(map, { inherited: true, descriptors: true, permissions: true }) | ||
``` | ||
<a name="assignReferencePoints"></a> | ||
## assignReferencePoints(props, [host], [path]) ⇒ [<code>Array.<PropAt></code>](#PropAt) | ||
Assigns reference points to a list of properties. | ||
**Returns**: [<code>Array.<PropAt></code>](#PropAt) - Array of location-tagged Props. | ||
| Param | Type | Default | Description | | ||
| --- | --- | --- | --- | | ||
| props | [<code>Array.<Prop></code>](#Prop) | | Prop array. | | ||
| [host] | [<code>Host</code>](#Host) | | Host object. | | ||
| [path] | [<code>Path</code>](#Path) | <code>[]</code> | Path to current proplist. | | ||
**Example** | ||
```js | ||
let props = [ | ||
{ | ||
key: 'foo', | ||
value: 'bar', | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
}, | ||
{ | ||
key: { baz: 'beh' }, | ||
value: { qux: 'quz' }, | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
} | ||
] | ||
// returns [ | ||
// { | ||
// path: ['foo'], | ||
// value: 'bar', | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// }, | ||
// { | ||
// path: [{ foo: 'bar' }], | ||
// value: { qux: 'quz' }, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
assignReferencePoints(props) | ||
``` | ||
**Example** | ||
```js | ||
props = [ | ||
{ | ||
key: 'qux', | ||
value: 'quz', | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
} | ||
] | ||
// returns [ | ||
// { | ||
// host: { foo: 'bar' }, | ||
// path: ['qux'], | ||
// value: 'quz', | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// } | ||
// ] | ||
assignReferencePoints(props, { foo: 'bar' }) | ||
``` | ||
<a name="search"></a> | ||
## <span style="color:gray">(generator)</span> search(host, opt) ⇒ [<code>PropAt</code>](#PropAt) | ||
Non-recursively searches through the host object by queueing its children. | ||
Attaches information based on options. | ||
Determines whether child should be unpacked by checking if it is a primitive. | ||
Keeps track of all object references encountered to avoid circular looping. | ||
Explores object keys via creation of a new Host. | ||
**Yields**: [<code>PropAt</code>](#PropAt) - Current Prop with attached location. | ||
**Returns**: <code>undefined</code> - Undefined if done. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| host | [<code>Host</code>](#Host) | Host container supplied to module. | | ||
| opt | [<code>Options</code>](#Options) | Execution settings. | | ||
**Example** | ||
```js | ||
// Searching through an Object | ||
const data = { | ||
foo: { | ||
bar: { | ||
baz: { | ||
beh: 'qux' | ||
} | ||
} | ||
} | ||
} | ||
const query = search(data, { own: true }) | ||
for (let step of query) { | ||
// iterates once: | ||
// 1: step === { path: [ 'foo', 'bar', 'baz', 'beh' ], value: 'qux' } | ||
} | ||
``` | ||
**Example** | ||
```js | ||
// Searching through an multi-nested Object | ||
const data = { | ||
foo: { | ||
beh: { | ||
lorem: 'ex' | ||
} | ||
}, | ||
bar: { | ||
qux: { | ||
ipsum: 'igne' | ||
} | ||
}, | ||
baz: { | ||
quz: { | ||
dolor: 'vita' | ||
} | ||
} | ||
} | ||
const query = search(data, { own: true }) | ||
for (let step of query) { | ||
// iterates 3 times: | ||
// 1: step === { path: [ 'foo', 'beh', 'lorem' ], value: 'ex' } | ||
// 2: step === { path: [ 'bar', 'qux', 'ipsum' ], value: 'igne' } | ||
// 3: step === { path: [ 'baz', 'quz', 'dolor' ], value: 'vita' } | ||
} | ||
``` | ||
<a name="mergeOptions"></a> | ||
## mergeOptions(opt) ⇒ [<code>Options</code>](#Options) | ||
Merges supplied options with defaults. | ||
**Returns**: [<code>Options</code>](#Options) - Execution settings. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| opt | [<code>Options</code>](#Options) | Options passed to the module. | | ||
**Example** | ||
```js | ||
// returns { own: true } | ||
mergeOptions({}) | ||
``` | ||
**Example** | ||
```js | ||
// returns { own: false, inherited: true } | ||
mergeOptions({ own: false, inherited: true }) | ||
``` | ||
**Example** | ||
```js | ||
// returns { | ||
// inherited: true, | ||
// own: true, | ||
// nonEnumerable: true, | ||
// permissions: true, | ||
// descriptors: true, | ||
// stepwise: true, | ||
// includeRefValues: true, | ||
// full: true | ||
// } | ||
mergeOptions({ full: true }) | ||
``` | ||
**Example** | ||
```js | ||
// returns { | ||
// inherited: true, | ||
// own: true, | ||
// nonEnumerable: true, | ||
// permissions: true, | ||
// descriptors: false, | ||
// stepwise: true, | ||
// includeRefValues: true, | ||
// full: true | ||
// } | ||
mergeOptions({ full: true, descriptors: false }) | ||
``` | ||
# Global Typedefs | ||
<a name="Search"></a> | ||
## Search : <code>Object</code> | ||
Instance of [search](#search) generator with loaded parameters. Used when execution of an entire search is not necessary. A <code>for .. of</code> loop on this object will iterate over every result from the search. | ||
<a name="DescriptorEntries"></a> | ||
## DescriptorEntries : <code>Array</code> | ||
An Array of Arrays with Key at index 0 and a property descriptors object at index 1. | ||
<ul><li>Equivalent to the result of <code>Object.entries(Object.getOwnPropertyDescriptors(Target))</code> | ||
<li> Only relevant property descriptors should be added.</ul> | ||
**Example** | ||
```js | ||
[ | ||
[ | ||
'foo', | ||
{ | ||
value: 'bar', | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
} | ||
] | ||
] | ||
``` | ||
<a name="Custom"></a> | ||
## Custom : <code>\*</code> | ||
Any kind of container that can be used as a target. | ||
Only applicable if an adequate PropsCustomizer is supplied. | ||
**Example** | ||
```js | ||
// This is a kind of container that would require a PropsCustomizer function. | ||
(() => { | ||
class NonNativeDataStructure { | ||
constructor(arr) { | ||
const values = arr | ||
this.get = i => values[i] | ||
this.getValues = () => values | ||
this.push = x => values.push(x) | ||
class CustomDataStructure { | ||
constructor(array) { | ||
this.get = i => array[i] | ||
this.getValues = () => array | ||
this.push = x => array.push(x) | ||
} | ||
} | ||
return new NonNativeDataStructure([ 'foo', 'bar' ]) | ||
return new CustomDataStructure([ 'foo', 'bar' ]) | ||
})() | ||
``` | ||
<a name="PropsCustomizer"></a> | ||
## PropsCustomizer ⇒ [<code>DescriptorEntries</code>](#DescriptorEntries) | ||
Function supplied in Options that handles Target objects and returns a descriptor matrix of any children within a Custom container. Returns undefined if not applicable. | ||
<a name="~Host"></a> | ||
#### Host | ||
**Returns**: [<code>DescriptorEntries</code>](#DescriptorEntries) - Array of arrays of keys and property descriptor objects. | ||
A non-primitive [Container](#~Container) which represents the root of a given path. | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| target | [<code>Target</code>](#Target) | Container to analyze for additional children. | | ||
##### Type: | ||
**Example** | ||
```js | ||
target => { | ||
if (target instanceof ArrayBuffer && target.byteLength === 16) { | ||
// Mapping the output of Object.entries to a DescriptorEntries array. | ||
return Object.entries( | ||
new Int8Array(target) | ||
).map(entry => ( | ||
[ | ||
entry[0], { value: entry[1] } | ||
] | ||
)) | ||
} | ||
} | ||
``` | ||
<a name="Target"></a> | ||
* [deep-props~Container](/docs/global.md#~Container) | ||
## Target : <code>Object</code> \| <code>Array</code> \| <code>Map</code> \| <code>Set</code> \| [<code>Custom</code>](#Custom) | ||
Unpacking target for finding children. | ||
Retrieved from the first value in the queue. | ||
Used to supply the queue with more potential targets within Prop objects. | ||
Source: | ||
<a name="Permissions"></a> | ||
* [index.js](/index.js), [line 44](/index.js#L44) | ||
## Permissions : <code>Object</code> | ||
The result of the Object permissions tests for a Target object. | ||
<a name="~Key"></a> | ||
#### Key | ||
**Properties** | ||
Key used for accessing a child property within a container. When its value is `'__proto__'`, it is used as a stand-in for `Object.getPrototypeOf()`. | ||
| Name | Type | Description | | ||
| --- | --- | --- | | ||
| parentIsFrozen | <code>boolean</code> | Result of Object.isFrozen(Target) | | ||
| parentIsSealed | <code>boolean</code> | Result of Object.isSealed(Target) | | ||
| parentIsExtensible | <code>boolean</code> | Result of Object.isExtensible(Target) | | ||
##### Type: | ||
<a name="Key"></a> | ||
* string | [deep-props~Container](/docs/global.md#~Container) | ||
## Key : <code>\*</code> | ||
Key used for accessing a child value from a Target object. | ||
Source: | ||
<a name="Path"></a> | ||
* [index.js](/index.js), [line 32](/index.js#L32) | ||
## Path : [<code>Array.<Key></code>](#Key) | ||
Array of keys used to describe the children of a Host at each level above a given Prop. | ||
<a name="~ResultGenerator"></a> | ||
#### ResultGenerator | ||
<a name="Host"></a> | ||
Generator object which yields stepwise operation results. | ||
## Host : [<code>Target</code>](#Target) | ||
A non-primitive container which represents the root of a given Path. | ||
##### Type: | ||
<a name="Ref"></a> | ||
* Object | ||
## Ref : <code>Object</code> | ||
Describes the location of a previously encountered target. | ||
Source: | ||
**Properties** | ||
* [index.js](/index.js), [line 50](/index.js#L50) | ||
| Name | Type | Description | | ||
| --- | --- | --- | | ||
| [host] | [<code>Host</code>](#Host) | If Host is different than the supplied Host, it will be specified. | | ||
| path | [<code>Path</code>](#Path) | Path of previously encountered target. | | ||
<hr> | ||
**Example** | ||
```js | ||
{ path: [ 'foo', 'bar' ] } | ||
``` | ||
**Example** | ||
```js | ||
{ host: { foo: 'bar' }, path: [ 'baz', 'beh' ] } | ||
``` | ||
<a name="Prop"></a> | ||
## [Home](/README.md) | ||
## Prop : <code>Object</code> | ||
Description of the properties found for a given value during the search, | ||
### Modules | ||
**Properties** | ||
* [extract](/libs/extract/docs/API.md) | ||
| Name | Type | Description | | ||
| --- | --- | --- | | ||
| key | [<code>Path</code>](#Path) | Key used on the parent (Target) object to retrieve the value. | | ||
| [value] | <code>\*</code> | Value described at the Prop's location (if any). In cases of a previously discovered reference (circular or otherwise), value will be replaced with a ref property (unless opt.showRefValues is true). | | ||
| [writable] | <code>boolean</code> | 'Writable' property descriptor of the value. | | ||
| [enumerable] | <code>boolean</code> | 'Enumerable' property descriptor of the value. | | ||
| [configurable] | <code>boolean</code> | 'Configurable' property descriptor of the value. | | ||
| [parentIsFrozen] | <code>boolean</code> | Frozen status of the parent object. | | ||
| [parentIsSealed] | <code>boolean</code> | Sealed status of the parent object. | | ||
| [parentIsExtensible] | <code>boolean</code> | Extensible status of the parent object. | | ||
### Namespaces | ||
**Example** | ||
```js | ||
{ | ||
key: 'foo', | ||
value: 'bar', | ||
writable: true, | ||
enumerable: true, | ||
configurable: true, | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
} | ||
``` | ||
<a name="PropAt"></a> | ||
## PropAt : <code>Object</code> | ||
Description of a given level of the chain. Transformed Prop Object with location attched. | ||
**Properties** | ||
| Name | Type | Description | | ||
| --- | --- | --- | | ||
| [host] | [<code>Host</code>](#Host) | When a non-primitive key has been encountered, a separate chain will be created with that key. Items on that chain will be labeled with a 'host' property to specify which Host the path applies to. PropAt Objects lacking a 'host' property imply that the path applies to the initially supplied Host. | | ||
| path | [<code>Path</code>](#Path) | Describes the steps taken from the Host in order to reach the Prop's value. | | ||
| [value] | <code>\*</code> | Value described at the Prop's location (if any). In cases of a previously discovered reference (circular or otherwise), value will be replaced with a ref property (unless opt.showRefValues is true). | | ||
| [writable] | <code>boolean</code> | 'Writable' property descriptor of the value. | | ||
| [enumerable] | <code>boolean</code> | 'Enumerable' property descriptor of the value. | | ||
| [configurable] | <code>boolean</code> | 'Configurable' property descriptor of the value. | | ||
| [parentIsFrozen] | <code>boolean</code> | Frozen status of the parent object. | | ||
| [parentIsSealed] | <code>boolean</code> | Sealed status of the parent object. | | ||
| [parentIsExtensible] | <code>boolean</code> | Extensible status of the parent object. | | ||
| [ref] | [<code>Ref</code>](#Ref) | If the value strictly equals a previously discovered Target, the Host and Path of that Target will be provided. | | ||
**Example** | ||
```js | ||
{ | ||
path: [ 'foo', 'bar', 'baz', 'beh' ], | ||
value: 'qux', | ||
writable: true, | ||
enumerable: true, | ||
configurable: true, | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
} | ||
``` | ||
<a name="Options"></a> | ||
## Options : <code>Object</code> | ||
Execution-wide settings supplied to the module. | ||
Modifies types of data attached to results. | ||
Modifies types of children to extract. | ||
**Properties** | ||
| Name | Type | Default | Description | | ||
| --- | --- | --- | --- | | ||
| [inherited] | <code>boolean</code> | | Whether or not to search for inherited properties. Attaches these keys behind a '\_\_proto__' key. | | ||
| [own] | <code>boolean</code> | <code>true</code> | Whether or not to search for own properties. Defaults to true. | | ||
| [nonEnumerable] | <code>boolean</code> | | Whether or not to search for and return non-enumerable properties. | | ||
| [permissions] | <code>boolean</code> | | Whether or not to attach Permissions to results. | | ||
| [descriptors] | <code>boolean</code> | | Whether or not to attach property descriptors other than 'value' to results. | | ||
| [stepwise] | <code>boolean</code> | | Whether or not to yield a PropAt object at every step down the chain. | | ||
| [includeRefValues] | <code>boolean</code> | | Whether or not to attach a value to Props with Refs attached. | | ||
| [gen] | <code>boolean</code> | | Whether or not to return a generator instead of executing the entire search. | | ||
| [full] | <code>boolean</code> | | If true, replaces undefined options with maximum search settings (all options except for propsCustomizer will be set to true). User supplied options supercede any changes here. | | ||
| [propsCustomizer] | [<code>PropsCustomizer</code>](#PropsCustomizer) | | Function used for custom extraction of PropEntries from a Target. | | ||
**Example** | ||
```js | ||
{ | ||
inherited: true, | ||
own: true, | ||
nonEnumerable: true, | ||
permissions: true, | ||
descriptors: true, | ||
stepwise: true, | ||
includeRefValues: true, | ||
gen: true, | ||
full: true, | ||
propsCustomizer: target => { | ||
if (target instanceof ArrayBuffer && target.byteLength === 16) { | ||
return Object.entries( | ||
new Int8Array(target) | ||
).map(entry => ( | ||
[ | ||
entry[0], { value: entry[1] } | ||
] | ||
)) | ||
} | ||
} | ||
} | ||
``` | ||
* [deep-props](/docs/global.md) | ||
* [extract](/libs/extract/docs/global.md) |
1098
index.js
@@ -1,3 +0,1 @@ | ||
'use strict' | ||
/** | ||
@@ -10,44 +8,23 @@ * @author Justin Collier <jpcxme@gmail.com> | ||
/** | ||
* Instance of search generator with loaded parameters. Used when execution of an entire search is not necessary. A <code>for .. of</code> loop on this object will iterate over every result from the search. | ||
* Provides a collection of tools for performing operations on deeply nested object properties, prototypes, and object keys. Avoids stack limit violations by using task queues rather than recursion. Allows for custom execution settings including non-native dataset handling. | ||
* | ||
* @typedef {Object} Search | ||
* @namespace deep-props | ||
*/ | ||
/** | ||
* An Array of Arrays with Key at index 0 and a property descriptors object at index 1. | ||
* <ul><li>Equivalent to the result of <code>Object.entries(Object.getOwnPropertyDescriptors(Target))</code> | ||
* <li> Only relevant property descriptors should be added.</ul> | ||
* | ||
* @typedef {Array} DescriptorEntries | ||
* @example | ||
* [ | ||
* [ | ||
* 'foo', | ||
* { | ||
* value: 'bar', | ||
* writable: true, | ||
* enumerable: true, | ||
* configurable: true | ||
* } | ||
* ] | ||
* ] | ||
*/ | ||
'use strict' | ||
/** | ||
* Any kind of container that can be used as a target. | ||
* Only applicable if an adequate PropsCustomizer is supplied. | ||
* Custom dataset for use as a <a href="#~Container">Container</a>. May be accessed via valid customizer functions. | ||
* | ||
* @typedef {*} Custom | ||
* @typedef {*} deep-props~Custom | ||
* @example | ||
* // This is a kind of container that would require a PropsCustomizer function. | ||
* (() => { | ||
* class NonNativeDataStructure { | ||
* constructor(arr) { | ||
* const values = arr | ||
* this.get = i => values[i] | ||
* this.getValues = () => values | ||
* this.push = x => values.push(x) | ||
* class CustomDataStructure { | ||
* constructor(array) { | ||
* this.get = i => array[i] | ||
* this.getValues = () => array | ||
* this.push = x => array.push(x) | ||
* } | ||
* } | ||
* return new NonNativeDataStructure([ 'foo', 'bar' ]) | ||
* return new CustomDataStructure([ 'foo', 'bar' ]) | ||
* })() | ||
@@ -57,1060 +34,27 @@ */ | ||
/** | ||
* Function supplied in Options that handles Target objects and returns a descriptor matrix of any children within a Custom container. Returns undefined if not applicable. | ||
* Key used for accessing a child property within a container. When its value is <code>'__proto__'</code>, it is used as a stand-in for <code>Object.getPrototypeOf()</code>. | ||
* | ||
* @typedef {Function} PropsCustomizer | ||
* @param {Target} target - Container to analyze for additional children. | ||
* @returns {DescriptorEntries} Array of arrays of keys and property descriptor objects. | ||
* @example | ||
* target => { | ||
* if (target instanceof ArrayBuffer && target.byteLength === 16) { | ||
* // Mapping the output of Object.entries to a DescriptorEntries array. | ||
* return Object.entries( | ||
* new Int8Array(target) | ||
* ).map(entry => ( | ||
* [ | ||
* entry[0], { value: entry[1] } | ||
* ] | ||
* )) | ||
* } | ||
* } | ||
* @typedef {(string|deep-props~Container)} deep-props~Key | ||
*/ | ||
/** | ||
* Unpacking target for finding children. | ||
* Retrieved from the first value in the queue. | ||
* Used to supply the queue with more potential targets within Prop objects. | ||
* Container object used as a target for child property extraction. | ||
* | ||
* @typedef {(Object|Array|Map|Set|Custom)} Target | ||
* @typedef {(Object|Array|Map|WeakMap|Set|WeakSet|deep-props~Custom)} deep-props~Container | ||
*/ | ||
/** | ||
* The result of the Object permissions tests for a Target object. | ||
* A non-primitive <a href="#~Container">Container</a> which represents the root of a given path. | ||
* | ||
* @typedef {Object} Permissions | ||
* @property {boolean} parentIsFrozen - Result of Object.isFrozen(Target) | ||
* @property {boolean} parentIsSealed - Result of Object.isSealed(Target) | ||
* @property {boolean} parentIsExtensible - Result of Object.isExtensible(Target) | ||
* @typedef {deep-props~Container} deep-props~Host | ||
*/ | ||
/** | ||
* Key used for accessing a child value from a Target object. | ||
* Generator object which yields stepwise operation results. | ||
* | ||
* @typedef {*} Key | ||
* @typedef {Object} deep-props~ResultGenerator | ||
*/ | ||
/** | ||
* Array of keys used to describe the children of a Host at each level above a given Prop. | ||
* | ||
* @typedef {Key[]} Path | ||
*/ | ||
/** | ||
* A non-primitive container which represents the root of a given Path. | ||
* | ||
* @typedef {Target} Host | ||
*/ | ||
/** | ||
* Describes the location of a previously encountered target. | ||
* | ||
* @typedef {Object} Ref | ||
* @property {Host} [host] - If Host is different than the supplied Host, it will be specified. | ||
* @property {Path} path - Path of previously encountered target. | ||
* @example | ||
* { path: [ 'foo', 'bar' ] } | ||
* @example | ||
* { host: { foo: 'bar' }, path: [ 'baz', 'beh' ] } | ||
*/ | ||
/** | ||
* Description of the properties found for a given value during the search, | ||
* | ||
* @typedef {Object} Prop | ||
* @property {Path} key - Key used on the parent (Target) object to retrieve the value. | ||
* @property {*} [value] - Value described at the Prop's location (if any). In cases of a previously discovered reference (circular or otherwise), value will be replaced with a ref property (unless opt.showRefValues is true). | ||
* @property {boolean} [writable] - 'Writable' property descriptor of the value. | ||
* @property {boolean} [enumerable] - 'Enumerable' property descriptor of the value. | ||
* @property {boolean} [configurable] - 'Configurable' property descriptor of the value. | ||
* @property {boolean} [parentIsFrozen] - Frozen status of the parent object. | ||
* @property {boolean} [parentIsSealed] - Sealed status of the parent object. | ||
* @property {boolean} [parentIsExtensible] - Extensible status of the parent object. | ||
* @example | ||
* { | ||
* key: 'foo', | ||
* value: 'bar', | ||
* writable: true, | ||
* enumerable: true, | ||
* configurable: true, | ||
* parentIsFrozen: false, | ||
* parentIsSealed: false, | ||
* parentIsExtensible: true | ||
* } | ||
*/ | ||
/** | ||
* Description of a given level of the chain. Transformed Prop Object with location attched. | ||
* | ||
* @typedef {Object} PropAt | ||
* @property {Host} [host] - When a non-primitive key has been encountered, a separate chain will be created with that key. Items on that chain will be labeled with a 'host' property to specify which host the path applies to. PropAt Objects lacking a 'host' property imply that the path applies to the initially supplied Host. | ||
* @property {Path} path - Describes the steps taken from the Host in order to reach the Prop's value. | ||
* @property {*} [value] - Value described at the Prop's location (if any). In cases of a previously discovered reference (circular or otherwise), value will be replaced with a ref property (unless opt.showRefValues is true). | ||
* @property {boolean} [writable] - 'Writable' property descriptor of the value. | ||
* @property {boolean} [enumerable] - 'Enumerable' property descriptor of the value. | ||
* @property {boolean} [configurable] - 'Configurable' property descriptor of the value. | ||
* @property {boolean} [parentIsFrozen] - Frozen status of the parent object. | ||
* @property {boolean} [parentIsSealed] - Sealed status of the parent object. | ||
* @property {boolean} [parentIsExtensible] - Extensible status of the parent object. | ||
* @property {Ref} [ref] - If the value strictly equals a previously discovered Target, the Host and Path of that Target will be provided. | ||
* @example | ||
* { | ||
* path: [ 'foo', 'bar', 'baz', 'beh' ], | ||
* value: 'qux', | ||
* writable: true, | ||
* enumerable: true, | ||
* configurable: true, | ||
* parentIsFrozen: false, | ||
* parentIsSealed: false, | ||
* parentIsExtensible: true | ||
* } | ||
*/ | ||
/** | ||
* Execution-wide settings supplied to the module. | ||
* Modifies types of data attached to results. | ||
* Modifies types of children to extract. | ||
* | ||
* @typedef {Object} Options | ||
* @property {boolean} [inherited] - Whether or not to search for inherited properties. Attaches these keys behind a '\_\_proto__' key. | ||
* @property {boolean} [own=true] - Whether or not to search for own properties. Defaults to true. | ||
* @property {boolean} [nonEnumerable] - Whether or not to search for and return non-enumerable properties. | ||
* @property {boolean} [permissions] - Whether or not to attach Permissions to results. | ||
* @property {boolean} [descriptors] - Whether or not to attach property descriptors other than 'value' to results. | ||
* @property {boolean} [stepwise] - Whether or not to yield a PropAt object at every step down the chain. | ||
* @property {boolean} [includeRefValues] - Whether or not to attach a value to Props with Refs attached. | ||
* @property {boolean} [gen] - Whether or not to return a generator instead of executing the entire search. | ||
* @property {boolean} [full] - If true, replaces undefined Options with maximum search settings (All options except for propsCustomizer will be set to true). User supplied options supercede any changes here. | ||
* @property {PropsCustomizer} [propsCustomizer] - Function used for custom extraction of PropEntries from a Target. | ||
* @example | ||
* { | ||
* inherited: true, | ||
* own: true, | ||
* nonEnumerable: true, | ||
* permissions: true, | ||
* descriptors: true, | ||
* stepwise: true, | ||
* includeRefValues: true, | ||
* gen: true, | ||
* full: true, | ||
* propsCustomizer: target => { | ||
* if (target instanceof ArrayBuffer && target.byteLength === 16) { | ||
* return Object.entries( | ||
* new Int8Array(target) | ||
* ).map(entry => ( | ||
* [ | ||
* entry[0], { value: entry[1] } | ||
* ] | ||
* )) | ||
* } | ||
* } | ||
* } | ||
*/ | ||
/** | ||
* Determines if x is a JS primitive. | ||
* Used to determine if a value should be unpacked. | ||
* | ||
* @param {*} x - Test value. | ||
* @returns {boolean} True if primitive, false if not. | ||
* @example | ||
* // returns true | ||
* | ||
* isPrimitive('foo') | ||
* @example | ||
* // returns false | ||
* | ||
* isPrimitive({}) | ||
*/ | ||
const isPrimitive = x => ( | ||
typeof x === 'string' || | ||
typeof x === 'number' || | ||
typeof x === 'boolean' || | ||
typeof x === 'symbol' || | ||
x === null || | ||
x === undefined | ||
) | ||
/** | ||
* Gets the frozen, sealed, and extensible statuses of an object. | ||
* | ||
* @param {Target} target - Target object. | ||
* @returns {Permissions} Result of the three Object permissions tests. | ||
* @example | ||
* // returns { | ||
* // parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true | ||
* // } | ||
* | ||
* getObjectPermissions({}) | ||
*/ | ||
const getObjectPermissions = target => ({ | ||
parentIsFrozen: Object.isFrozen(target), | ||
parentIsSealed: Object.isSealed(target), | ||
parentIsExtensible: Object.isExtensible(target) | ||
}) | ||
/** | ||
* Converts list of descriptors to prop Array. | ||
* Attaches information based on options. | ||
* | ||
* @param {DescriptorEntries} descriptorEntries - Matrix of keys and descriptors. | ||
* @param {Permissions} permissions - Permissions list. | ||
* @param {Options} opt - Execution settings. | ||
* @returns {Prop[]} Converted 1D Array of properties. | ||
* @example | ||
* | ||
* // returns [ | ||
* // { | ||
* // key: 'foo', | ||
* // value: 'bar', | ||
* // writable: true, | ||
* // enumerable: true, | ||
* // configurable: true, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* genPropsFromDescriptorEntries( | ||
* [ | ||
* [ | ||
* 'foo', | ||
* { | ||
* value: 'bar', | ||
* writable: true, | ||
* enumerable: true, | ||
* configurable: true | ||
* } | ||
* ] | ||
* ], | ||
* { parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
* { descriptors: true, permissions: true } | ||
* ) | ||
*/ | ||
const genPropsFromDescriptorEntries = (descriptorEntries, permissions, opt) => ( | ||
descriptorEntries.map( | ||
entry => { | ||
let prop | ||
if (opt.descriptors === true) { | ||
prop = { key: entry[0], ...entry[1] } | ||
} else { | ||
prop = { key: entry[0] } | ||
if (entry[1].hasOwnProperty('value')) { | ||
prop.value = entry[1].value | ||
} | ||
} | ||
if (opt.permissions === true) { | ||
prop = { ...prop, ...permissions } | ||
} | ||
return prop | ||
} | ||
) | ||
) | ||
/** | ||
* Generates a prop for a target object's prototype. | ||
* | ||
* @param {Target} target - Target object. | ||
* @param {Permissions} permissions - Object permission statuses. | ||
* @param {Options} opt - Execution settings. | ||
* @returns {Prop[]} Array with single entry of '\_\_proto__' key and value. | ||
* @example | ||
* // returns [ | ||
* // { | ||
* // key: '__proto__', | ||
* // value: {}, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* genProtoProp( | ||
* {}, | ||
* { parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
* { descriptors: true, permissions: true } | ||
* ) | ||
*/ | ||
const genProtoProp = (target, permissions, opt) => ( | ||
genPropsFromDescriptorEntries( | ||
[[ | ||
'__proto__', | ||
{ value: Object.getPrototypeOf(target) } | ||
]], | ||
permissions, | ||
opt | ||
) | ||
) | ||
/** | ||
* Generates a list of non-inherited properties of a target object. | ||
* | ||
* @param {Target} target - Target object. | ||
* @param {Permissions} permissions - Object permission statuses. | ||
* @param {Options} opt - Execution settings. | ||
* @returns {Prop[]} Array of associated properties. | ||
* @example | ||
* // returns [ | ||
* // { | ||
* // key: 'foo', | ||
* // value: 'bar', | ||
* // writable: true, | ||
* // enumerable: true, | ||
* // configurable: true, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // }, | ||
* // { | ||
* // key: 'baz', | ||
* // value: 'beh', | ||
* // writable: true, | ||
* // enumerable: true, | ||
* // configurable: true, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* getOwnProps( | ||
* { foo: 'bar', baz: 'beh' }, | ||
* { parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
* { descriptors: true, permissions: true } | ||
* ) | ||
*/ | ||
const getOwnProps = (target, permissions, opt) => ( | ||
genPropsFromDescriptorEntries( | ||
Object.entries( | ||
Object.getOwnPropertyDescriptors( | ||
target | ||
) | ||
).filter( | ||
entry => { | ||
if ( | ||
opt.nonEnumerable === true || | ||
entry[1].enumerable !== false | ||
) { | ||
return entry | ||
} | ||
} | ||
), | ||
permissions, | ||
opt | ||
) | ||
) | ||
/** | ||
* Gets a list of properties within a target Map. | ||
* | ||
* @param {Target} target - Target object. | ||
* @param {Permissions} permissions - Object permission statuses. | ||
* @param {Options} opt - Execution settings. | ||
* @returns {Prop[]} Array of associated properties. | ||
* @example | ||
* // returns [ | ||
* // { | ||
* // key: 'foo', | ||
* // value: 'bar', | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // }, | ||
* // { | ||
* // key: { baz: 'beh' }, | ||
* // value: { qux: 'quz' }, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* getMapProps( | ||
* new Map([ | ||
* [ 'foo', 'bar' ], | ||
* [ { baz: 'beh' }, { qux: 'quz' } ] | ||
* ]), | ||
* { parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
* { descriptors: true, permissions: true } | ||
* ) | ||
*/ | ||
const getMapProps = (target, permissions, opt) => ( | ||
genPropsFromDescriptorEntries( | ||
Object.entries( | ||
Object.getOwnPropertyDescriptors( | ||
[...target] | ||
) | ||
).map( | ||
entry => { | ||
if (entry[0] !== 'length') { | ||
return [ | ||
entry[1].value[0], | ||
{ value: entry[1].value[1] } | ||
] | ||
} | ||
} | ||
).filter(x => x !== undefined), | ||
permissions, | ||
opt | ||
) | ||
) | ||
/** | ||
* Gets a list of properties within a target Set. | ||
* Uses insertion order as keys. | ||
* | ||
* @param {Target} target - Target object. | ||
* @param {Permissions} permissions - Object permission statuses. | ||
* @param {Options} opt - Execution settings. | ||
* @returns {Prop[]} Array of associated properties. | ||
* @example | ||
* // returns [ | ||
* // { | ||
* // key: '0', | ||
* // value: 1, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // }, | ||
* // { | ||
* // key: '1', | ||
* // value: 2, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* getSetProps( | ||
* new Set([ 1, 2 ]), | ||
* { parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
* { descriptors: true, permissions: true } | ||
* ) | ||
*/ | ||
const getSetProps = (target, permissions, opt) => ( | ||
genPropsFromDescriptorEntries( | ||
Object.entries( | ||
Object.getOwnPropertyDescriptors( | ||
[...target] | ||
) | ||
).map( | ||
entry => { | ||
if (entry[0] !== 'length') { | ||
return [ | ||
entry[0], | ||
{ value: entry[1].value } | ||
] | ||
} | ||
} | ||
).filter(x => x !== undefined), | ||
permissions, | ||
opt | ||
) | ||
) | ||
/** | ||
* Gets any special object properties. | ||
* If propsCustomizer is supplied, and returns a defined value from target, then getSpecialProps will return this value. | ||
* | ||
* @param {Target} target - Target object. | ||
* @param {Permissions} permissions - Object permission statuses. | ||
* @param {Options} opt - Options. | ||
* @returns {Prop[]} Array of associated properties. | ||
* @example | ||
* const map = new Map( | ||
* [ | ||
* [ 'foo', 'bar' ] | ||
* ] | ||
* ) | ||
* | ||
* // returns [ | ||
* // { | ||
* // key: 'foo', | ||
* // value: 'bar', | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* getSpecialProps( | ||
* map, | ||
* { parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
* { descriptors: true, permissions: true } | ||
* ) | ||
* @example | ||
* const s = new Set( | ||
* [ 'baz', 'beh' ] | ||
* ) | ||
* | ||
* // returns [ | ||
* // { | ||
* // key: '0', | ||
* // value: 'baz', | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // }, | ||
* // { | ||
* // key: '1', | ||
* // value: 'beh', | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* getSpecialProps( | ||
* s, | ||
* { parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
* { descriptors: true, permissions: true } | ||
* ) | ||
* @example | ||
* class NonNativeDataStructure { | ||
* constructor(arr) { | ||
* const values = arr | ||
* this.get = i => values[i] | ||
* this.getValues = () => values | ||
* this.push = x => values.push(x) | ||
* } | ||
* } | ||
* | ||
* const custom = new NonNativeDataStructure([ 'qux', 'quz' ]) | ||
* | ||
* const propsCustomizer = target => { | ||
* if (target instanceof NonNativeDataStructure) { | ||
* return ( | ||
* Object.entries( | ||
* Object.getOwnPropertyDescriptors( | ||
* target.getValues() | ||
* ) | ||
* ).filter( | ||
* entry => entry[1].enumerable !== false | ||
* ) | ||
* ) | ||
* } | ||
* } | ||
* | ||
* // returns [ | ||
* // { | ||
* // key: '0', | ||
* // value: 'qux', | ||
* // writable: true, | ||
* // enumerable: true, | ||
* // configurable: true, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // }, | ||
* // { | ||
* // key: '1', | ||
* // value: 'quz', | ||
* // writable: true, | ||
* // enumerable: true, | ||
* // configurable: true, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* getSpecialProps( | ||
* custom, | ||
* { parentIsFrozen: false, parentIsSealed: false, parentIsExtensible: true }, | ||
* { descriptors: true, permissions: true, propsCustomizer } | ||
* ) | ||
*/ | ||
const getSpecialProps = (target, permissions, opt) => { | ||
let customProps | ||
if (opt.propsCustomizer instanceof Function) { | ||
customProps = opt.propsCustomizer(target) | ||
} | ||
if (customProps !== undefined) { | ||
return ( | ||
genPropsFromDescriptorEntries( | ||
customProps, permissions, opt | ||
) | ||
) | ||
} | ||
if (target instanceof Map) return getMapProps(target, permissions, opt) | ||
if (target instanceof Set) return getSetProps(target, permissions, opt) | ||
return [] | ||
module.exports = { | ||
extract: require('./libs/extract') | ||
} | ||
/** | ||
* Returns all inherited properties, own properties, special properties, and object permissions. | ||
* | ||
* @param {Target} target - Target object. | ||
* @param {Options} opt - Execution settings. | ||
* @returns {Prop[]} Array of associated properties. | ||
* @example | ||
* const map = new Map( | ||
* [ | ||
* [ 'foo', 'bar' ] | ||
* ] | ||
* ) | ||
* | ||
* // returns [ | ||
* // { | ||
* // key: '__proto__', | ||
* // value: Map {}, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // }, | ||
* // { | ||
* // key: 'foo', | ||
* // value: 'bar', | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* getProps(map, { inherited: true, descriptors: true, permissions: true }) | ||
*/ | ||
const getProps = (target, opt) => { | ||
if (isPrimitive(target)) return [] | ||
const permissions = ( | ||
opt.permissions === true | ||
? getObjectPermissions(target) | ||
: {} | ||
) | ||
const proto = ( | ||
opt.inherited === true | ||
? genProtoProp(target, permissions, opt) | ||
: [] | ||
) | ||
const ownProps = ( | ||
opt.own === true | ||
? getOwnProps(target, permissions, opt) | ||
: [] | ||
) | ||
return [ | ||
...proto, | ||
...ownProps, | ||
...getSpecialProps(target, permissions, opt) | ||
] | ||
} | ||
/** | ||
* Assigns reference points to a list of properties. | ||
* | ||
* @param {Prop[]} props - Prop array. | ||
* @param {Host} [host] - Host object. | ||
* @param {Path} [path=[]] - Path to current proplist. | ||
* @returns {PropAt[]} Array of location-tagged Props. | ||
* @example | ||
* let props = [ | ||
* { | ||
* key: 'foo', | ||
* value: 'bar', | ||
* parentIsFrozen: false, | ||
* parentIsSealed: false, | ||
* parentIsExtensible: true | ||
* }, | ||
* { | ||
* key: { baz: 'beh' }, | ||
* value: { qux: 'quz' }, | ||
* parentIsFrozen: false, | ||
* parentIsSealed: false, | ||
* parentIsExtensible: true | ||
* } | ||
* ] | ||
* | ||
* // returns [ | ||
* // { | ||
* // path: ['foo'], | ||
* // value: 'bar', | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // }, | ||
* // { | ||
* // path: [{ foo: 'bar' }], | ||
* // value: { qux: 'quz' }, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* assignReferencePoints(props) | ||
* @example | ||
* props = [ | ||
* { | ||
* key: 'qux', | ||
* value: 'quz', | ||
* parentIsFrozen: false, | ||
* parentIsSealed: false, | ||
* parentIsExtensible: true | ||
* } | ||
* ] | ||
* | ||
* // returns [ | ||
* // { | ||
* // host: { foo: 'bar' }, | ||
* // path: ['qux'], | ||
* // value: 'quz', | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // } | ||
* // ] | ||
* | ||
* assignReferencePoints(props, { foo: 'bar' }) | ||
*/ | ||
const assignReferencePoints = (props, host, path = []) => ( | ||
props.map(prop => { | ||
const cur = {} | ||
if (host !== undefined) cur.host = host | ||
cur.path = path.concat(prop.key) | ||
for (let key in prop) { | ||
if (key !== 'key') cur[key] = prop[key] | ||
} | ||
return cur | ||
}) | ||
) | ||
/** | ||
* Non-recursively searches through the host object by queueing its children. | ||
* Attaches information based on options. | ||
* Determines whether child should be unpacked by checking if it is a primitive. | ||
* Keeps track of all object references encountered to avoid circular looping. | ||
* Explores object keys via creation of a new Host. | ||
* | ||
* @generator | ||
* @param {Host} host - Host container supplied to module. | ||
* @param {Options} opt - Execution settings. | ||
* @yields {PropAt} Current Prop with attached location. | ||
* @returns {undefined} Undefined if done. | ||
* @example | ||
* // Searching through an Object | ||
* | ||
* const data = { | ||
* foo: { | ||
* bar: { | ||
* baz: { | ||
* beh: 'qux' | ||
* } | ||
* } | ||
* } | ||
* } | ||
* | ||
* const query = search(data, { own: true }) | ||
* for (let step of query) { | ||
* // iterates once: | ||
* // 1: step === { path: [ 'foo', 'bar', 'baz', 'beh' ], value: 'qux' } | ||
* } | ||
* @example | ||
* // Searching through an multi-nested Object | ||
* | ||
* const data = { | ||
* foo: { | ||
* beh: { | ||
* lorem: 'ex' | ||
* } | ||
* }, | ||
* bar: { | ||
* qux: { | ||
* ipsum: 'igne' | ||
* } | ||
* }, | ||
* baz: { | ||
* quz: { | ||
* dolor: 'vita' | ||
* } | ||
* } | ||
* } | ||
* | ||
* const query = search(data, { own: true }) | ||
* for (let step of query) { | ||
* // iterates 3 times: | ||
* // 1: step === { path: [ 'foo', 'beh', 'lorem' ], value: 'ex' } | ||
* // 2: step === { path: [ 'bar', 'qux', 'ipsum' ], value: 'igne' } | ||
* // 3: step === { path: [ 'baz', 'quz', 'dolor' ], value: 'vita' } | ||
* } | ||
*/ | ||
const search = function * (host, opt) { | ||
if (isPrimitive(host)) return [] | ||
const hostsExtracted = new WeakSet() | ||
const visited = new WeakMap() | ||
let Q = assignReferencePoints(getProps(host, opt)) | ||
hostsExtracted.add(host) | ||
visited.set(host, { path: [] }) | ||
while (Q.length > 0) { | ||
const cur = Q.shift() | ||
if (visited.has(cur.value)) { | ||
const ref = { ...cur, ref: visited.get(cur.value) } | ||
if (opt.includeRefValues !== true) delete ref.value | ||
yield ref | ||
continue | ||
} | ||
if (opt.stepwise === true) { | ||
yield cur | ||
} | ||
cur.path.forEach(step => { | ||
if (!isPrimitive(step) && !hostsExtracted.has(step)) { | ||
Q = assignReferencePoints( | ||
getProps(step, opt), step | ||
).concat(Q) | ||
hostsExtracted.add(step) | ||
visited.set(step, { host: step, path: [] }) | ||
} | ||
}) | ||
if (!isPrimitive(cur.value)) { | ||
const toQ = assignReferencePoints( | ||
getProps(cur.value, opt), cur.host, cur.path | ||
) | ||
if (cur.hasOwnProperty('host')) { | ||
visited.set(cur.value, { host: cur.host, path: cur.path }) | ||
} else { | ||
visited.set(cur.value, { path: cur.path }) | ||
} | ||
if (toQ.length === 0 && opt.stepwise !== true) { | ||
yield cur | ||
} | ||
Q = toQ.concat(Q) | ||
} else if (opt.stepwise !== true) { | ||
yield cur | ||
} | ||
} | ||
} | ||
/** | ||
* Merges supplied options with defaults. | ||
* | ||
* @param {Options} opt - Options passed to the module. | ||
* @returns {Options} Execution settings. | ||
* @example | ||
* // returns { own: true } | ||
* | ||
* mergeOptions({}) | ||
* @example | ||
* // returns { own: false, inherited: true } | ||
* | ||
* mergeOptions({ own: false, inherited: true }) | ||
* @example | ||
* // returns { | ||
* // inherited: true, | ||
* // own: true, | ||
* // nonEnumerable: true, | ||
* // permissions: true, | ||
* // descriptors: true, | ||
* // stepwise: true, | ||
* // includeRefValues: true, | ||
* // full: true | ||
* // } | ||
* | ||
* mergeOptions({ full: true }) | ||
* @example | ||
* // returns { | ||
* // inherited: true, | ||
* // own: true, | ||
* // nonEnumerable: true, | ||
* // permissions: true, | ||
* // descriptors: false, | ||
* // stepwise: true, | ||
* // includeRefValues: true, | ||
* // full: true | ||
* // } | ||
* | ||
* mergeOptions({ full: true, descriptors: false }) | ||
*/ | ||
const mergeOptions = opt => { | ||
const defaultOptions = { | ||
own: true | ||
} | ||
const fullOptions = { | ||
inherited: true, | ||
own: true, | ||
nonEnumerable: true, | ||
permissions: true, | ||
descriptors: true, | ||
stepwise: true, | ||
includeRefValues: true | ||
} | ||
if (opt.full === true) { | ||
return { | ||
...defaultOptions, | ||
...fullOptions, | ||
...opt | ||
} | ||
} else { | ||
return { | ||
...defaultOptions, | ||
...opt | ||
} | ||
} | ||
} | ||
/** | ||
* Creates an array of deep paths and properties associated with an object. Non-recursively iterates through unpacked children until an endpoint is reached. Optionally traverses prototypes and non-enumerable properties. Endpoints may be previously discovered object references, primitives, or objects without children. | ||
* | ||
* @module deepProps | ||
* @exports deepProps | ||
* @param {Host} host - Object to unpack. | ||
* @param {Options} [opt={}] - Execution settings. | ||
* @returns {(PropAt[]|Search)} Array of paths and values or references. Returns Search generator if opt.gen is true. | ||
* @example | ||
* // Simple nested object | ||
* | ||
* const data = { | ||
* foo: { | ||
* bar: { | ||
* baz: { | ||
* beh: 'qux' | ||
* } | ||
* } | ||
* } | ||
* } | ||
* | ||
* // returns [{ path: [ 'foo', 'bar', 'baz', 'beh' ], val: 'qux' }] | ||
* | ||
* deepProps(data) | ||
* @example | ||
* // Multi-nested object | ||
* | ||
* const data = { | ||
* foo: { | ||
* beh: { | ||
* lorem: 'ex' | ||
* } | ||
* }, | ||
* bar: { | ||
* qux: { | ||
* ipsum: 'igne' | ||
* } | ||
* }, | ||
* baz: { | ||
* quz: { | ||
* dolor: 'vita' | ||
* } | ||
* } | ||
* } | ||
* | ||
* // returns [ | ||
* // { path: [ 'foo', 'beh', 'lorem' ], val: 'ex' }, | ||
* // { path: [ 'bar', 'qux', 'ipsum' ], val: 'igne' }, | ||
* // { path: [ 'baz', 'quz', 'dolor' ], val: 'vita' } | ||
* // ] | ||
* | ||
* deepProps(data) | ||
* @example | ||
* // Unrooting of Object Keys | ||
* | ||
* const data = new Map().set( | ||
* { foo: 'bar' }, new Map().set( | ||
* { baz: 'beh' }, new Map().set( | ||
* { qux: 'quz' }, new Map().set( | ||
* { quux: 'quuz' }, 'thud' | ||
* ) | ||
* ) | ||
* ) | ||
* ) | ||
* | ||
* // returns: | ||
* // [ | ||
* // { | ||
* // path: [ { foo: 'bar' }, { baz: 'beh' }, { qux: 'quz' }, { quux: 'quuz' } ], | ||
* // value: 'thud' | ||
* // }, | ||
* // { host: { quux: 'quuz' }, path: ['quux'], value: 'quuz' }, | ||
* // { host: { qux: 'quz' }, path: ['qux'], value: 'quz' }, | ||
* // { host: { baz: 'beh' }, path: ['baz'], value: 'beh' }, | ||
* // { host: { foo: 'bar' }, path: ['foo'], value: 'bar' } | ||
* // ] | ||
* | ||
* props(data) | ||
* @example | ||
* // Extraction from complicated nests | ||
* | ||
* const data = { | ||
* foo: [ | ||
* new Map().set( | ||
* 'bar', new Set([ | ||
* { | ||
* baz: { | ||
* qux: { | ||
* quz: [ | ||
* 'quux', | ||
* 'quuz' | ||
* ] | ||
* } | ||
* } | ||
* }, | ||
* { | ||
* lorem: { | ||
* ipsum: 'dolor' | ||
* } | ||
* } | ||
* ]) | ||
* ) | ||
* ] | ||
* } | ||
* | ||
* // returns: | ||
* // [ | ||
* // { | ||
* // path: [ 'foo', '0', 'bar', '0', 'baz', 'qux', 'quz', '0' ], | ||
* // value: 'quux' }, | ||
* // { path: [ 'foo', '0', 'bar', '0', 'baz', 'qux', 'quz', '1' ], | ||
* // value: 'quuz' }, | ||
* // { path: [ 'foo', '0', 'bar', '1', 'lorem', 'ipsum' ], | ||
* // value: 'dolor' | ||
* // } | ||
* // ] | ||
* | ||
* props(data) | ||
* @example | ||
* // Verbose Options | ||
* | ||
* const data = { foo: { bar: 'baz' } } | ||
* Object.freeze(data.foo) | ||
* | ||
* // returns: | ||
* // [ | ||
* // { | ||
* // path: ['foo'], | ||
* // value: { bar: 'baz' }, | ||
* // writable: true, | ||
* // enumerable: true, | ||
* // configurable: true, | ||
* // parentIsFrozen: false, | ||
* // parentIsSealed: false, | ||
* // parentIsExtensible: true | ||
* // }, | ||
* // { | ||
* // path: [ 'foo', 'bar' ], | ||
* // value: 'baz', | ||
* // writable: false, | ||
* // enumerable: true, | ||
* // configurable: false, | ||
* // parentIsFrozen: true, | ||
* // parentIsSealed: true, | ||
* // parentIsExtensible: false | ||
* // } | ||
* // ] | ||
* | ||
* props(data, { stepwise: true, descriptors: true, permissions: true }) | ||
*/ | ||
const deepProps = (host, opt = {}) => { | ||
opt = mergeOptions(opt) | ||
if (opt.gen === true) return search(host, opt) | ||
const tree = [] | ||
for (let res of search(host, opt)) tree.push(res) | ||
return tree | ||
} | ||
module.exports = deepProps |
{ | ||
"name": "deep-props", | ||
"version": "0.0.8", | ||
"description": "Creates an array of deep paths and properties associated with an object. Non-recursively iterates through deep objects until an endpoint is reached. Optionally unpacks prototypes and non-enumerable property descriptors. Supports Objects, Arrays, Maps, and Sets automatically.", | ||
"version": "0.1.0", | ||
"description": "Provides a collection of tools for performing operations on deeply nested object properties, prototypes, and object keys. Avoids stack limit violations by using task queues rather than recursion. Allows for custom execution settings including non-native dataset handling.", | ||
"engines": { | ||
@@ -11,7 +11,4 @@ "node": ">=8.7.0" | ||
"test": "node test.js", | ||
"prepare": "npm run-script check-linting; npm test", | ||
"build-docs": "jsdoc index.js --readme README.md", | ||
"check-linting": "eslint index.js; eslint test.js", | ||
"print-markdown": "jsdoc2md index.js", | ||
"minify": "uglifyjs --source-map filename=\"index.min.js.map\" --output index.min.js index.js -c -m --toplevel --comments" | ||
"prepare": "npm run-script build-docs; npm test", | ||
"build-docs": "jsdoc . -c build/.jsdoc.conf.json --readme README.md -d build/jsdoc; node build/markdown.js; rm -r build/jsdoc" | ||
}, | ||
@@ -28,3 +25,4 @@ "keywords": [ | ||
"map", | ||
"set" | ||
"set", | ||
"tools" | ||
], | ||
@@ -44,7 +42,6 @@ "author": "Justin Collier", | ||
"jsdoc": "^3.5.5", | ||
"jsdoc-to-markdown": "^4.0.1", | ||
"standard": "^11.0.1", | ||
"uglify-es": "^3.3.9" | ||
}, | ||
"dependencies": {} | ||
"turndown": "^4.0.2", | ||
"turndown-plugin-gfm": "^1.0.1" | ||
} | ||
} |
204
README.md
@@ -5,12 +5,13 @@ # deep-props | ||
Creates an array of deep paths and properties associated with an object. Non-recursively iterates through deep objects until an endpoint is reached. Optionally unpacks prototypes and non-enumerable property descriptors. Supports Objects, Arrays, Maps, and Sets automatically. | ||
__Migration notice: users of deep-props ≤ v0.0.8 must replace module calls with deep-props.extract. See the [deployment instructions](#deployment) below. All other functionality is the same.__ | ||
Endpoints may be previously discovered object references, primitives, or objects whose children are inaccessible due to settings or otherwise. | ||
Provides a collection of tools for performing operations on deeply nested object properties, prototypes, and object keys. Avoids stack limit violations by using task queues rather than recursion. Allows for custom execution settings including non-native dataset handling. | ||
Avoids recursion by using a task queue; very deep objects may be traversed without hitting the stack limit. | ||
<a name="submodules"></a> | ||
#### Submodules: | ||
+ __[extract](/libs/extract/README.md)__ | ||
Any unsupported data structure may be accessed by supplying a customizer function. See [the global docs](https://github.com/jpcx/deep-props/blob/master/docs/global.md#PropsCustomizer). | ||
[![NPM](https://nodei.co/npm/deep-props.extract.png?mini=true)](https://nodei.co/npm/deep-props.extract/) | ||
+ Creates an array of deep paths and properties associated with an object. Non-recursively iterates through deep objects until an endpoint is reached. Optionally unpacks prototypes and non-enumerable property descriptors. Supports Objects, Arrays, Maps, and Sets automatically. | ||
Circular references or otherwise duplicate references to objects will be signified using a 'ref' property, rather than a value. See the [return details](#PropAt). | ||
## Getting Started | ||
@@ -24,6 +25,10 @@ | ||
``` | ||
Installing all modules: | ||
```console | ||
npm install deep-props | ||
``` | ||
Submodules may be installed individually. See [the module list](#submodules) above. | ||
### Testing | ||
@@ -33,6 +38,7 @@ | ||
``` | ||
```console | ||
npm test --prefix /path/to/node_modules/deep-props | ||
``` | ||
<a name="deployment"></a> | ||
### Deployment | ||
@@ -42,185 +48,33 @@ | ||
const props = require('deep-props') | ||
const extract = props.extract | ||
``` | ||
### Usage | ||
**Nested object extraction** | ||
```js | ||
const data = { foo: { bar: { baz: 'qux' } } } | ||
// returns { path: [ 'foo', 'bar', 'baz' ], value: 'qux' } | ||
props(data) | ||
``` | ||
**Unrooting of Object Keys** | ||
```js | ||
const data = new Map().set( | ||
{ foo: 'bar' }, new Map().set( | ||
{ baz: 'beh' }, new Map().set( | ||
{ qux: 'quz' }, new Map().set( | ||
{ quux: 'quuz' }, 'thud' | ||
) | ||
) | ||
) | ||
) | ||
// returns: | ||
// [ | ||
// { | ||
// path: [ { foo: 'bar' }, { baz: 'beh' }, { qux: 'quz' }, { quux: 'quuz' } ], | ||
// value: 'thud' | ||
// }, | ||
// { host: { quux: 'quuz' }, path: ['quux'], value: 'quuz' }, | ||
// { host: { qux: 'quz' }, path: ['qux'], value: 'quz' }, | ||
// { host: { baz: 'beh' }, path: ['baz'], value: 'beh' }, | ||
// { host: { foo: 'bar' }, path: ['foo'], value: 'bar' } | ||
// ] | ||
props(data) | ||
``` | ||
**Extraction from complicated nests** | ||
```js | ||
const data = { | ||
foo: [ | ||
new Map().set( | ||
'bar', new Set([ | ||
{ | ||
baz: { | ||
qux: { | ||
quz: [ | ||
'quux', | ||
'quuz' | ||
] | ||
} | ||
} | ||
}, | ||
{ | ||
lorem: { | ||
ipsum: 'dolor' | ||
} | ||
} | ||
]) | ||
) | ||
] | ||
} | ||
// returns: | ||
// [ | ||
// { | ||
// path: [ 'foo', '0', 'bar', '0', 'baz', 'qux', 'quz', '0' ], | ||
// value: 'quux' }, | ||
// { path: [ 'foo', '0', 'bar', '0', 'baz', 'qux', 'quz', '1' ], | ||
// value: 'quuz' }, | ||
// { path: [ 'foo', '0', 'bar', '1', 'lorem', 'ipsum' ], | ||
// value: 'dolor' | ||
// } | ||
// ] | ||
props(data) | ||
``` | ||
**Verbose Options** | ||
```js | ||
const data = { foo: { bar: 'baz' } } | ||
Object.freeze(data.foo) | ||
// returns: | ||
// [ | ||
// { | ||
// path: ['foo'], | ||
// value: { bar: 'baz' }, | ||
// writable: true, | ||
// enumerable: true, | ||
// configurable: true, | ||
// parentIsFrozen: false, | ||
// parentIsSealed: false, | ||
// parentIsExtensible: true | ||
// }, | ||
// { | ||
// path: [ 'foo', 'bar' ], | ||
// value: 'baz', | ||
// writable: false, | ||
// enumerable: true, | ||
// configurable: false, | ||
// parentIsFrozen: true, | ||
// parentIsSealed: true, | ||
// parentIsExtensible: false | ||
// } | ||
// ] | ||
props(data, { stepwise: true, descriptors: true, permissions: true }) | ||
``` | ||
## Documentation | ||
### deepProps ⇒ [<code>Array.<PropAt></code>](#PropAt) \| [<code>Search</code>](https://github.com/jpcx/deep-props/blob/master/docs/global.md#Search) | ||
##### Module README files: | ||
+ [extract](/libs/extract/README.md) | ||
**Returns**: [<code>Array.<PropAt></code>](#PropAt) \| [<code>Search</code>](https://github.com/jpcx/deep-props/blob/master/docs/global.md#Search) - Array of paths and values or references. Returns Search generator if opt.gen is true. | ||
##### API Usage Documentation files: | ||
+ [extract](/libs/extract/docs/API.md) | ||
| Param | Type | Default | Description | | ||
| --- | --- | --- | --- | | ||
| host | [<code>Host</code>](https://github.com/jpcx/deep-props/blob/master/docs/global.md#Host) | | Object to unpack. | | ||
| [opt] | [<code>Options</code>](#Options) | <code>{}</code> | Execution settings. | | ||
##### Module-Specific Type Definitions and Functions: | ||
+ [extract](/libs/extract/docs/global.md) | ||
<a name="Options"></a> | ||
##### Global Namespace Type Definitions: | ||
+ [deep-props](/docs/global.md) | ||
### Options : <code>Object</code> | ||
*See: [<code>Options</code>](https://github.com/jpcx/deep-props/blob/master/docs/global.md#Options)* | ||
## Versioning | ||
Execution-wide settings supplied to the module. | ||
Modifies types of data attached to results. | ||
Modifies types of children to extract. | ||
Versioned using [SemVer](http://semver.org/). For available versions, see the [tags on this repository](https://github.com/jpcx/deep-props/tags). | ||
**Properties** | ||
## Contribution | ||
| Name | Type | Default | Description | | ||
| --- | --- | --- | --- | | ||
| [inherited] | <code>boolean</code> | | Whether or not to search for inherited properties. Attaches these keys behind a '\_\_proto__' key. | | ||
| [own] | <code>boolean</code> | <code>true</code> | Whether or not to search for own properties. Defaults to true. | | ||
| [nonEnumerable] | <code>boolean</code> | | Whether or not to search for and return non-enumerable properties. | | ||
| [permissions] | <code>boolean</code> | | Whether or not to attach Permissions to results. | | ||
| [descriptors] | <code>boolean</code> | | Whether or not to attach property descriptors other than 'value' to results. | | ||
| [stepwise] | <code>boolean</code> | | Whether or not to yield a PropAt object at every step down the chain. | | ||
| [includeRefValues] | <code>boolean</code> | | Whether or not to attach a value to Props with Refs attached. | | ||
| [gen] | <code>boolean</code> | | Whether or not to return a generator instead of executing the entire search. | | ||
| [full] | <code>boolean</code> | | If true, replaces undefined options with maximum search settings (all options except for propsCustomizer will be set to true). User supplied options supercede any changes here. | | ||
| [propsCustomizer] | [<code>PropsCustomizer</code>](https://github.com/jpcx/deep-props/blob/master/docs/global.md#PropsCustomizer) | | Function used for custom extraction of PropEntries from a Target. | | ||
Please raise an issue if you find any. Suggestions are welcome! | ||
<a name="PropAt"></a> | ||
### PropAt : <code>Object</code> | ||
*See: [<code>PropAt</code>](https://github.com/jpcx/deep-props/blob/master/docs/global.md#PropAt)* | ||
Description of a given level of the chain. Transformed Prop Object with location attched. | ||
**Properties** | ||
| Name | Type | Description | | ||
| --- | --- | --- | | ||
| [host] | [<code>Host</code>](https://github.com/jpcx/deep-props/blob/master/docs/global.md#Host) | When a non-primitive key has been encountered, a separate chain will be created with that key. Items on that chain will be labeled with a 'host' property to specify which Host the path applies to. PropAt Objects lacking a 'host' property imply that the path applies to the initially supplied Host. | | ||
| path | [<code>Path</code>](https://github.com/jpcx/deep-props/blob/master/docs/global.md#Path) | Describes the steps taken from the Host in order to reach the Prop's value. | | ||
| [value] | <code>\*</code> | Value described at the Prop's location (if any). In cases of a previously discovered reference (circular or otherwise), value will be replaced with a ref property (unless opt.showRefValues is true). | | ||
| [writable] | <code>boolean</code> | 'Writable' property descriptor of the value. | | ||
| [enumerable] | <code>boolean</code> | 'Enumerable' property descriptor of the value. | | ||
| [configurable] | <code>boolean</code> | 'Configurable' property descriptor of the value. | | ||
| [parentIsFrozen] | <code>boolean</code> | Frozen status of the parent object. | | ||
| [parentIsSealed] | <code>boolean</code> | Sealed status of the parent object. | | ||
| [parentIsExtensible] | <code>boolean</code> | Extensible status of the parent object. | | ||
| [ref] | [<code>Ref</code>](https://github.com/jpcx/deep-props/blob/master/docs/global.md#Ref) | If the value strictly equals a previously discovered Target, the Host and Path of that Target will be provided. | | ||
### See | ||
* [API Docs](https://github.com/jpcx/deep-props/blob/master/docs/API.md) | ||
* [Global Docs](https://github.com/jpcx/deep-props/blob/master/docs/global.md) | ||
## Versioning | ||
Versioned using [SemVer](http://semver.org/). For available versions, see the [tags on this repository](https://github.com/jpcx/deep-props/tags). | ||
## Author | ||
* **Justin Collier** - [jpcx](https://github.com/jpcx) | ||
+ **Justin Collier** - [jpcx](https://github.com/jpcx) | ||
## License | ||
This project is licensed under the MIT License - see the [LICENSE](https://github.com/jpcx/deep-props/blob/master/LICENSE) file for details | ||
This project is licensed under the MIT License - see the [LICENSE](/LICENSE) file for details |
1430
test.js
/** | ||
* Test script for deep-props module. | ||
* | ||
* @author Justin Collier <jpcxme@gmail.com> | ||
* @license MIT | ||
* @see {@link http://github.com/jpcx/deep-props|GitHub} | ||
* @license MIT | ||
*/ | ||
@@ -22,1396 +20,32 @@ | ||
const props = require('./') | ||
const assert = require('assert') | ||
const ANSI_RED = '\x1b[31m' | ||
const ANSI_GREEN = '\x1b[32m' | ||
const ANSI_CYAN = '\x1b[36m' | ||
const ANSI_RESET = '\x1b[0m' | ||
const ANSI_BOLD = '\x1b[1m' | ||
const tests = [] | ||
const master = require('./') | ||
// --- Test 1: --- // | ||
const extractTests = require('./libs/extract/test.js') | ||
tests.push(() => { | ||
const description = `Testing pure nested objects...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = { | ||
foo: { | ||
bar: { | ||
baz: { | ||
beh: 'qux' | ||
} | ||
} | ||
} | ||
}` | ||
const operations = [] | ||
const data = { | ||
foo: { | ||
bar: { | ||
baz: { | ||
beh: 'qux' | ||
} | ||
} | ||
} | ||
} | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', 'bar', 'baz', 'beh' ], | ||
value: data.foo.bar.baz.beh | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
const extractResults = extractTests.run(master.extract) | ||
// --- Test 2: --- // | ||
let totalTestsAttempt = 0 | ||
let numFailed = 0 | ||
tests.push(() => { | ||
const description = `Testing pure nested arrays...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = [ | ||
[ | ||
[ | ||
[ | ||
'foo' | ||
] | ||
] | ||
] | ||
]` | ||
const operations = [] | ||
const data = [ | ||
[ | ||
[ | ||
[ | ||
'foo' | ||
] | ||
] | ||
] | ||
] | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ '0', '0', '0', '0' ], | ||
value: data[0][0][0][0] | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
totalTestsAttempt += extractResults.totalTestsAttempt | ||
// --- Test 3: --- // | ||
if (extractResults.numFailed > 0) { | ||
numFailed += extractResults.numFailed | ||
tests.push(() => { | ||
const description = `Testing pure nested Maps...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = new Map() | ||
.set( | ||
'foo', new Map() | ||
.set( | ||
'bar', new Map() | ||
.set( | ||
'baz', new Map() | ||
.set( | ||
'beh', 'qux' | ||
) | ||
) | ||
) | ||
)` | ||
const operations = [] | ||
const data = new Map() | ||
.set( | ||
'foo', new Map() | ||
.set( | ||
'bar', new Map() | ||
.set( | ||
'baz', new Map() | ||
.set( | ||
'beh', 'qux' | ||
) | ||
) | ||
) | ||
) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', 'bar', 'baz', 'beh' ], | ||
value: data.get('foo').get('bar').get('baz').get('beh') | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
console.log( | ||
`\n========================================${ | ||
'========================================' | ||
}\n` | ||
) | ||
// --- Test 4: --- // | ||
tests.push(() => { | ||
const description = `Testing pure nested Sets...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = new Set([ | ||
new Set([ | ||
new Set([ | ||
new Set([ | ||
'foo' | ||
]) | ||
]) | ||
]) | ||
])` | ||
const operations = [] | ||
const data = new Set([ | ||
new Set([ | ||
new Set([ | ||
new Set([ | ||
'foo' | ||
]) | ||
]) | ||
]) | ||
]) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ '0', '0', '0', '0' ], | ||
value: [...[...[...[...data][0]][0]][0]][0] | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 5: --- // | ||
tests.push(() => { | ||
const description = `Testing multi-typed nests...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = { | ||
foo: [ | ||
new Map().set( | ||
'bar', new Set(['baz']) | ||
) | ||
] | ||
}` | ||
const operations = [] | ||
const data = { | ||
foo: [ | ||
new Map().set( | ||
'bar', new Set(['baz']) | ||
) | ||
] | ||
} | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', '0', 'bar', '0' ], | ||
value: [...data.foo[0].get('bar')][0] | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 6: --- // | ||
tests.push(() => { | ||
const description = `Testing unsupported objects...${ | ||
'\n\nData Preparation:' | ||
} | ||
const wmKey = { foo: 'bar' } | ||
const wmVal = 'baz' | ||
const data = new WeakMap().set(wmKey, wmVal)` | ||
const operations = [] | ||
const wmKey = { foo: 'bar' } | ||
const wmVal = 'baz' | ||
const data = new WeakMap().set(wmKey, wmVal) | ||
operations.push({ | ||
expect: [], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 7: --- // | ||
tests.push(() => { | ||
const description = `Testing pure multi-nested Objects...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = { | ||
foo: { | ||
beh: { | ||
lorem: 'ex' | ||
} | ||
}, | ||
bar: { | ||
qux: { | ||
ipsum: 'igne' | ||
} | ||
}, | ||
baz: { | ||
quz: { | ||
dolor: 'vita' | ||
} | ||
} | ||
}` | ||
const operations = [] | ||
const data = { | ||
foo: { | ||
beh: { | ||
lorem: 'ex' | ||
} | ||
}, | ||
bar: { | ||
qux: { | ||
ipsum: 'igne' | ||
} | ||
}, | ||
baz: { | ||
quz: { | ||
dolor: 'vita' | ||
} | ||
} | ||
} | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', 'beh', 'lorem' ], | ||
value: data.foo.beh.lorem | ||
}, | ||
{ | ||
path: [ 'bar', 'qux', 'ipsum' ], | ||
value: data.bar.qux.ipsum | ||
}, | ||
{ | ||
path: [ 'baz', 'quz', 'dolor' ], | ||
value: data.baz.quz.dolor | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 8: --- // | ||
tests.push(() => { | ||
const description = `Testing pure multi-nested Arrays...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = [ | ||
[ | ||
[ | ||
'foo' | ||
] | ||
], | ||
[ | ||
[ | ||
'bar' | ||
] | ||
], | ||
[ | ||
[ | ||
'baz' | ||
] | ||
] | ||
]` | ||
const operations = [] | ||
const data = [ | ||
[ | ||
[ | ||
'foo' | ||
] | ||
], | ||
[ | ||
[ | ||
'bar' | ||
] | ||
], | ||
[ | ||
[ | ||
'baz' | ||
] | ||
] | ||
] | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ '0', '0', '0' ], | ||
value: data[0][0][0] | ||
}, | ||
{ | ||
path: [ '1', '0', '0' ], | ||
value: data[1][0][0] | ||
}, | ||
{ | ||
path: [ '2', '0', '0' ], | ||
value: data[2][0][0] | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 9: --- // | ||
tests.push(() => { | ||
const description = `Testing pure multi-nested Maps...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = new Map() | ||
.set( | ||
'foo', new Map() | ||
.set( | ||
'beh', new Map() | ||
.set( | ||
'lorem', 'ex' | ||
) | ||
) | ||
) | ||
.set( | ||
'bar', new Map() | ||
.set( | ||
'qux', new Map() | ||
.set( | ||
'ipsum', 'igne' | ||
) | ||
) | ||
) | ||
.set( | ||
'baz', new Map() | ||
.set( | ||
'quz', new Map() | ||
.set( | ||
'dolor', 'vita' | ||
) | ||
) | ||
)` | ||
const operations = [] | ||
const data = new Map() | ||
.set( | ||
'foo', new Map() | ||
.set( | ||
'beh', new Map() | ||
.set( | ||
'lorem', 'ex' | ||
) | ||
) | ||
) | ||
.set( | ||
'bar', new Map() | ||
.set( | ||
'qux', new Map() | ||
.set( | ||
'ipsum', 'igne' | ||
) | ||
) | ||
) | ||
.set( | ||
'baz', new Map() | ||
.set( | ||
'quz', new Map() | ||
.set( | ||
'dolor', 'vita' | ||
) | ||
) | ||
) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', 'beh', 'lorem' ], | ||
value: data.get('foo').get('beh').get('lorem') | ||
}, | ||
{ | ||
path: [ 'bar', 'qux', 'ipsum' ], | ||
value: data.get('bar').get('qux').get('ipsum') | ||
}, | ||
{ | ||
path: [ 'baz', 'quz', 'dolor' ], | ||
value: data.get('baz').get('quz').get('dolor') | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 10: --- // | ||
tests.push(() => { | ||
const description = `Testing circular references...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = { | ||
foo: { | ||
bar: { | ||
baz: {} | ||
} | ||
} | ||
} | ||
data.foo.bar.baz.beh = data` | ||
const operations = [] | ||
const data = { | ||
foo: { | ||
bar: { | ||
baz: {} | ||
} | ||
} | ||
} | ||
data.foo.bar.baz.beh = data | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', 'bar', 'baz', 'beh' ], | ||
ref: { path: [] } | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 11: --- // | ||
tests.push(() => { | ||
const description = `Testing generator...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = { | ||
foo: { | ||
beh: { | ||
lorem: 'ex' | ||
} | ||
}, | ||
bar: { | ||
qux: { | ||
ipsum: 'igne' | ||
} | ||
}, | ||
baz: { | ||
quz: { | ||
dolor: 'vita' | ||
} | ||
} | ||
}` | ||
const operations = [] | ||
const data = { | ||
foo: { | ||
beh: { | ||
lorem: 'ex' | ||
} | ||
}, | ||
bar: { | ||
qux: { | ||
ipsum: 'igne' | ||
} | ||
}, | ||
baz: { | ||
quz: { | ||
dolor: 'vita' | ||
} | ||
} | ||
} | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', 'beh', 'lorem' ], | ||
value: data.foo.beh.lorem | ||
}, | ||
{ | ||
path: [ 'bar', 'qux', 'ipsum' ], | ||
value: data.bar.qux.ipsum | ||
}, | ||
{ | ||
path: [ 'baz', 'quz', 'dolor' ], | ||
value: data.baz.quz.dolor | ||
} | ||
], | ||
result: () => { | ||
const compilation = [] | ||
for (let result of props(data, { gen: true })) { | ||
compilation.push(result) | ||
} | ||
return compilation | ||
} | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 12: --- // | ||
tests.push(() => { | ||
const description = `Testing full...${ | ||
'\n\n Tests each result for:' + | ||
'\n - At least one of the descriptor properties' + | ||
'\n - All of the permissions properties' + | ||
'\n Additionally, tests for a few chosen deeply nested properties' | ||
}${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = {}` | ||
const operations = [] | ||
const data = {} | ||
operations.push({ | ||
expect: true, | ||
result: () => { | ||
const t = props(data, { full: true }) | ||
const anyMissingDescriptors = t.some( | ||
prop => { | ||
if (prop.path[prop.path.length - 1] === '__proto__') { | ||
// ignore generated proto property; it lacks descriptors | ||
return false | ||
} | ||
return !( | ||
[ | ||
'writable', | ||
'enumerable', | ||
'configurable' | ||
].some(p => prop.hasOwnProperty(p)) | ||
) | ||
} | ||
) | ||
const anyMissingAnyPermissions = t.some( | ||
prop => ( | ||
[ | ||
'parentIsFrozen', | ||
'parentIsSealed', | ||
'parentIsExtensible' | ||
].some(p => !prop.hasOwnProperty(p)) | ||
) | ||
) | ||
const hasDeepProp1 = t.some( | ||
prop => { | ||
try { | ||
assert.deepStrictEqual(prop.path, [ '__proto__', '__proto__' ]) | ||
return prop.value === null | ||
} catch (err) { | ||
return false | ||
} | ||
} | ||
) | ||
const hasDeepProp2 = t.some( | ||
prop => { | ||
try { | ||
assert.deepStrictEqual( | ||
prop.path, | ||
[ | ||
'__proto__', | ||
'constructor', | ||
'getOwnPropertyDescriptor', | ||
'length' | ||
] | ||
) | ||
return prop.value === 2 | ||
} catch (err) { | ||
return false | ||
} | ||
} | ||
) | ||
const hasDeepProp3 = t.some( | ||
prop => { | ||
try { | ||
assert.deepStrictEqual( | ||
prop.path, | ||
[ | ||
'__proto__', | ||
'constructor', | ||
'isExtensible', | ||
'__proto__' | ||
] | ||
) | ||
assert.deepStrictEqual( | ||
prop.ref.path, | ||
[ | ||
'__proto__', | ||
'constructor', | ||
'__proto__' | ||
] | ||
) | ||
return true | ||
} catch (err) { | ||
return false | ||
} | ||
} | ||
) | ||
return ( | ||
!( | ||
anyMissingDescriptors || | ||
anyMissingAnyPermissions | ||
) && | ||
( | ||
hasDeepProp1 && | ||
hasDeepProp2 && | ||
hasDeepProp3 | ||
) | ||
) | ||
} | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 13: --- // | ||
tests.push(() => { | ||
const description = `Testing full with generator...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = {}` | ||
const operations = [] | ||
const data = {} | ||
operations.push({ | ||
expect: true, | ||
result: () => { | ||
const t = [] | ||
for (let result of props(data, { full: true, gen: true })) { | ||
t.push(result) | ||
} | ||
const anyMissingDescriptors = t.some( | ||
prop => { | ||
if (prop.path[prop.path.length - 1] === '__proto__') { | ||
// ignore generated proto property; it lacks descriptors | ||
return false | ||
} | ||
return !( | ||
[ | ||
'writable', | ||
'enumerable', | ||
'configurable' | ||
].some(p => prop.hasOwnProperty(p)) | ||
) | ||
} | ||
) | ||
const anyMissingAnyPermissions = t.some( | ||
prop => ( | ||
[ | ||
'parentIsFrozen', | ||
'parentIsSealed', | ||
'parentIsExtensible' | ||
].some(p => !prop.hasOwnProperty(p)) | ||
) | ||
) | ||
const hasDeepProp1 = t.some( | ||
prop => { | ||
try { | ||
assert.deepStrictEqual(prop.path, [ '__proto__', '__proto__' ]) | ||
return prop.value === null | ||
} catch (err) { | ||
return false | ||
} | ||
} | ||
) | ||
const hasDeepProp2 = t.some( | ||
prop => { | ||
try { | ||
assert.deepStrictEqual( | ||
prop.path, | ||
[ | ||
'__proto__', | ||
'constructor', | ||
'getOwnPropertyDescriptor', | ||
'length' | ||
] | ||
) | ||
return prop.value === 2 | ||
} catch (err) { | ||
return false | ||
} | ||
} | ||
) | ||
const hasDeepProp3 = t.some( | ||
prop => { | ||
try { | ||
assert.deepStrictEqual( | ||
prop.path, | ||
[ | ||
'__proto__', | ||
'constructor', | ||
'isExtensible', | ||
'__proto__' | ||
] | ||
) | ||
assert.deepStrictEqual( | ||
prop.ref.path, | ||
[ | ||
'__proto__', | ||
'constructor', | ||
'__proto__' | ||
] | ||
) | ||
return true | ||
} catch (err) { | ||
return false | ||
} | ||
} | ||
) | ||
return ( | ||
!( | ||
anyMissingDescriptors || | ||
anyMissingAnyPermissions | ||
) && | ||
( | ||
hasDeepProp1 && | ||
hasDeepProp2 && | ||
hasDeepProp3 | ||
) | ||
) | ||
} | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 14: --- // | ||
tests.push(() => { | ||
const description = `Testing proto...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = {} | ||
const proto_1 = {} | ||
const proto_2 = {} | ||
const proto_3 = {} | ||
Object.setPrototypeOf(data, proto1) | ||
Object.setPrototypeOf(proto1, proto2) | ||
Object.setPrototypeOf(proto2, proto3) | ||
Object.setPrototypeOf(proto3, null)` | ||
const operations = [] | ||
const data = {} | ||
const proto1 = {} | ||
const proto2 = {} | ||
const proto3 = {} | ||
Object.setPrototypeOf(data, proto1) | ||
Object.setPrototypeOf(proto1, proto2) | ||
Object.setPrototypeOf(proto2, proto3) | ||
Object.setPrototypeOf(proto3, null) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ '__proto__', '__proto__', '__proto__', '__proto__' ], | ||
value: null | ||
} | ||
], | ||
result: () => props(data, { inherited: true }) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 15: --- // | ||
tests.push(() => { | ||
const description = `Testing full with proto...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = {} | ||
const proto1 = {} | ||
const proto2 = {} | ||
const proto3 = {} | ||
Object.setPrototypeOf(data, proto1) | ||
Object.setPrototypeOf(proto1, proto2) | ||
Object.setPrototypeOf(proto2, proto3) | ||
Object.setPrototypeOf(proto3, null)` | ||
const operations = [] | ||
const data = {} | ||
const proto1 = {} | ||
const proto2 = {} | ||
const proto3 = {} | ||
Object.setPrototypeOf(data, proto1) | ||
Object.setPrototypeOf(proto1, proto2) | ||
Object.setPrototypeOf(proto2, proto3) | ||
Object.setPrototypeOf(proto3, null) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: ['__proto__'], | ||
value: proto1, | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
}, | ||
{ | ||
path: [ '__proto__', '__proto__' ], | ||
value: proto2, | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
}, | ||
{ | ||
path: [ '__proto__', '__proto__', '__proto__' ], | ||
value: proto3, | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
}, | ||
{ | ||
path: [ '__proto__', '__proto__', '__proto__', '__proto__' ], | ||
value: null, | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
} | ||
], | ||
result: () => props(data, { full: true, proto: true }) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 16: --- // | ||
tests.push(() => { | ||
const description = `Testing non-enumerable object properties...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = {} | ||
Object.defineProperty(data, 'foo', { value: {}, enumerable: false }) | ||
Object.defineProperty(data.foo, 'bar', { value: {}, enumerable: false }) | ||
Object.defineProperty(data.foo.bar, 'baz', { | ||
value: 'beh', enumerable: false | ||
})` | ||
const operations = [] | ||
const data = {} | ||
Object.defineProperty(data, 'foo', { value: {}, enumerable: false }) | ||
Object.defineProperty(data.foo, 'bar', { value: {}, enumerable: false }) | ||
Object.defineProperty(data.foo.bar, 'baz', { | ||
value: 'beh', enumerable: false | ||
}) | ||
operations.push({ | ||
expect: [ | ||
{ path: [ 'foo', 'bar', 'baz' ], value: data.foo.bar.baz } | ||
], | ||
result: () => props(data, { nonEnumerable: true }) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 17: --- // | ||
tests.push(() => { | ||
const description = `Testing Object key exploration...${ | ||
'\n\nData Preparation:' | ||
} | ||
const mKey1 = { foo: 'bar' } | ||
const mKey2 = { baz: 'beh' } | ||
const mKey3 = { qux: 'quz' } | ||
const data = new Map([ | ||
[ | ||
mKey1, | ||
new Map([ | ||
[ | ||
mKey2, | ||
new Map([ | ||
[ | ||
mKey3, | ||
{ quux: 'quuz' } | ||
] | ||
]) | ||
] | ||
]) | ||
] | ||
])` | ||
const operations = [] | ||
const mKey1 = { foo: 'bar' } | ||
const mKey2 = { baz: 'beh' } | ||
const mKey3 = { qux: 'quz' } | ||
const data = new Map([ | ||
[ | ||
mKey1, | ||
new Map([ | ||
[ | ||
mKey2, | ||
new Map([ | ||
[ | ||
mKey3, | ||
{ quux: 'quuz' } | ||
] | ||
]) | ||
] | ||
]) | ||
] | ||
]) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ mKey1, mKey2, mKey3, 'quux' ], | ||
value: data.get(mKey1).get(mKey2).get(mKey3).quux | ||
}, | ||
{ | ||
host: mKey3, | ||
path: ['qux'], | ||
value: 'quz' | ||
}, | ||
{ | ||
host: mKey2, | ||
path: ['baz'], | ||
value: 'beh' | ||
}, | ||
{ | ||
host: mKey1, | ||
path: ['foo'], | ||
value: 'bar' | ||
} | ||
], | ||
result: () => props(data) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 18: --- // | ||
tests.push(() => { | ||
const description = `Testing entries customizer...${ | ||
'\n\nData Preparation:' | ||
} | ||
/** | ||
* Sample data structure. | ||
* Supplies 'get', 'getValues', and 'push' functions. | ||
*/ | ||
class NonNativeDataStructure { | ||
constructor(arr) { | ||
const values = arr | ||
this.get = i => values[i] | ||
this.getValues = () => values | ||
this.push = x => values.push(x) | ||
} | ||
} | ||
const data = new NonNativeDataStructure([ | ||
new NonNativeDataStructure(['foo']) | ||
])` | ||
/** | ||
* Sample data structure. | ||
* Supplies 'get', 'getValues', and 'push' functions. | ||
* | ||
* @private | ||
* @class | ||
*/ | ||
class NonNativeDataStructure { | ||
/** | ||
* Stores array and initializes get method. | ||
* | ||
* @private | ||
* @param {Array} arr - Array to store. | ||
* @example | ||
* // returns 3 | ||
* const hiddenData = new NonNativeDataStructure([1,2,3]) | ||
* hiddenData.get(2) | ||
* @example | ||
* //returns [1, 2, 3] | ||
* hiddenData.getValues() | ||
* @example | ||
* //returns 1 | ||
* hiddenData.push('foo') | ||
*/ | ||
constructor (arr) { | ||
const values = arr | ||
this.get = i => values[i] | ||
this.getValues = () => values | ||
this.push = x => values.push(x) | ||
} | ||
} | ||
const operations = [] | ||
const data = new NonNativeDataStructure([ | ||
new NonNativeDataStructure(['foo']) | ||
]) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: ['get'], | ||
value: data.get | ||
}, | ||
{ | ||
path: ['getValues'], | ||
value: data.getValues | ||
}, | ||
{ | ||
path: ['push'], | ||
value: data.push | ||
}, | ||
{ | ||
path: [ '0', 'get' ], | ||
value: data.get(0).get | ||
}, | ||
{ | ||
path: [ '0', 'getValues' ], | ||
value: data.get(0).getValues | ||
}, | ||
{ | ||
path: [ '0', 'push' ], | ||
value: data.get(0).push | ||
}, | ||
{ | ||
path: [ '0', '0' ], | ||
value: data.get(0).get(0) | ||
}, | ||
{ | ||
path: [ '0', 'length' ], | ||
value: data.get(0).get('length') | ||
}, | ||
{ | ||
path: ['length'], | ||
value: data.get('length') | ||
} | ||
], | ||
result: () => { | ||
return props(data, { | ||
propsCustomizer: target => { | ||
if (target instanceof NonNativeDataStructure) { | ||
return ( | ||
Object.entries( | ||
Object.getOwnPropertyDescriptors( | ||
target.getValues() | ||
) | ||
) | ||
) | ||
} | ||
} | ||
}) | ||
} | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 19: --- // | ||
tests.push(() => { | ||
const description = `Testing entries customizer with circular references...${ | ||
'\n\nData Preparation:' | ||
} | ||
/** | ||
* Sample data structure. | ||
* Supplies 'get', 'getValues', and 'push' functions. | ||
*/ | ||
class NonNativeDataStructure { | ||
constructor(arr) { | ||
const values = arr | ||
this.get = i => values[i] | ||
this.getValues = () => values | ||
this.push = x => values.push(x) | ||
} | ||
} | ||
const data = new NonNativeDataStructure([ | ||
new NonNativeDataStructure(['foo']) | ||
]) | ||
data.get(0).push(data.get(0))` | ||
/** | ||
* Sample data structure. | ||
* Supplies 'get', 'getValues', and 'push' functions. | ||
* | ||
* @private | ||
* @class | ||
*/ | ||
class NonNativeDataStructure { | ||
/** | ||
* Stores array and initializes get method. | ||
* | ||
* @private | ||
* @param {Array} arr - Array to store. | ||
* @example | ||
* // returns 3 | ||
* const hiddenData = new NonNativeDataStructure([1,2,3]) | ||
* hiddenData.get(2) | ||
* @example | ||
* //returns [1, 2, 3] | ||
* hiddenData.getValues() | ||
* @example | ||
* //returns 1 | ||
* hiddenData.push('foo') | ||
*/ | ||
constructor (arr) { | ||
const values = arr | ||
this.get = i => values[i] | ||
this.getValues = () => values | ||
this.push = x => values.push(x) | ||
} | ||
} | ||
const operations = [] | ||
const data = new NonNativeDataStructure([ | ||
new NonNativeDataStructure(['foo']) | ||
]) | ||
data.get(0).push(data.get(0)) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: ['get'], | ||
value: data.get | ||
}, | ||
{ | ||
path: ['getValues'], | ||
value: data.getValues | ||
}, | ||
{ | ||
path: ['push'], | ||
value: data.push | ||
}, | ||
{ | ||
path: [ '0', 'get' ], | ||
value: data.get(0).get | ||
}, | ||
{ | ||
path: [ '0', 'getValues' ], | ||
value: data.get(0).getValues | ||
}, | ||
{ | ||
path: [ '0', 'push' ], | ||
value: data.get(0).push | ||
}, | ||
{ | ||
path: [ '0', '0' ], | ||
value: data.get(0).get(0) | ||
}, | ||
{ | ||
path: [ '0', '1' ], | ||
ref: { path: ['0'] } | ||
}, | ||
{ | ||
path: [ '0', 'length' ], | ||
value: data.get(0).get('length') | ||
}, | ||
{ | ||
path: ['length'], | ||
value: data.get('length') | ||
} | ||
], | ||
result: () => { | ||
return props(data, { | ||
propsCustomizer: target => { | ||
if (target instanceof NonNativeDataStructure) { | ||
return ( | ||
Object.entries( | ||
Object.getOwnPropertyDescriptors( | ||
target.getValues() | ||
) | ||
) | ||
) | ||
} | ||
} | ||
}) | ||
} | ||
}) | ||
return { data, description, operations } | ||
}) | ||
// --- Test 20: --- // | ||
tests.push(() => { | ||
const description = `Testing various individual options...${ | ||
'\n\nData Preparation:' | ||
} | ||
const data = { | ||
foo: { | ||
bar: 'baz' | ||
} | ||
} | ||
Object.defineProperty( | ||
data.foo, | ||
'hidden', | ||
{ value: 'foobar', enumerable: false } | ||
)` | ||
const operations = [] | ||
const data = { | ||
foo: { | ||
bar: 'baz' | ||
} | ||
} | ||
Object.defineProperty( | ||
data.foo, | ||
'hidden', | ||
{ value: 'foobar', enumerable: false } | ||
console.log( | ||
ANSI_BOLD + ANSI_RED + 'Extract module test failure!\n' + ANSI_RESET | ||
) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ '__proto__', '__proto__' ], | ||
value: Object.getPrototypeOf(Object.getPrototypeOf(data)) | ||
}, | ||
{ | ||
path: [ 'foo', '__proto__' ], | ||
ref: { path: ['__proto__'] } | ||
}, | ||
{ | ||
path: [ 'foo', 'bar' ], | ||
value: data.foo.bar | ||
} | ||
], | ||
result: () => props(data, { inherited: true }) | ||
}) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ '__proto__', '__proto__' ], | ||
value: Object.getPrototypeOf(Object.getPrototypeOf(data)) | ||
}, | ||
{ | ||
path: [ 'foo', '__proto__' ], | ||
value: Object.getPrototypeOf(data.foo), | ||
ref: { path: ['__proto__'] } | ||
}, | ||
{ | ||
path: [ 'foo', 'bar' ], | ||
value: data.foo.bar | ||
} | ||
], | ||
result: () => props(data, { inherited: true, includeRefValues: true }) | ||
}) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ '__proto__', '__proto__' ], | ||
value: Object.getPrototypeOf(Object.getPrototypeOf(data)) | ||
} | ||
], | ||
result: () => props(data, { inherited: true, own: false }) | ||
}) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', 'bar' ], | ||
value: data.foo.bar | ||
}, | ||
{ | ||
path: [ 'foo', 'hidden' ], | ||
value: data.foo.hidden | ||
} | ||
], | ||
result: () => props(data, { nonEnumerable: true }) | ||
}) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', 'bar' ], | ||
value: data.foo.bar, | ||
parentIsFrozen: false, | ||
parentIsSealed: false, | ||
parentIsExtensible: true | ||
} | ||
], | ||
result: () => props(data, { permissions: true }) | ||
}) | ||
operations.push({ | ||
expect: [ | ||
{ | ||
path: [ 'foo', 'bar' ], | ||
value: data.foo.bar, | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
} | ||
], | ||
result: () => props(data, { descriptors: true }) | ||
}) | ||
operations.push({ | ||
expect: [ | ||
{ path: ['foo'], value: data.foo }, | ||
{ path: [ 'foo', 'bar' ], value: data.foo.bar } | ||
], | ||
result: () => props(data, { stepwise: true }) | ||
}) | ||
return { data, description, operations } | ||
}) | ||
console.log('Performing tests...') | ||
const errors = [] | ||
let numFailed = 0 | ||
let totalTestsAttempt = 0 | ||
for (let i = 0; i < tests.length; i++) { | ||
totalTestsAttempt++ | ||
console.log(`\n---\n\nTest ${i + 1}`) | ||
let reported = false | ||
try { | ||
const test = tests[i]() | ||
console.log(test.description) | ||
console.log('\nData:') | ||
dirDeep(test.data) | ||
for (let op of test.operations) { | ||
const result = op.result() | ||
let assertion | ||
try { | ||
assert.deepStrictEqual(op.expect, result) | ||
assertion = true | ||
} catch (err) { | ||
assertion = err | ||
} | ||
const resultString = op.result.toString() | ||
let formattedResult = ANSI_CYAN | ||
if ( | ||
resultString.match(/\(\) => [^{(]/) !== null && | ||
resultString.match(/\(\) => [^{(]/).index === 0 | ||
) { | ||
formattedResult += resultString.replace(/\(\) => /, '') | ||
} else { | ||
formattedResult += resultString.replace(/\(\) => [{(\s]*/, '') | ||
.slice(0, -1) | ||
.trim() | ||
} | ||
formattedResult += ANSI_RESET | ||
const operation = '\nOperation:\n ' + formattedResult | ||
console.log(operation) | ||
process.stdout.write('\nExpected:\n') | ||
dirDeep(op.expect) | ||
process.stdout.write('\nResult:\n') | ||
dirDeep(op.result()) | ||
if (assertion === true) { | ||
console.log(ANSI_GREEN + '[OK]' + ANSI_RESET) | ||
} else { | ||
console.log(ANSI_RED + '[FAIL]' + ANSI_RESET) | ||
errors.push({ | ||
test_num: i + 1, | ||
descr: test.description, | ||
assertion, | ||
operation | ||
}) | ||
numFailed++ | ||
reported = true | ||
} | ||
} | ||
} catch (err) { | ||
console.log(ANSI_RED + '[FAIL]' + ANSI_RESET) | ||
errors.push({ test_num: i + 1, err }) | ||
if (reported === false) numFailed++ | ||
for (let e of extractResults.errors) { | ||
dirDeep(e) | ||
} | ||
@@ -1426,24 +60,8 @@ } | ||
if (errors.length > 0) { | ||
console.log(ANSI_BOLD + 'Submodules\' Test Results:\n' + ANSI_RESET) | ||
if (numFailed === 0) { | ||
console.log(ANSI_GREEN + '[PASS]' + ANSI_RESET) | ||
} else { | ||
console.log(ANSI_RED + '[FAIL]' + ANSI_RESET) | ||
for (let e of errors) { | ||
console.log(`\nFailed test ${e.test_num}! `) | ||
if (e.err) { | ||
console.log() | ||
dirDeep(e.err) | ||
} else { | ||
console.log(`Description: ${e.descr}`) | ||
if (e.operation) console.log(e.operation) | ||
if (e.assertion) { | ||
const err = Error() | ||
Object.keys(e.assertion).forEach(x => { | ||
err[x] = e.assertion[x] | ||
}) | ||
console.log('\nError:') | ||
dirDeep(err) | ||
} | ||
} | ||
} | ||
} else { | ||
console.log(ANSI_GREEN + '[PASS]' + ANSI_RESET) | ||
} | ||
@@ -1450,0 +68,0 @@ |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
120191
15
2905
77
2
1