@hyperfrontend/list-utils 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +29 -31
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.cjs.js +0 -2
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.esm.js +1 -2
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.cjs.js +0 -2
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js +1 -2
- package/bundle/index.iife.js +4 -20
- package/bundle/index.iife.min.js +1 -1
- package/bundle/index.umd.js +4 -20
- package/bundle/index.umd.min.js +1 -1
- package/index.cjs.js +26 -13
- package/index.esm.js +16 -3
- package/package.json +1 -1
- package/_dependencies/@hyperfrontend/data-utils/index.cjs.js +0 -214
- package/_dependencies/@hyperfrontend/data-utils/index.esm.js +0 -202
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/date/index.cjs.js +0 -8
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/date/index.esm.js +0 -7
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.cjs.js +0 -6
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.esm.js +0 -5
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js +0 -7
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.esm.js +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.1.1](https://github.com/AndrewRedican/hyperfrontend/compare/29dc54d58c2b3c8b8dd35f7c546ebc6cd9191f6e...adf0a4f77dece2be855e7ab88185a4fe84e7b16b) - 2026-09-14
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- accept arrays, functions and class instances in fifo and lifo lists
|
|
10
|
+
|
|
5
11
|
## [0.1.0](https://github.com/AndrewRedican/hyperfrontend/compare/5f116abb8ba6355dfb283fa03b7481e5eb029480...cc16961a470190848f71a7cce41f7377bc4511a4) - 2026-08-25
|
|
6
12
|
|
|
7
13
|
### Breaking Changes
|
package/README.md
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://www.hyperfrontend.dev/docs/libraries/utils/list/">
|
|
3
|
+
<img width="640" height="180" src="https://www.hyperfrontend.dev/media/banner-list-utils/banner.gif" alt="@hyperfrontend/list-utils">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
2
6
|
|
|
3
7
|
<p align="center">
|
|
4
8
|
<a href="https://github.com/AndrewRedican/hyperfrontend/actions/workflows/ci-lib-list-utils.yml">
|
|
@@ -33,6 +37,12 @@
|
|
|
33
37
|
<img src="https://img.shields.io/badge/tree%20shakeable-%E2%9C%93-success?style=flat-square" alt="Tree Shakeable">
|
|
34
38
|
</p>
|
|
35
39
|
|
|
40
|
+
<p align="center">
|
|
41
|
+
<a href="https://www.hyperfrontend.dev/docs/libraries/utils/list/">
|
|
42
|
+
<img width="640" height="360" src="https://www.hyperfrontend.dev/media/list-utils-order/hero.gif" alt="A tube open at both ends and a cup open only at the top, with the same three numbered discs dropped into each; three pulls take the discs out, the tube from its bottom and the cup from its top, and the exit rows read 1 2 3 under the tube and 3 2 1 beside the cup">
|
|
43
|
+
</a>
|
|
44
|
+
</p>
|
|
45
|
+
|
|
36
46
|
Purpose-built collection utilities for queue management, filtering, and iteration patterns.
|
|
37
47
|
|
|
38
48
|
• 👉 See [**documentation**](https://www.hyperfrontend.dev/docs/libraries/utils/list/)
|
|
@@ -46,16 +56,16 @@ The library enforces immutability through frozen interfaces while maintaining hi
|
|
|
46
56
|
|
|
47
57
|
### Key Features
|
|
48
58
|
|
|
49
|
-
- **FIFO and LIFO queues** with type-safe object tracking
|
|
50
|
-
- **Value picker** for cyclical iteration (ideal for round-robin patterns)
|
|
51
|
-
- **String sanitization** utilities (dedupe, trim, filter empty)
|
|
52
|
-
- **Range generation** for loop-free number sequences
|
|
53
|
-
- **Map utilities** for common Map operations
|
|
59
|
+
- **[FIFO and LIFO queues](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createFifoList)** with type-safe object tracking
|
|
60
|
+
- **[Value picker](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createValuePicker)** for cyclical iteration (ideal for round-robin patterns)
|
|
61
|
+
- **[String sanitization](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-uniqueStrings)** utilities (dedupe, trim, filter empty)
|
|
62
|
+
- **[Range generation](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createRange)** for loop-free number sequences
|
|
63
|
+
- **[Map utilities](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-getLastKeyInMap)** for common Map operations
|
|
54
64
|
- **Zero dependencies** - Self-contained implementation with no third-party runtime dependencies
|
|
55
65
|
|
|
56
66
|
### Architecture Highlights
|
|
57
67
|
|
|
58
|
-
|
|
68
|
+
Both queue kinds hold object references only: pushing a primitive throws at runtime, not just at compile time. Membership is reference identity, so two structurally identical objects are two separate entries, and `has()` and `remove()` need the same reference you pushed.
|
|
59
69
|
|
|
60
70
|
## Why Use @hyperfrontend/list-utils?
|
|
61
71
|
|
|
@@ -99,40 +109,28 @@ colorPicker.next() // 'blue'
|
|
|
99
109
|
colorPicker.next() // 'green'
|
|
100
110
|
colorPicker.next() // 'red' (cycles back)
|
|
101
111
|
|
|
102
|
-
// String sanitization
|
|
112
|
+
// String sanitization: blanks are dropped, survivors keep their original padding
|
|
103
113
|
const userInputs = [' hello ', '', 'world', null, 'hello', ' ', 'world']
|
|
104
|
-
const cleaned = uniqueStrings(nonEmptyStrings(userInputs)) // ['hello', 'world']
|
|
114
|
+
const cleaned = uniqueStrings(nonEmptyStrings(userInputs)) // [' hello ', 'world', 'hello']
|
|
105
115
|
```
|
|
106
116
|
|
|
107
117
|
## API Overview
|
|
108
118
|
|
|
109
|
-
|
|
119
|
+
Two ordered collections plus a few standalone helpers; the collections are the place to start. [`createFifoList`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createFifoList) and [`createLifoList`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createLifoList) each take no arguments, are generic over an object type, and return a frozen instance with the same fixed method surface: [`push`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`pull`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`map`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`forEach`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`remove`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`has`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`size`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList) and [`clear`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList). Choosing one factory over the other decides which end `pull()` reads from and nothing else about the surrounding code. They diverge on one point, deliberately: pushing an item a FIFO list already holds throws, surfacing a queue that would otherwise take the same job twice, whereas a LIFO list ignores the duplicate and leaves the item where it already was.
|
|
110
120
|
|
|
111
|
-
|
|
112
|
-
- **`createLifoList<T>()`** - Last-in-first-out stack with reference uniqueness
|
|
121
|
+
Both are backed by a `Set` of the references you pushed, which has two consequences to know before reaching for either. A primitive is rejected at runtime, not only by the type parameter. And membership is reference identity, so two structurally identical objects are two entries, and `has()` and `remove()` want back the exact reference you handed over.
|
|
113
122
|
|
|
114
|
-
|
|
123
|
+
The helpers are independent of the lists and of each other. [`createValuePicker`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createValuePicker) wraps a non-empty string array in a `current()` and `next()` pair that cycles forever, while [`createRange`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createRange) returns an inclusive run of numbers. For string arrays, [`nonEmptyStrings`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-nonEmptyStrings) drops null, undefined, empty and whitespace-only entries but returns the survivors exactly as they arrived: it tests `value.trim()`, it does not trim, so padded values come through padded and [`uniqueStrings`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-uniqueStrings) then dedupes on exact string equality, which leaves `' hello '` and `'hello'` as two distinct results. The last helper, [`getLastKeyInMap`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-getLastKeyInMap), reads the most recently inserted key of a `Map`.
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
- **`createRange(start, end)`** - Generate number arrays without loops
|
|
118
|
-
|
|
119
|
-
### String Utilities
|
|
120
|
-
|
|
121
|
-
- **`nonEmptyStrings(values)`** - Filter null/undefined/empty/whitespace strings
|
|
122
|
-
- **`uniqueStrings(values)`** - Remove duplicates while preserving order
|
|
123
|
-
|
|
124
|
-
### Map Helpers
|
|
125
|
-
|
|
126
|
-
- **`getLastKeyInMap(map)`** - Retrieve the last inserted key from a Map
|
|
125
|
+
Every signature, type parameter and thrown error is in the [API reference](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-reference).
|
|
127
126
|
|
|
128
127
|
## Compatibility
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
| Deno, Bun, Cloudflare Workers | ✅ |
|
|
129
|
+
<p align="center">
|
|
130
|
+
<a href="https://www.hyperfrontend.dev/docs/libraries/utils/list/#compatibility">
|
|
131
|
+
<img width="640" height="150" src="https://www.hyperfrontend.dev/media/runtimes-list-utils/runtimes.png" alt="Runs in Node.js 18 or later, evergreen browsers and web workers">
|
|
132
|
+
</a>
|
|
133
|
+
</p>
|
|
136
134
|
|
|
137
135
|
### Output Formats
|
|
138
136
|
|
|
@@ -157,7 +155,7 @@ const cleaned = uniqueStrings(nonEmptyStrings(userInputs)) // ['hello', 'world']
|
|
|
157
155
|
</script>
|
|
158
156
|
```
|
|
159
157
|
|
|
160
|
-
**Global variable:** `HyperfrontendListUtils`
|
|
158
|
+
**Global variable:** [`HyperfrontendListUtils`](https://www.hyperfrontend.dev/docs/libraries/utils/list/)
|
|
161
159
|
|
|
162
160
|
## Part of hyperfrontend
|
|
163
161
|
|
package/bundle/index.iife.js
CHANGED
|
@@ -2,7 +2,6 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const _Array = globalThis.Array;
|
|
5
|
-
const isArray = _Array.isArray;
|
|
6
5
|
const from = _Array.from;
|
|
7
6
|
|
|
8
7
|
const _Error = globalThis.Error;
|
|
@@ -12,31 +11,15 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
12
11
|
const _Object = globalThis.Object;
|
|
13
12
|
const freeze = _Object.freeze;
|
|
14
13
|
|
|
15
|
-
const registeredClasses = [];
|
|
16
|
-
|
|
17
|
-
const getType = (target) => {
|
|
18
|
-
if (target === null)
|
|
19
|
-
return 'null';
|
|
20
|
-
const nativeDataType = typeof target;
|
|
21
|
-
if (nativeDataType === 'object') {
|
|
22
|
-
if (isArray(target))
|
|
23
|
-
return 'array';
|
|
24
|
-
for (const registeredClass of registeredClasses) {
|
|
25
|
-
if (target instanceof registeredClass)
|
|
26
|
-
return registeredClass.name;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return nativeDataType;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
14
|
const _Set = globalThis.Set;
|
|
33
15
|
const _Reflect = globalThis.Reflect;
|
|
34
16
|
const createSet = (iterable) => _Reflect.construct(_Set, iterable ? [iterable] : []);
|
|
35
17
|
|
|
18
|
+
const isNonPrimitive$1 = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
36
19
|
function createFifoList() {
|
|
37
20
|
const list = createSet();
|
|
38
21
|
const push = (item) => {
|
|
39
|
-
if (
|
|
22
|
+
if (!isNonPrimitive$1(item)) {
|
|
40
23
|
throw createError('A fifo list only supports non-primitive values');
|
|
41
24
|
}
|
|
42
25
|
if (list.has(item)) {
|
|
@@ -72,10 +55,11 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
72
55
|
return freeze(result);
|
|
73
56
|
}
|
|
74
57
|
|
|
58
|
+
const isNonPrimitive = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
75
59
|
function createLifoList() {
|
|
76
60
|
const list = createSet();
|
|
77
61
|
const push = (item) => {
|
|
78
|
-
if (
|
|
62
|
+
if (!isNonPrimitive(item)) {
|
|
79
63
|
throw createError('A lifo list only supports non-primitive values');
|
|
80
64
|
}
|
|
81
65
|
list.add(item);
|
package/bundle/index.iife.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var HyperfrontendListUtils=function(
|
|
1
|
+
var HyperfrontendListUtils=function(t){"use strict";const e=globalThis.Array.from,n=globalThis.Error,r=globalThis.Reflect,o=(t,e)=>r.construct(n,[t,e]),i=globalThis.Object.freeze,l=globalThis.Set,s=globalThis.Reflect,a=t=>s.construct(l,t?[t]:[]);return t.createFifoList=function(){const t=a();return i({push:e=>{if(("object"!=typeof(n=e)||null===n)&&"function"!=typeof n)throw o("A fifo list only supports non-primitive values");var n;if(t.has(e))throw o("Cannot push an item that already exists in the list");t.add(e)},pull:()=>{const e=t.values().next().value;return t.delete(e),e},map:n=>e(t.values()).map(n),forEach:e=>{t.forEach(e)},remove:e=>t.delete(e),has:e=>t.has(e),size:()=>t.size,clear:()=>t.clear()})},t.createLifoList=function(){const t=a();return i({push:e=>{if(("object"!=typeof(n=e)||null===n)&&"function"!=typeof n)throw o("A lifo list only supports non-primitive values");var n;t.add(e)},pull:()=>{const n=e(t).pop();return n&&t.delete(n),n},map:n=>e(t).map(n),forEach:e=>{t.forEach(e)},remove:e=>t.delete(e),has:e=>t.has(e),size:()=>t.size,clear:()=>{t.clear()}})},t.createRange=function(t,e){const n=[];for(let r=t;r<=e;r+=1)n.push(r);return n},t.createValuePicker=function(t){if(!t||!t.length)throw o("Expected values not to be an empty list.");let e=-1;function n(){return e<0&&(e=0),t[e]}return{current:n,next:function(){return e<0?n():(e=++e<t.length?e:0,t[e])}}},t.getLastKeyInMap=t=>{const e=[...t.keys()];return e[e.length-1]},t.nonEmptyStrings=function(t){return t.filter(t=>![void 0,null,""].includes(t)&&""!==t.trim())},t.uniqueStrings=function(t){return e(a(t))},t}({});
|
package/bundle/index.umd.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
7
|
const _Array = globalThis.Array;
|
|
8
|
-
const isArray = _Array.isArray;
|
|
9
8
|
const from = _Array.from;
|
|
10
9
|
|
|
11
10
|
const _Error = globalThis.Error;
|
|
@@ -15,31 +14,15 @@
|
|
|
15
14
|
const _Object = globalThis.Object;
|
|
16
15
|
const freeze = _Object.freeze;
|
|
17
16
|
|
|
18
|
-
const registeredClasses = [];
|
|
19
|
-
|
|
20
|
-
const getType = (target) => {
|
|
21
|
-
if (target === null)
|
|
22
|
-
return 'null';
|
|
23
|
-
const nativeDataType = typeof target;
|
|
24
|
-
if (nativeDataType === 'object') {
|
|
25
|
-
if (isArray(target))
|
|
26
|
-
return 'array';
|
|
27
|
-
for (const registeredClass of registeredClasses) {
|
|
28
|
-
if (target instanceof registeredClass)
|
|
29
|
-
return registeredClass.name;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return nativeDataType;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
17
|
const _Set = globalThis.Set;
|
|
36
18
|
const _Reflect = globalThis.Reflect;
|
|
37
19
|
const createSet = (iterable) => _Reflect.construct(_Set, iterable ? [iterable] : []);
|
|
38
20
|
|
|
21
|
+
const isNonPrimitive$1 = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
39
22
|
function createFifoList() {
|
|
40
23
|
const list = createSet();
|
|
41
24
|
const push = (item) => {
|
|
42
|
-
if (
|
|
25
|
+
if (!isNonPrimitive$1(item)) {
|
|
43
26
|
throw createError('A fifo list only supports non-primitive values');
|
|
44
27
|
}
|
|
45
28
|
if (list.has(item)) {
|
|
@@ -75,10 +58,11 @@
|
|
|
75
58
|
return freeze(result);
|
|
76
59
|
}
|
|
77
60
|
|
|
61
|
+
const isNonPrimitive = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
78
62
|
function createLifoList() {
|
|
79
63
|
const list = createSet();
|
|
80
64
|
const push = (item) => {
|
|
81
|
-
if (
|
|
65
|
+
if (!isNonPrimitive(item)) {
|
|
82
66
|
throw createError('A lifo list only supports non-primitive values');
|
|
83
67
|
}
|
|
84
68
|
list.add(item);
|
package/bundle/index.umd.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).HyperfrontendListUtils={})}(this,function(e){"use strict";const t=globalThis.Array
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).HyperfrontendListUtils={})}(this,function(e){"use strict";const t=globalThis.Array.from,n=globalThis.Error,o=globalThis.Reflect,r=(e,t)=>o.construct(n,[e,t]),i=globalThis.Object.freeze,l=globalThis.Set,s=globalThis.Reflect,u=e=>s.construct(l,e?[e]:[]);e.createFifoList=function(){const e=u();return i({push:t=>{if(("object"!=typeof(n=t)||null===n)&&"function"!=typeof n)throw r("A fifo list only supports non-primitive values");var n;if(e.has(t))throw r("Cannot push an item that already exists in the list");e.add(t)},pull:()=>{const t=e.values().next().value;return e.delete(t),t},map:n=>t(e.values()).map(n),forEach:t=>{e.forEach(t)},remove:t=>e.delete(t),has:t=>e.has(t),size:()=>e.size,clear:()=>e.clear()})},e.createLifoList=function(){const e=u();return i({push:t=>{if(("object"!=typeof(n=t)||null===n)&&"function"!=typeof n)throw r("A lifo list only supports non-primitive values");var n;e.add(t)},pull:()=>{const n=t(e).pop();return n&&e.delete(n),n},map:n=>t(e).map(n),forEach:t=>{e.forEach(t)},remove:t=>e.delete(t),has:t=>e.has(t),size:()=>e.size,clear:()=>{e.clear()}})},e.createRange=function(e,t){const n=[];for(let o=e;o<=t;o+=1)n.push(o);return n},e.createValuePicker=function(e){if(!e||!e.length)throw r("Expected values not to be an empty list.");let t=-1;function n(){return t<0&&(t=0),e[t]}return{current:n,next:function(){return t<0?n():(t=++t<e.length?t:0,e[t])}}},e.getLastKeyInMap=e=>{const t=[...e.keys()];return t[t.length-1]},e.nonEmptyStrings=function(e){return e.filter(e=>![void 0,null,""].includes(e)&&""!==e.trim())},e.uniqueStrings=function(e){return t(u(e))}});
|
package/index.cjs.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const index_cjs_js$
|
|
4
|
-
const index_cjs_js$
|
|
5
|
-
const index_cjs_js$3 = require('./_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
|
|
3
|
+
const index_cjs_js$3 = require('./_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.cjs.js');
|
|
4
|
+
const index_cjs_js$2 = require('./_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
|
|
6
5
|
const index_cjs_js$1 = require('./_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.cjs.js');
|
|
7
6
|
const index_cjs_js = require('./_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.cjs.js');
|
|
8
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Reports whether a value is a non-primitive, the only kind of value these lists accept.
|
|
10
|
+
*
|
|
11
|
+
* @param value - The value a caller tried to push.
|
|
12
|
+
* @returns True when the value is a non-null object or a function.
|
|
13
|
+
*/
|
|
14
|
+
const isNonPrimitive$1 = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
9
15
|
/**
|
|
10
16
|
* Creates a FIFO (First-In-First-Out) list.
|
|
11
17
|
*
|
|
@@ -29,11 +35,11 @@ const index_cjs_js = require('./_dependencies/@hyperfrontend/immutable-api-utils
|
|
|
29
35
|
function createFifoList() {
|
|
30
36
|
const list = index_cjs_js.createSet();
|
|
31
37
|
const push = (item) => {
|
|
32
|
-
if (
|
|
33
|
-
throw index_cjs_js$
|
|
38
|
+
if (!isNonPrimitive$1(item)) {
|
|
39
|
+
throw index_cjs_js$2.createError('A fifo list only supports non-primitive values');
|
|
34
40
|
}
|
|
35
41
|
if (list.has(item)) {
|
|
36
|
-
throw index_cjs_js$
|
|
42
|
+
throw index_cjs_js$2.createError('Cannot push an item that already exists in the list');
|
|
37
43
|
}
|
|
38
44
|
list.add(item);
|
|
39
45
|
};
|
|
@@ -43,7 +49,7 @@ function createFifoList() {
|
|
|
43
49
|
return value;
|
|
44
50
|
};
|
|
45
51
|
const map = (callback) => {
|
|
46
|
-
return index_cjs_js$
|
|
52
|
+
return index_cjs_js$3.from(list.values()).map(callback);
|
|
47
53
|
};
|
|
48
54
|
const forEach = (callback) => {
|
|
49
55
|
list.forEach(callback);
|
|
@@ -65,6 +71,13 @@ function createFifoList() {
|
|
|
65
71
|
return index_cjs_js$1.freeze(result);
|
|
66
72
|
}
|
|
67
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Reports whether a value is a non-primitive, the only kind of value these lists accept.
|
|
76
|
+
*
|
|
77
|
+
* @param value - The value a caller tried to push.
|
|
78
|
+
* @returns True when the value is a non-null object or a function.
|
|
79
|
+
*/
|
|
80
|
+
const isNonPrimitive = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
68
81
|
/**
|
|
69
82
|
* Creates a LIFO (Last-In-First-Out) list.
|
|
70
83
|
*
|
|
@@ -85,20 +98,20 @@ function createFifoList() {
|
|
|
85
98
|
function createLifoList() {
|
|
86
99
|
const list = index_cjs_js.createSet();
|
|
87
100
|
const push = (item) => {
|
|
88
|
-
if (
|
|
89
|
-
throw index_cjs_js$
|
|
101
|
+
if (!isNonPrimitive(item)) {
|
|
102
|
+
throw index_cjs_js$2.createError('A lifo list only supports non-primitive values');
|
|
90
103
|
}
|
|
91
104
|
list.add(item);
|
|
92
105
|
};
|
|
93
106
|
const pull = () => {
|
|
94
|
-
const lastItem = index_cjs_js$
|
|
107
|
+
const lastItem = index_cjs_js$3.from(list).pop();
|
|
95
108
|
if (lastItem) {
|
|
96
109
|
list.delete(lastItem);
|
|
97
110
|
}
|
|
98
111
|
return lastItem;
|
|
99
112
|
};
|
|
100
113
|
const map = (callback) => {
|
|
101
|
-
return index_cjs_js$
|
|
114
|
+
return index_cjs_js$3.from(list).map(callback);
|
|
102
115
|
};
|
|
103
116
|
const forEach = (callback) => {
|
|
104
117
|
list.forEach(callback);
|
|
@@ -163,7 +176,7 @@ function createRange(start, end) {
|
|
|
163
176
|
*/
|
|
164
177
|
function createValuePicker(values) {
|
|
165
178
|
if (!values || !values.length) {
|
|
166
|
-
throw index_cjs_js$
|
|
179
|
+
throw index_cjs_js$2.createError('Expected values not to be an empty list.');
|
|
167
180
|
}
|
|
168
181
|
let index = -1;
|
|
169
182
|
/**
|
|
@@ -241,7 +254,7 @@ function nonEmptyStrings(values) {
|
|
|
241
254
|
* ```
|
|
242
255
|
*/
|
|
243
256
|
function uniqueStrings(values) {
|
|
244
|
-
return index_cjs_js$
|
|
257
|
+
return index_cjs_js$3.from(index_cjs_js.createSet(values));
|
|
245
258
|
}
|
|
246
259
|
|
|
247
260
|
exports.createFifoList = createFifoList;
|
package/index.esm.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { getType } from './_dependencies/@hyperfrontend/data-utils/index.esm.js';
|
|
2
1
|
import { from } from './_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.esm.js';
|
|
3
2
|
import { createError } from './_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.esm.js';
|
|
4
3
|
import { freeze } from './_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js';
|
|
5
4
|
import { createSet } from './_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.esm.js';
|
|
6
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Reports whether a value is a non-primitive, the only kind of value these lists accept.
|
|
8
|
+
*
|
|
9
|
+
* @param value - The value a caller tried to push.
|
|
10
|
+
* @returns True when the value is a non-null object or a function.
|
|
11
|
+
*/
|
|
12
|
+
const isNonPrimitive$1 = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
7
13
|
/**
|
|
8
14
|
* Creates a FIFO (First-In-First-Out) list.
|
|
9
15
|
*
|
|
@@ -27,7 +33,7 @@ import { createSet } from './_dependencies/@hyperfrontend/immutable-api-utils/bu
|
|
|
27
33
|
function createFifoList() {
|
|
28
34
|
const list = createSet();
|
|
29
35
|
const push = (item) => {
|
|
30
|
-
if (
|
|
36
|
+
if (!isNonPrimitive$1(item)) {
|
|
31
37
|
throw createError('A fifo list only supports non-primitive values');
|
|
32
38
|
}
|
|
33
39
|
if (list.has(item)) {
|
|
@@ -63,6 +69,13 @@ function createFifoList() {
|
|
|
63
69
|
return freeze(result);
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Reports whether a value is a non-primitive, the only kind of value these lists accept.
|
|
74
|
+
*
|
|
75
|
+
* @param value - The value a caller tried to push.
|
|
76
|
+
* @returns True when the value is a non-null object or a function.
|
|
77
|
+
*/
|
|
78
|
+
const isNonPrimitive = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
66
79
|
/**
|
|
67
80
|
* Creates a LIFO (Last-In-First-Out) list.
|
|
68
81
|
*
|
|
@@ -83,7 +96,7 @@ function createFifoList() {
|
|
|
83
96
|
function createLifoList() {
|
|
84
97
|
const list = createSet();
|
|
85
98
|
const push = (item) => {
|
|
86
|
-
if (
|
|
99
|
+
if (!isNonPrimitive(item)) {
|
|
87
100
|
throw createError('A lifo list only supports non-primitive values');
|
|
88
101
|
}
|
|
89
102
|
list.add(item);
|
package/package.json
CHANGED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { isArray } = require('../immutable-api-utils/built-in-copy/array/index.cjs.js');
|
|
4
|
-
const { createError } = require('../immutable-api-utils/built-in-copy/error/index.cjs.js');
|
|
5
|
-
const { freeze, keys: keys$1 } = require('../immutable-api-utils/built-in-copy/object/index.cjs.js');
|
|
6
|
-
const { createMap } = require('../immutable-api-utils/built-in-copy/map/index.cjs.js');
|
|
7
|
-
const { createDate } = require('../immutable-api-utils/built-in-copy/date/index.cjs.js');
|
|
8
|
-
const { random, round } = require('../immutable-api-utils/built-in-copy/math/index.cjs.js');
|
|
9
|
-
|
|
10
|
-
const isMarker = (text) => {
|
|
11
|
-
if (typeof text !== 'string' || !text.startsWith('__$'))
|
|
12
|
-
return false;
|
|
13
|
-
return /^__\$[0-9]+$/.test(text);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const registeredClasses = [];
|
|
17
|
-
const registeredIterableClasses = [
|
|
18
|
-
{
|
|
19
|
-
classRef: Array,
|
|
20
|
-
instantiate: () => [],
|
|
21
|
-
getKeys: (target) => {
|
|
22
|
-
const keysArray = keys$1(target);
|
|
23
|
-
if (getConfig().detectCircularReferences) {
|
|
24
|
-
return keysArray.filter((key) => !isMarker(key));
|
|
25
|
-
}
|
|
26
|
-
return keysArray;
|
|
27
|
-
},
|
|
28
|
-
read: (target, key) => target[key],
|
|
29
|
-
write: (target, value, key) => (target[key] = value),
|
|
30
|
-
remove: (target, value) => target.splice(value, 1),
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
classRef: Object,
|
|
34
|
-
instantiate: () => ({}),
|
|
35
|
-
getKeys: (target) => {
|
|
36
|
-
const keysArray = keys$1(target);
|
|
37
|
-
if (getConfig().detectCircularReferences) {
|
|
38
|
-
return keysArray.filter((key) => !isMarker(key));
|
|
39
|
-
}
|
|
40
|
-
return keysArray;
|
|
41
|
-
},
|
|
42
|
-
read: (target, key) => target[key],
|
|
43
|
-
write: (target, value, key) => (target[key] = value),
|
|
44
|
-
remove: (target, value) => delete target[value],
|
|
45
|
-
},
|
|
46
|
-
];
|
|
47
|
-
let samePositionOfOwnProperties = false;
|
|
48
|
-
let detectCircularReferences = false;
|
|
49
|
-
const getConfig = () => ({
|
|
50
|
-
samePositionOfOwnProperties,
|
|
51
|
-
detectCircularReferences,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const getKeysFromIterable = (target, dataType) => {
|
|
55
|
-
if (dataType === 'array')
|
|
56
|
-
dataType = Array.name;
|
|
57
|
-
if (dataType === 'object')
|
|
58
|
-
dataType = Object.name;
|
|
59
|
-
const iterableClass = registeredIterableClasses.find(({ classRef }) => dataType === classRef.name);
|
|
60
|
-
if (iterableClass === undefined)
|
|
61
|
-
return [];
|
|
62
|
-
return iterableClass.getKeys(target);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const getType = (target) => {
|
|
66
|
-
if (target === null)
|
|
67
|
-
return 'null';
|
|
68
|
-
const nativeDataType = typeof target;
|
|
69
|
-
if (nativeDataType === 'object') {
|
|
70
|
-
if (isArray(target))
|
|
71
|
-
return 'array';
|
|
72
|
-
for (const registeredClass of registeredClasses) {
|
|
73
|
-
if (target instanceof registeredClass)
|
|
74
|
-
return registeredClass.name;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return nativeDataType;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const getIterableTypes = () => registeredIterableClasses.map(({ classRef }) => {
|
|
81
|
-
const name = classRef.name;
|
|
82
|
-
if (name === Object.name)
|
|
83
|
-
return 'object';
|
|
84
|
-
if (name === Array.name)
|
|
85
|
-
return 'array';
|
|
86
|
-
return name;
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const isIterableType = (dataType) => getIterableTypes().includes(dataType);
|
|
90
|
-
|
|
91
|
-
const isIterable = (target) => isIterableType(getType(target));
|
|
92
|
-
|
|
93
|
-
const marker = () => {
|
|
94
|
-
const randomValue = round(random() * 10000000000000);
|
|
95
|
-
const sequential = createDate().getTime();
|
|
96
|
-
const unique = `${randomValue}${sequential}`;
|
|
97
|
-
const prefix = `__$`;
|
|
98
|
-
return `${prefix}${unique}`;
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const referenceStack = () => {
|
|
102
|
-
const records = createMap();
|
|
103
|
-
const flag = marker();
|
|
104
|
-
const exists = (ref) => (isIterable(ref) ? flag in ref && records.has(ref[flag]) : false);
|
|
105
|
-
const add = (ref) => {
|
|
106
|
-
if (!isIterable(ref) || exists(ref))
|
|
107
|
-
return;
|
|
108
|
-
ref[flag] = Symbol();
|
|
109
|
-
records.set(ref[flag], [flag, ref, records.size]);
|
|
110
|
-
};
|
|
111
|
-
const lastSeen = (ref) => {
|
|
112
|
-
if (!isIterable(ref))
|
|
113
|
-
return null;
|
|
114
|
-
const record = records.get(ref[flag]);
|
|
115
|
-
return record ? record[2] - records.size : null;
|
|
116
|
-
};
|
|
117
|
-
const clear = () => (records.forEach(([key, ref]) => {
|
|
118
|
-
delete ref[key];
|
|
119
|
-
}),
|
|
120
|
-
records.clear());
|
|
121
|
-
return {
|
|
122
|
-
add: (ref) => add(ref),
|
|
123
|
-
exists: (ref) => exists(ref),
|
|
124
|
-
lastSeen: (ref) => lastSeen(ref),
|
|
125
|
-
clear: () => clear(),
|
|
126
|
-
get size() {
|
|
127
|
-
return records.size;
|
|
128
|
-
},
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const errorMessage = (thing, type) => `Expected ${thing} to be ${type}.`;
|
|
133
|
-
const nextIterationDetails = (path, key, value) => ({
|
|
134
|
-
nextPath: [...path, key],
|
|
135
|
-
nextValue: value[key],
|
|
136
|
-
});
|
|
137
|
-
const circularDependencyTraversal = (condition, callback, config, key, path, value, parent, state, stack, root = false) => {
|
|
138
|
-
if (stack.exists(value))
|
|
139
|
-
return state;
|
|
140
|
-
const ok = condition(config, key, value, path, parent);
|
|
141
|
-
if (config.exitEarly)
|
|
142
|
-
return state;
|
|
143
|
-
if (ok)
|
|
144
|
-
callback(key, value, path, state, parent);
|
|
145
|
-
stack.add(value);
|
|
146
|
-
const type = getType(value);
|
|
147
|
-
if (!isIterableType(type))
|
|
148
|
-
return state;
|
|
149
|
-
const keys = getKeysFromIterable(value, type);
|
|
150
|
-
keys.forEach((key) => {
|
|
151
|
-
const { nextPath, nextValue } = nextIterationDetails(path, key, value);
|
|
152
|
-
circularDependencyTraversal(condition, callback, config, key, nextPath, nextValue, value, state, stack);
|
|
153
|
-
});
|
|
154
|
-
if (root)
|
|
155
|
-
stack.clear();
|
|
156
|
-
return state;
|
|
157
|
-
};
|
|
158
|
-
const nonCircularDependencyTraversal = (condition, callback, config, key, path, value, parent, state) => {
|
|
159
|
-
const ok = condition(config, key, value, path, parent);
|
|
160
|
-
if (config.exitEarly)
|
|
161
|
-
return state;
|
|
162
|
-
if (ok)
|
|
163
|
-
callback(key, value, path, state, parent);
|
|
164
|
-
const type = getType(value);
|
|
165
|
-
if (!isIterableType(type))
|
|
166
|
-
return state;
|
|
167
|
-
const keys = getKeysFromIterable(value, type);
|
|
168
|
-
keys.forEach((key) => {
|
|
169
|
-
const { nextPath, nextValue } = nextIterationDetails(path, key, value);
|
|
170
|
-
nonCircularDependencyTraversal(condition, callback, config, key, nextPath, nextValue, value, state);
|
|
171
|
-
});
|
|
172
|
-
return state;
|
|
173
|
-
};
|
|
174
|
-
const traversal = (target, condition, callback, options, state) => {
|
|
175
|
-
if (typeof callback !== 'function')
|
|
176
|
-
throw createError(errorMessage('callback', 'a function'));
|
|
177
|
-
if (!(typeof options === 'object' && !isArray(options)))
|
|
178
|
-
throw createError(errorMessage('options', 'an object'));
|
|
179
|
-
if (!isArray(options.depth))
|
|
180
|
-
throw createError(errorMessage('options.depth', 'an array'));
|
|
181
|
-
const [startDepth, maxDepth] = options.depth;
|
|
182
|
-
if (startDepth !== void 0 && typeof startDepth !== 'number')
|
|
183
|
-
throw createError(errorMessage('options.depth.0', 'a number'));
|
|
184
|
-
if (maxDepth !== void 0) {
|
|
185
|
-
const maxDepthType = typeof maxDepth;
|
|
186
|
-
if (!['number', 'string'].includes(maxDepthType))
|
|
187
|
-
throw createError(errorMessage('options.depth.1', 'a number or a string'));
|
|
188
|
-
if (maxDepthType === 'string' && maxDepth !== '*')
|
|
189
|
-
throw createError("Only valid string value in options.depth.1 is '*'.");
|
|
190
|
-
}
|
|
191
|
-
const config = {
|
|
192
|
-
depth: freeze([options.depth[0] ?? 0, options.depth[1] ?? '*']),
|
|
193
|
-
exitEarly: false,
|
|
194
|
-
};
|
|
195
|
-
const initialArgs = [condition, callback, config, '', [], target, void 0, state];
|
|
196
|
-
if (getConfig().detectCircularReferences)
|
|
197
|
-
return circularDependencyTraversal(...initialArgs, referenceStack(), true);
|
|
198
|
-
return nonCircularDependencyTraversal(...initialArgs);
|
|
199
|
-
};
|
|
200
|
-
const createTraversal = (condition) => (target, callback, options, state) => traversal(target, condition, callback, options ?? { depth: [0, '*'] }, state ?? {});
|
|
201
|
-
const condition = (config, key, value, path) => !(path.length < config.depth[0] || config.depth[1] < path.length);
|
|
202
|
-
const traverseBetweenDepthRange = createTraversal(condition);
|
|
203
|
-
exports.createTraversal = createTraversal;
|
|
204
|
-
exports.getConfig = getConfig;
|
|
205
|
-
exports.getIterableTypes = getIterableTypes;
|
|
206
|
-
exports.getKeysFromIterable = getKeysFromIterable;
|
|
207
|
-
exports.getType = getType;
|
|
208
|
-
exports.isIterable = isIterable;
|
|
209
|
-
exports.isIterableType = isIterableType;
|
|
210
|
-
exports.isMarker = isMarker;
|
|
211
|
-
exports.marker = marker;
|
|
212
|
-
exports.referenceStack = referenceStack;
|
|
213
|
-
exports.registeredClasses = registeredClasses;
|
|
214
|
-
exports.registeredIterableClasses = registeredIterableClasses;
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { isArray } from '../immutable-api-utils/built-in-copy/array/index.esm.js';
|
|
2
|
-
import { createError } from '../immutable-api-utils/built-in-copy/error/index.esm.js';
|
|
3
|
-
import { keys, freeze } from '../immutable-api-utils/built-in-copy/object/index.esm.js';
|
|
4
|
-
import { createMap } from '../immutable-api-utils/built-in-copy/map/index.esm.js';
|
|
5
|
-
import { createDate } from '../immutable-api-utils/built-in-copy/date/index.esm.js';
|
|
6
|
-
import { round, random } from '../immutable-api-utils/built-in-copy/math/index.esm.js';
|
|
7
|
-
|
|
8
|
-
const isMarker = (text) => {
|
|
9
|
-
if (typeof text !== 'string' || !text.startsWith('__$'))
|
|
10
|
-
return false;
|
|
11
|
-
return /^__\$[0-9]+$/.test(text);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const registeredClasses = [];
|
|
15
|
-
const registeredIterableClasses = [
|
|
16
|
-
{
|
|
17
|
-
classRef: Array,
|
|
18
|
-
instantiate: () => [],
|
|
19
|
-
getKeys: (target) => {
|
|
20
|
-
const keysArray = keys(target);
|
|
21
|
-
if (getConfig().detectCircularReferences) {
|
|
22
|
-
return keysArray.filter((key) => !isMarker(key));
|
|
23
|
-
}
|
|
24
|
-
return keysArray;
|
|
25
|
-
},
|
|
26
|
-
read: (target, key) => target[key],
|
|
27
|
-
write: (target, value, key) => (target[key] = value),
|
|
28
|
-
remove: (target, value) => target.splice(value, 1),
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
classRef: Object,
|
|
32
|
-
instantiate: () => ({}),
|
|
33
|
-
getKeys: (target) => {
|
|
34
|
-
const keysArray = keys(target);
|
|
35
|
-
if (getConfig().detectCircularReferences) {
|
|
36
|
-
return keysArray.filter((key) => !isMarker(key));
|
|
37
|
-
}
|
|
38
|
-
return keysArray;
|
|
39
|
-
},
|
|
40
|
-
read: (target, key) => target[key],
|
|
41
|
-
write: (target, value, key) => (target[key] = value),
|
|
42
|
-
remove: (target, value) => delete target[value],
|
|
43
|
-
},
|
|
44
|
-
];
|
|
45
|
-
let samePositionOfOwnProperties = false;
|
|
46
|
-
let detectCircularReferences = false;
|
|
47
|
-
const getConfig = () => ({
|
|
48
|
-
samePositionOfOwnProperties,
|
|
49
|
-
detectCircularReferences,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const getKeysFromIterable = (target, dataType) => {
|
|
53
|
-
if (dataType === 'array')
|
|
54
|
-
dataType = Array.name;
|
|
55
|
-
if (dataType === 'object')
|
|
56
|
-
dataType = Object.name;
|
|
57
|
-
const iterableClass = registeredIterableClasses.find(({ classRef }) => dataType === classRef.name);
|
|
58
|
-
if (iterableClass === undefined)
|
|
59
|
-
return [];
|
|
60
|
-
return iterableClass.getKeys(target);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const getType = (target) => {
|
|
64
|
-
if (target === null)
|
|
65
|
-
return 'null';
|
|
66
|
-
const nativeDataType = typeof target;
|
|
67
|
-
if (nativeDataType === 'object') {
|
|
68
|
-
if (isArray(target))
|
|
69
|
-
return 'array';
|
|
70
|
-
for (const registeredClass of registeredClasses) {
|
|
71
|
-
if (target instanceof registeredClass)
|
|
72
|
-
return registeredClass.name;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return nativeDataType;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const getIterableTypes = () => registeredIterableClasses.map(({ classRef }) => {
|
|
79
|
-
const name = classRef.name;
|
|
80
|
-
if (name === Object.name)
|
|
81
|
-
return 'object';
|
|
82
|
-
if (name === Array.name)
|
|
83
|
-
return 'array';
|
|
84
|
-
return name;
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const isIterableType = (dataType) => getIterableTypes().includes(dataType);
|
|
88
|
-
|
|
89
|
-
const isIterable = (target) => isIterableType(getType(target));
|
|
90
|
-
|
|
91
|
-
const marker = () => {
|
|
92
|
-
const randomValue = round(random() * 10000000000000);
|
|
93
|
-
const sequential = createDate().getTime();
|
|
94
|
-
const unique = `${randomValue}${sequential}`;
|
|
95
|
-
const prefix = `__$`;
|
|
96
|
-
return `${prefix}${unique}`;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const referenceStack = () => {
|
|
100
|
-
const records = createMap();
|
|
101
|
-
const flag = marker();
|
|
102
|
-
const exists = (ref) => (isIterable(ref) ? flag in ref && records.has(ref[flag]) : false);
|
|
103
|
-
const add = (ref) => {
|
|
104
|
-
if (!isIterable(ref) || exists(ref))
|
|
105
|
-
return;
|
|
106
|
-
ref[flag] = Symbol();
|
|
107
|
-
records.set(ref[flag], [flag, ref, records.size]);
|
|
108
|
-
};
|
|
109
|
-
const lastSeen = (ref) => {
|
|
110
|
-
if (!isIterable(ref))
|
|
111
|
-
return null;
|
|
112
|
-
const record = records.get(ref[flag]);
|
|
113
|
-
return record ? record[2] - records.size : null;
|
|
114
|
-
};
|
|
115
|
-
const clear = () => (records.forEach(([key, ref]) => {
|
|
116
|
-
delete ref[key];
|
|
117
|
-
}),
|
|
118
|
-
records.clear());
|
|
119
|
-
return {
|
|
120
|
-
add: (ref) => add(ref),
|
|
121
|
-
exists: (ref) => exists(ref),
|
|
122
|
-
lastSeen: (ref) => lastSeen(ref),
|
|
123
|
-
clear: () => clear(),
|
|
124
|
-
get size() {
|
|
125
|
-
return records.size;
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const errorMessage = (thing, type) => `Expected ${thing} to be ${type}.`;
|
|
131
|
-
const nextIterationDetails = (path, key, value) => ({
|
|
132
|
-
nextPath: [...path, key],
|
|
133
|
-
nextValue: value[key],
|
|
134
|
-
});
|
|
135
|
-
const circularDependencyTraversal = (condition, callback, config, key, path, value, parent, state, stack, root = false) => {
|
|
136
|
-
if (stack.exists(value))
|
|
137
|
-
return state;
|
|
138
|
-
const ok = condition(config, key, value, path, parent);
|
|
139
|
-
if (config.exitEarly)
|
|
140
|
-
return state;
|
|
141
|
-
if (ok)
|
|
142
|
-
callback(key, value, path, state, parent);
|
|
143
|
-
stack.add(value);
|
|
144
|
-
const type = getType(value);
|
|
145
|
-
if (!isIterableType(type))
|
|
146
|
-
return state;
|
|
147
|
-
const keys = getKeysFromIterable(value, type);
|
|
148
|
-
keys.forEach((key) => {
|
|
149
|
-
const { nextPath, nextValue } = nextIterationDetails(path, key, value);
|
|
150
|
-
circularDependencyTraversal(condition, callback, config, key, nextPath, nextValue, value, state, stack);
|
|
151
|
-
});
|
|
152
|
-
if (root)
|
|
153
|
-
stack.clear();
|
|
154
|
-
return state;
|
|
155
|
-
};
|
|
156
|
-
const nonCircularDependencyTraversal = (condition, callback, config, key, path, value, parent, state) => {
|
|
157
|
-
const ok = condition(config, key, value, path, parent);
|
|
158
|
-
if (config.exitEarly)
|
|
159
|
-
return state;
|
|
160
|
-
if (ok)
|
|
161
|
-
callback(key, value, path, state, parent);
|
|
162
|
-
const type = getType(value);
|
|
163
|
-
if (!isIterableType(type))
|
|
164
|
-
return state;
|
|
165
|
-
const keys = getKeysFromIterable(value, type);
|
|
166
|
-
keys.forEach((key) => {
|
|
167
|
-
const { nextPath, nextValue } = nextIterationDetails(path, key, value);
|
|
168
|
-
nonCircularDependencyTraversal(condition, callback, config, key, nextPath, nextValue, value, state);
|
|
169
|
-
});
|
|
170
|
-
return state;
|
|
171
|
-
};
|
|
172
|
-
const traversal = (target, condition, callback, options, state) => {
|
|
173
|
-
if (typeof callback !== 'function')
|
|
174
|
-
throw createError(errorMessage('callback', 'a function'));
|
|
175
|
-
if (!(typeof options === 'object' && !isArray(options)))
|
|
176
|
-
throw createError(errorMessage('options', 'an object'));
|
|
177
|
-
if (!isArray(options.depth))
|
|
178
|
-
throw createError(errorMessage('options.depth', 'an array'));
|
|
179
|
-
const [startDepth, maxDepth] = options.depth;
|
|
180
|
-
if (startDepth !== void 0 && typeof startDepth !== 'number')
|
|
181
|
-
throw createError(errorMessage('options.depth.0', 'a number'));
|
|
182
|
-
if (maxDepth !== void 0) {
|
|
183
|
-
const maxDepthType = typeof maxDepth;
|
|
184
|
-
if (!['number', 'string'].includes(maxDepthType))
|
|
185
|
-
throw createError(errorMessage('options.depth.1', 'a number or a string'));
|
|
186
|
-
if (maxDepthType === 'string' && maxDepth !== '*')
|
|
187
|
-
throw createError("Only valid string value in options.depth.1 is '*'.");
|
|
188
|
-
}
|
|
189
|
-
const config = {
|
|
190
|
-
depth: freeze([options.depth[0] ?? 0, options.depth[1] ?? '*']),
|
|
191
|
-
exitEarly: false,
|
|
192
|
-
};
|
|
193
|
-
const initialArgs = [condition, callback, config, '', [], target, void 0, state];
|
|
194
|
-
if (getConfig().detectCircularReferences)
|
|
195
|
-
return circularDependencyTraversal(...initialArgs, referenceStack(), true);
|
|
196
|
-
return nonCircularDependencyTraversal(...initialArgs);
|
|
197
|
-
};
|
|
198
|
-
const createTraversal = (condition) => (target, callback, options, state) => traversal(target, condition, callback, options ?? { depth: [0, '*'] }, state ?? {});
|
|
199
|
-
const condition = (config, key, value, path) => !(path.length < config.depth[0] || config.depth[1] < path.length);
|
|
200
|
-
const traverseBetweenDepthRange = createTraversal(condition);
|
|
201
|
-
|
|
202
|
-
export { createTraversal, getConfig, getIterableTypes, getKeysFromIterable, getType, isIterable, isIterableType, isMarker, marker, referenceStack, registeredClasses, registeredIterableClasses };
|