@fluixi/utils 1.0.0-alpha.53
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/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/dist/lib/array/advanced.cjs +1 -0
- package/dist/lib/array/advanced.d.ts +1116 -0
- package/dist/lib/array/advanced.d.ts.map +1 -0
- package/dist/lib/array/advanced.js +2429 -0
- package/dist/lib/array/advanced.mjs +1 -0
- package/dist/lib/array/array.cjs +1 -0
- package/dist/lib/array/array.d.ts +6 -0
- package/dist/lib/array/array.d.ts.map +1 -0
- package/dist/lib/array/array.js +31 -0
- package/dist/lib/array/array.mjs +1 -0
- package/dist/lib/array/index.cjs +1 -0
- package/dist/lib/array/index.d.ts +4 -0
- package/dist/lib/array/index.d.ts.map +1 -0
- package/dist/lib/array/index.js +3 -0
- package/dist/lib/array/index.mjs +1 -0
- package/dist/lib/array/utils.cjs +1 -0
- package/dist/lib/array/utils.d.ts +100 -0
- package/dist/lib/array/utils.d.ts.map +1 -0
- package/dist/lib/array/utils.js +165 -0
- package/dist/lib/array/utils.mjs +1 -0
- package/dist/lib/color/color.cjs +1 -0
- package/dist/lib/color/color.d.ts +366 -0
- package/dist/lib/color/color.d.ts.map +1 -0
- package/dist/lib/color/color.js +722 -0
- package/dist/lib/color/color.mjs +1 -0
- package/dist/lib/color/constants.cjs +1 -0
- package/dist/lib/color/constants.d.ts +361 -0
- package/dist/lib/color/constants.d.ts.map +1 -0
- package/dist/lib/color/constants.js +358 -0
- package/dist/lib/color/constants.mjs +1 -0
- package/dist/lib/color/conversions.cjs +1 -0
- package/dist/lib/color/conversions.d.ts +166 -0
- package/dist/lib/color/conversions.d.ts.map +1 -0
- package/dist/lib/color/conversions.js +766 -0
- package/dist/lib/color/conversions.mjs +1 -0
- package/dist/lib/color/helpers.cjs +1 -0
- package/dist/lib/color/helpers.d.ts +75 -0
- package/dist/lib/color/helpers.d.ts.map +1 -0
- package/dist/lib/color/helpers.js +419 -0
- package/dist/lib/color/helpers.mjs +1 -0
- package/dist/lib/color/index.cjs +1 -0
- package/dist/lib/color/index.d.ts +12 -0
- package/dist/lib/color/index.d.ts.map +1 -0
- package/dist/lib/color/index.js +15 -0
- package/dist/lib/color/index.mjs +1 -0
- package/dist/lib/color/types.cjs +1 -0
- package/dist/lib/color/types.d.ts +179 -0
- package/dist/lib/color/types.d.ts.map +1 -0
- package/dist/lib/color/types.js +1 -0
- package/dist/lib/color/types.mjs +0 -0
- package/dist/lib/color/utils.cjs +1 -0
- package/dist/lib/color/utils.d.ts +199 -0
- package/dist/lib/color/utils.d.ts.map +1 -0
- package/dist/lib/color/utils.js +858 -0
- package/dist/lib/color/utils.mjs +1 -0
- package/dist/lib/compare/comparators.cjs +1 -0
- package/dist/lib/compare/comparators.d.ts +65 -0
- package/dist/lib/compare/comparators.d.ts.map +1 -0
- package/dist/lib/compare/comparators.js +210 -0
- package/dist/lib/compare/comparators.mjs +1 -0
- package/dist/lib/compare/index.cjs +1 -0
- package/dist/lib/compare/index.d.ts +2 -0
- package/dist/lib/compare/index.d.ts.map +1 -0
- package/dist/lib/compare/index.js +1 -0
- package/dist/lib/compare/index.mjs +1 -0
- package/dist/lib/crypto/hash.cjs +1 -0
- package/dist/lib/crypto/hash.d.ts +6 -0
- package/dist/lib/crypto/hash.d.ts.map +1 -0
- package/dist/lib/crypto/hash.js +53 -0
- package/dist/lib/crypto/hash.mjs +1 -0
- package/dist/lib/crypto/index.cjs +1 -0
- package/dist/lib/crypto/index.d.ts +2 -0
- package/dist/lib/crypto/index.d.ts.map +1 -0
- package/dist/lib/crypto/index.js +1 -0
- package/dist/lib/crypto/index.mjs +1 -0
- package/dist/lib/dom/events.cjs +1 -0
- package/dist/lib/dom/events.d.ts +159 -0
- package/dist/lib/dom/events.d.ts.map +1 -0
- package/dist/lib/dom/events.js +50 -0
- package/dist/lib/dom/events.mjs +1 -0
- package/dist/lib/dom/index.cjs +1 -0
- package/dist/lib/dom/index.d.ts +3 -0
- package/dist/lib/dom/index.d.ts.map +1 -0
- package/dist/lib/dom/index.js +2 -0
- package/dist/lib/dom/index.mjs +1 -0
- package/dist/lib/dom/utils.cjs +1 -0
- package/dist/lib/dom/utils.d.ts +6 -0
- package/dist/lib/dom/utils.d.ts.map +1 -0
- package/dist/lib/dom/utils.js +37 -0
- package/dist/lib/dom/utils.mjs +1 -0
- package/dist/lib/functions/functions.cjs +1 -0
- package/dist/lib/functions/functions.d.ts +329 -0
- package/dist/lib/functions/functions.d.ts.map +1 -0
- package/dist/lib/functions/functions.js +585 -0
- package/dist/lib/functions/functions.mjs +1 -0
- package/dist/lib/functions/index.cjs +1 -0
- package/dist/lib/functions/index.d.ts +3 -0
- package/dist/lib/functions/index.d.ts.map +1 -0
- package/dist/lib/functions/index.js +1 -0
- package/dist/lib/functions/index.mjs +1 -0
- package/dist/lib/functions/types.cjs +1 -0
- package/dist/lib/functions/types.d.ts +2 -0
- package/dist/lib/functions/types.d.ts.map +1 -0
- package/dist/lib/functions/types.js +1 -0
- package/dist/lib/functions/types.mjs +0 -0
- package/dist/lib/helpers.cjs +1 -0
- package/dist/lib/helpers.d.ts +13 -0
- package/dist/lib/helpers.d.ts.map +1 -0
- package/dist/lib/helpers.js +66 -0
- package/dist/lib/helpers.mjs +1 -0
- package/dist/lib/index.cjs +1 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.mjs +1 -0
- package/dist/lib/maths/constants.cjs +1 -0
- package/dist/lib/maths/constants.d.ts +84 -0
- package/dist/lib/maths/constants.d.ts.map +1 -0
- package/dist/lib/maths/constants.js +79 -0
- package/dist/lib/maths/constants.mjs +1 -0
- package/dist/lib/maths/fn/advanced.cjs +1 -0
- package/dist/lib/maths/fn/advanced.d.ts +149 -0
- package/dist/lib/maths/fn/advanced.d.ts.map +1 -0
- package/dist/lib/maths/fn/advanced.js +264 -0
- package/dist/lib/maths/fn/advanced.mjs +1 -0
- package/dist/lib/maths/fn/basic.cjs +1 -0
- package/dist/lib/maths/fn/basic.d.ts +182 -0
- package/dist/lib/maths/fn/basic.d.ts.map +1 -0
- package/dist/lib/maths/fn/basic.js +343 -0
- package/dist/lib/maths/fn/basic.mjs +1 -0
- package/dist/lib/maths/fn/complex.cjs +1 -0
- package/dist/lib/maths/fn/complex.d.ts +153 -0
- package/dist/lib/maths/fn/complex.d.ts.map +1 -0
- package/dist/lib/maths/fn/complex.js +383 -0
- package/dist/lib/maths/fn/complex.mjs +1 -0
- package/dist/lib/maths/fn/index.cjs +1 -0
- package/dist/lib/maths/fn/index.d.ts +10 -0
- package/dist/lib/maths/fn/index.d.ts.map +1 -0
- package/dist/lib/maths/fn/index.js +9 -0
- package/dist/lib/maths/fn/index.mjs +1 -0
- package/dist/lib/maths/fn/trigo.cjs +1 -0
- package/dist/lib/maths/fn/trigo.d.ts +147 -0
- package/dist/lib/maths/fn/trigo.d.ts.map +1 -0
- package/dist/lib/maths/fn/trigo.js +227 -0
- package/dist/lib/maths/fn/trigo.mjs +1 -0
- package/dist/lib/maths/fn/vector.cjs +1 -0
- package/dist/lib/maths/fn/vector.d.ts +512 -0
- package/dist/lib/maths/fn/vector.d.ts.map +1 -0
- package/dist/lib/maths/fn/vector.js +1136 -0
- package/dist/lib/maths/fn/vector.mjs +1 -0
- package/dist/lib/maths/formatter.cjs +1 -0
- package/dist/lib/maths/formatter.d.ts +35 -0
- package/dist/lib/maths/formatter.d.ts.map +1 -0
- package/dist/lib/maths/formatter.js +56 -0
- package/dist/lib/maths/formatter.mjs +1 -0
- package/dist/lib/maths/guards.cjs +1 -0
- package/dist/lib/maths/guards.d.ts +166 -0
- package/dist/lib/maths/guards.d.ts.map +1 -0
- package/dist/lib/maths/guards.js +315 -0
- package/dist/lib/maths/guards.mjs +1 -0
- package/dist/lib/maths/index.cjs +1 -0
- package/dist/lib/maths/index.d.ts +24 -0
- package/dist/lib/maths/index.d.ts.map +1 -0
- package/dist/lib/maths/index.js +29 -0
- package/dist/lib/maths/index.mjs +1 -0
- package/dist/lib/maths/parser.cjs +1 -0
- package/dist/lib/maths/parser.d.ts +20 -0
- package/dist/lib/maths/parser.d.ts.map +1 -0
- package/dist/lib/maths/parser.js +78 -0
- package/dist/lib/maths/parser.mjs +1 -0
- package/dist/lib/maths/random.cjs +1 -0
- package/dist/lib/maths/random.d.ts +27 -0
- package/dist/lib/maths/random.d.ts.map +1 -0
- package/dist/lib/maths/random.js +49 -0
- package/dist/lib/maths/random.mjs +1 -0
- package/dist/lib/maths/types/advanced.cjs +1 -0
- package/dist/lib/maths/types/advanced.d.ts +357 -0
- package/dist/lib/maths/types/advanced.d.ts.map +1 -0
- package/dist/lib/maths/types/advanced.js +5 -0
- package/dist/lib/maths/types/advanced.mjs +0 -0
- package/dist/lib/maths/types/basic.cjs +1 -0
- package/dist/lib/maths/types/basic.d.ts +95 -0
- package/dist/lib/maths/types/basic.d.ts.map +1 -0
- package/dist/lib/maths/types/basic.js +5 -0
- package/dist/lib/maths/types/basic.mjs +0 -0
- package/dist/lib/maths/types/index.cjs +1 -0
- package/dist/lib/maths/types/index.d.ts +4 -0
- package/dist/lib/maths/types/index.d.ts.map +1 -0
- package/dist/lib/maths/types/index.js +3 -0
- package/dist/lib/maths/types/index.mjs +0 -0
- package/dist/lib/maths/types/negative.cjs +1 -0
- package/dist/lib/maths/types/negative.d.ts +51 -0
- package/dist/lib/maths/types/negative.d.ts.map +1 -0
- package/dist/lib/maths/types/negative.js +5 -0
- package/dist/lib/maths/types/negative.mjs +0 -0
- package/dist/lib/maths/types/operations.cjs +1 -0
- package/dist/lib/maths/types/operations.d.ts +188 -0
- package/dist/lib/maths/types/operations.d.ts.map +1 -0
- package/dist/lib/maths/types/operations.js +6 -0
- package/dist/lib/maths/types/operations.mjs +0 -0
- package/dist/lib/object/clone.cjs +1 -0
- package/dist/lib/object/clone.d.ts +46 -0
- package/dist/lib/object/clone.d.ts.map +1 -0
- package/dist/lib/object/clone.js +104 -0
- package/dist/lib/object/clone.mjs +1 -0
- package/dist/lib/object/diff.cjs +1 -0
- package/dist/lib/object/diff.d.ts +203 -0
- package/dist/lib/object/diff.d.ts.map +1 -0
- package/dist/lib/object/diff.js +805 -0
- package/dist/lib/object/diff.mjs +1 -0
- package/dist/lib/object/getter-setter.cjs +1 -0
- package/dist/lib/object/getter-setter.d.ts +361 -0
- package/dist/lib/object/getter-setter.d.ts.map +1 -0
- package/dist/lib/object/getter-setter.js +1771 -0
- package/dist/lib/object/getter-setter.mjs +1 -0
- package/dist/lib/object/index.cjs +6 -0
- package/dist/lib/object/index.d.ts +13 -0
- package/dist/lib/object/index.d.ts.map +1 -0
- package/dist/lib/object/index.js +12 -0
- package/dist/lib/object/index.mjs +6 -0
- package/dist/lib/object/merge.cjs +1 -0
- package/dist/lib/object/merge.d.ts +93 -0
- package/dist/lib/object/merge.d.ts.map +1 -0
- package/dist/lib/object/merge.js +474 -0
- package/dist/lib/object/merge.mjs +1 -0
- package/dist/lib/object/object.cjs +1 -0
- package/dist/lib/object/object.d.ts +128 -0
- package/dist/lib/object/object.d.ts.map +1 -0
- package/dist/lib/object/object.js +432 -0
- package/dist/lib/object/object.mjs +1 -0
- package/dist/lib/object/omit.cjs +1 -0
- package/dist/lib/object/omit.d.ts +351 -0
- package/dist/lib/object/omit.d.ts.map +1 -0
- package/dist/lib/object/omit.js +656 -0
- package/dist/lib/object/omit.mjs +1 -0
- package/dist/lib/object/reconcile.cjs +1 -0
- package/dist/lib/object/reconcile.d.ts +7 -0
- package/dist/lib/object/reconcile.d.ts.map +1 -0
- package/dist/lib/object/reconcile.js +122 -0
- package/dist/lib/object/reconcile.mjs +1 -0
- package/dist/lib/object/selector.cjs +1 -0
- package/dist/lib/object/selector.d.ts +50 -0
- package/dist/lib/object/selector.d.ts.map +1 -0
- package/dist/lib/object/selector.js +371 -0
- package/dist/lib/object/selector.mjs +1 -0
- package/dist/lib/object/sort.cjs +1 -0
- package/dist/lib/object/sort.d.ts +149 -0
- package/dist/lib/object/sort.d.ts.map +1 -0
- package/dist/lib/object/sort.js +298 -0
- package/dist/lib/object/sort.mjs +1 -0
- package/dist/lib/object/transform.cjs +1 -0
- package/dist/lib/object/transform.d.ts +620 -0
- package/dist/lib/object/transform.d.ts.map +1 -0
- package/dist/lib/object/transform.js +1058 -0
- package/dist/lib/object/transform.mjs +1 -0
- package/dist/lib/object/types.cjs +1 -0
- package/dist/lib/object/types.d.ts +250 -0
- package/dist/lib/object/types.d.ts.map +1 -0
- package/dist/lib/object/types.js +1 -0
- package/dist/lib/object/types.mjs +0 -0
- package/dist/lib/object/utils.cjs +6 -0
- package/dist/lib/object/utils.d.ts +165 -0
- package/dist/lib/object/utils.d.ts.map +1 -0
- package/dist/lib/object/utils.js +444 -0
- package/dist/lib/object/utils.mjs +6 -0
- package/dist/lib/primitive/boolean/boolean.cjs +1 -0
- package/dist/lib/primitive/boolean/boolean.d.ts +276 -0
- package/dist/lib/primitive/boolean/boolean.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/boolean.js +615 -0
- package/dist/lib/primitive/boolean/boolean.mjs +1 -0
- package/dist/lib/primitive/boolean/index.cjs +1 -0
- package/dist/lib/primitive/boolean/index.d.ts +3 -0
- package/dist/lib/primitive/boolean/index.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/index.js +2 -0
- package/dist/lib/primitive/boolean/index.mjs +1 -0
- package/dist/lib/primitive/boolean/types.cjs +1 -0
- package/dist/lib/primitive/boolean/types.d.ts +14 -0
- package/dist/lib/primitive/boolean/types.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/types.js +1 -0
- package/dist/lib/primitive/boolean/types.mjs +0 -0
- package/dist/lib/primitive/date/date.cjs +1 -0
- package/dist/lib/primitive/date/date.d.ts +641 -0
- package/dist/lib/primitive/date/date.d.ts.map +1 -0
- package/dist/lib/primitive/date/date.js +1998 -0
- package/dist/lib/primitive/date/date.mjs +1 -0
- package/dist/lib/primitive/date/index.cjs +1 -0
- package/dist/lib/primitive/date/index.d.ts +3 -0
- package/dist/lib/primitive/date/index.d.ts.map +1 -0
- package/dist/lib/primitive/date/index.js +2 -0
- package/dist/lib/primitive/date/index.mjs +1 -0
- package/dist/lib/primitive/date/types.cjs +1 -0
- package/dist/lib/primitive/date/types.d.ts +205 -0
- package/dist/lib/primitive/date/types.d.ts.map +1 -0
- package/dist/lib/primitive/date/types.js +7 -0
- package/dist/lib/primitive/date/types.mjs +0 -0
- package/dist/lib/primitive/index.cjs +5 -0
- package/dist/lib/primitive/index.d.ts +5 -0
- package/dist/lib/primitive/index.d.ts.map +1 -0
- package/dist/lib/primitive/index.js +4 -0
- package/dist/lib/primitive/index.mjs +5 -0
- package/dist/lib/primitive/string/index.cjs +5 -0
- package/dist/lib/primitive/string/index.d.ts +3 -0
- package/dist/lib/primitive/string/index.d.ts.map +1 -0
- package/dist/lib/primitive/string/index.js +2 -0
- package/dist/lib/primitive/string/index.mjs +5 -0
- package/dist/lib/primitive/string/parser-2.cjs +8 -0
- package/dist/lib/primitive/string/parser-2.d.ts +17 -0
- package/dist/lib/primitive/string/parser-2.d.ts.map +1 -0
- package/dist/lib/primitive/string/parser-2.js +656 -0
- package/dist/lib/primitive/string/parser-2.mjs +8 -0
- package/dist/lib/primitive/string/parser.cjs +3 -0
- package/dist/lib/primitive/string/parser.d.ts +9 -0
- package/dist/lib/primitive/string/parser.d.ts.map +1 -0
- package/dist/lib/primitive/string/parser.js +775 -0
- package/dist/lib/primitive/string/parser.mjs +3 -0
- package/dist/lib/primitive/string/string.cjs +5 -0
- package/dist/lib/primitive/string/string.d.ts +535 -0
- package/dist/lib/primitive/string/string.d.ts.map +1 -0
- package/dist/lib/primitive/string/string.js +1301 -0
- package/dist/lib/primitive/string/string.mjs +5 -0
- package/dist/lib/primitive/string/types.cjs +1 -0
- package/dist/lib/primitive/string/types.d.ts +318 -0
- package/dist/lib/primitive/string/types.d.ts.map +1 -0
- package/dist/lib/primitive/string/types.js +5 -0
- package/dist/lib/primitive/string/types.mjs +0 -0
- package/dist/lib/primitive/types.cjs +1 -0
- package/dist/lib/primitive/types.d.ts +27 -0
- package/dist/lib/primitive/types.d.ts.map +1 -0
- package/dist/lib/primitive/types.js +1 -0
- package/dist/lib/primitive/types.mjs +0 -0
- package/dist/lib/url/index.cjs +1 -0
- package/dist/lib/url/index.d.ts +3 -0
- package/dist/lib/url/index.d.ts.map +1 -0
- package/dist/lib/url/index.js +2 -0
- package/dist/lib/url/index.mjs +1 -0
- package/dist/lib/url/parser.cjs +1 -0
- package/dist/lib/url/parser.d.ts +216 -0
- package/dist/lib/url/parser.d.ts.map +1 -0
- package/dist/lib/url/parser.js +488 -0
- package/dist/lib/url/parser.mjs +1 -0
- package/dist/lib/url/types.cjs +1 -0
- package/dist/lib/url/types.d.ts +18 -0
- package/dist/lib/url/types.d.ts.map +1 -0
- package/dist/lib/url/types.js +1 -0
- package/dist/lib/url/types.mjs +0 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +150 -0
|
@@ -0,0 +1,1771 @@
|
|
|
1
|
+
import { isPrimitive } from '../helpers.js';
|
|
2
|
+
import { deepClone } from './clone.js';
|
|
3
|
+
import { diffObjectDetailed, applyDiff } from './diff.js';
|
|
4
|
+
import { mergeIntoStructure } from './merge.js';
|
|
5
|
+
import { parseSelector, stringifySelector } from './selector.js';
|
|
6
|
+
import { isObject, traverseStructure } from './utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Gets values from an object while preserving structure
|
|
9
|
+
* @param obj - Source object
|
|
10
|
+
* @param selector - Selector string or array
|
|
11
|
+
* @param options - Selector options
|
|
12
|
+
* @returns Structured object with selected paths
|
|
13
|
+
* @example
|
|
14
|
+
* get({users: [{name: 'Alice'}, {name: 'Bob'}]}, 'users[*].name')
|
|
15
|
+
* // Returns: {users: [{name: 'Alice'}, {name: 'Bob'}]}
|
|
16
|
+
*
|
|
17
|
+
* get({a: {b: 1}, c: {b: 2}}, ['a.b', 'c.b'])
|
|
18
|
+
* // Returns: {a: {b: 1}, c: {b: 2}}
|
|
19
|
+
*/
|
|
20
|
+
export function get(obj, selector, options = {}) {
|
|
21
|
+
const { preserveStructure, multiple, throwOnError = false, defaultValue, maxDepth = 20, } = options;
|
|
22
|
+
if (!obj || typeof obj !== 'object') {
|
|
23
|
+
return defaultValue;
|
|
24
|
+
}
|
|
25
|
+
// If no selector provided, return the object
|
|
26
|
+
if (!selector) {
|
|
27
|
+
return obj;
|
|
28
|
+
}
|
|
29
|
+
// console.log('try to get', selector)
|
|
30
|
+
// Handle array of selectors
|
|
31
|
+
if (Array.isArray(selector)) {
|
|
32
|
+
if (preserveStructure) {
|
|
33
|
+
// Return an object with all selected paths
|
|
34
|
+
const result = {};
|
|
35
|
+
for (const sel of selector) {
|
|
36
|
+
const value = get(obj, sel, { ...options, preserveStructure: false });
|
|
37
|
+
if (value !== undefined) {
|
|
38
|
+
// Merge the value into result preserving structure
|
|
39
|
+
mergeIntoStructure(result, sel, value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// Return array of values
|
|
46
|
+
return selector.map((sel) => get(obj, sel, { ...options, preserveStructure: false }));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const segments = parseSelector(selector, { ...options, throwOnError });
|
|
50
|
+
// console.log('segments', segments)
|
|
51
|
+
if (segments.length === 0) {
|
|
52
|
+
return obj;
|
|
53
|
+
}
|
|
54
|
+
if (typeof preserveStructure === 'undefined' || preserveStructure === false) {
|
|
55
|
+
// console.log('p')
|
|
56
|
+
const v = ValueOfKey(obj, selector);
|
|
57
|
+
if (multiple === false && Array.isArray(v)) {
|
|
58
|
+
return v[0];
|
|
59
|
+
}
|
|
60
|
+
if (v === undefined && defaultValue !== undefined) {
|
|
61
|
+
return defaultValue;
|
|
62
|
+
}
|
|
63
|
+
return v;
|
|
64
|
+
}
|
|
65
|
+
// Get structured result
|
|
66
|
+
const result = traverseStructure(obj, segments, 0, maxDepth);
|
|
67
|
+
if (result === undefined && defaultValue !== undefined) {
|
|
68
|
+
return defaultValue;
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
// Implementation
|
|
73
|
+
export function set(_obj, first, second, third, fourth) {
|
|
74
|
+
// if (params.length === 0) return obj;
|
|
75
|
+
let obj = deepClone(_obj);
|
|
76
|
+
// console.log('set called', {obj, first, second, third, fourth})
|
|
77
|
+
const params = [];
|
|
78
|
+
if (typeof first !== 'undefined')
|
|
79
|
+
params.push(first);
|
|
80
|
+
if (typeof second !== 'undefined')
|
|
81
|
+
params.push(second);
|
|
82
|
+
if (typeof third !== 'undefined')
|
|
83
|
+
params.push(third);
|
|
84
|
+
if (typeof fourth !== 'undefined')
|
|
85
|
+
params.push(fourth);
|
|
86
|
+
if (isPrimitive(obj)) {
|
|
87
|
+
if (typeof first === 'function') {
|
|
88
|
+
return first(obj);
|
|
89
|
+
}
|
|
90
|
+
return first;
|
|
91
|
+
}
|
|
92
|
+
// const [first, second, third, fourth, fifth] = params;
|
|
93
|
+
// Pattern 1 & 2: set(obj, "path", value) or set(obj, "path", updateFunction)
|
|
94
|
+
// console.log('set v', first, second, params.length);
|
|
95
|
+
if (params.length === 1) {
|
|
96
|
+
if (typeof params[0] === 'function') {
|
|
97
|
+
// console.log('callback update', params, obj)
|
|
98
|
+
return updateWithCallBack(obj, params[0]);
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(params[0])) {
|
|
101
|
+
// console.log('is array', [...params[0]])
|
|
102
|
+
// if ([...params[0]].some((v) => v.title === 'S / Red / Nike')) throw new Error('stop')
|
|
103
|
+
if (Array.isArray(obj)) {
|
|
104
|
+
// obj = params[0];
|
|
105
|
+
if (first.length === 0) {
|
|
106
|
+
for (let i = 0; i < obj.length; i++) {
|
|
107
|
+
obj.pop();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
const diff = diffObjectDetailed([...obj], [...params[0]]);
|
|
112
|
+
if (diff.hasChanges) {
|
|
113
|
+
applyDiff(obj, [...params[0]], true);
|
|
114
|
+
}
|
|
115
|
+
return obj;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// updateWithCallBack(obj, (value) => );
|
|
119
|
+
}
|
|
120
|
+
else if (typeof params[0] === 'object' && !Array.isArray(params[0])) {
|
|
121
|
+
if (typeof obj === 'object' && !Array.isArray(obj)) {
|
|
122
|
+
applyDiff(obj, params[0], true);
|
|
123
|
+
}
|
|
124
|
+
// if (Array.isArray(obj)) {
|
|
125
|
+
// obj.push(params[0]);
|
|
126
|
+
// } else {
|
|
127
|
+
// obj = { ...obj, ...params[0] };
|
|
128
|
+
// }
|
|
129
|
+
// return updateWithCallBack(obj, (value) => ({ ...value, ...params[0] }));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
obj = params[0];
|
|
133
|
+
}
|
|
134
|
+
return obj;
|
|
135
|
+
// return updateWithCallBack(obj, (value) => params[0]);
|
|
136
|
+
}
|
|
137
|
+
if (Array.isArray(obj)) {
|
|
138
|
+
if (typeof first === 'string' &&
|
|
139
|
+
(isArrayOfString(second) || typeof second === 'string') &&
|
|
140
|
+
params.length === 3) {
|
|
141
|
+
if (Array.isArray(second)) {
|
|
142
|
+
for (let i = 0; i < second.length; i++) {
|
|
143
|
+
setValueAtPath(obj, first, third, second[i]);
|
|
144
|
+
}
|
|
145
|
+
return obj;
|
|
146
|
+
}
|
|
147
|
+
return setValueAtPath(obj, first, third, second);
|
|
148
|
+
}
|
|
149
|
+
if (typeof first === 'string' &&
|
|
150
|
+
typeof second === 'function' &&
|
|
151
|
+
params.length === 2) {
|
|
152
|
+
return setValueAtPath(obj, first, second);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// if (Array.isArray(obj) && typeof second === 'object') {
|
|
156
|
+
// console.log('items', obj, first, second);
|
|
157
|
+
// }
|
|
158
|
+
if ((typeof first === 'string' || typeof first === 'number') &&
|
|
159
|
+
(params.length === 2 || params.length === 3)) {
|
|
160
|
+
if (isRangeObject(second)) {
|
|
161
|
+
return setValueBetweenRange(obj, first, second, third);
|
|
162
|
+
}
|
|
163
|
+
if (typeof first === 'number') {
|
|
164
|
+
// console.log('setting v', {obj, first, second, third})
|
|
165
|
+
if (typeof third !== 'undefined') {
|
|
166
|
+
return setValueAtPath(obj, `[${first}].${second}`, third);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
return setValueAtPath(obj, `[${first}]`, second, third);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// console.log('len 1', params.length);
|
|
173
|
+
if (typeof second === 'number' && typeof third !== 'undefined') {
|
|
174
|
+
return setValueAtIndex(obj, first, second, third);
|
|
175
|
+
}
|
|
176
|
+
if (Array.isArray(second) &&
|
|
177
|
+
second.length > 0 &&
|
|
178
|
+
second.every((s) => typeof s === 'number')) {
|
|
179
|
+
return setValueWithIndices(obj, first, second, third);
|
|
180
|
+
}
|
|
181
|
+
if (typeof third !== 'undefined' && typeof second === 'function') {
|
|
182
|
+
return setValueWithFilter(obj, first, second, third);
|
|
183
|
+
}
|
|
184
|
+
return setValueAtPath(obj, first, second, third);
|
|
185
|
+
}
|
|
186
|
+
// Pattern 3 & 4: set(obj, "path", index, value) or set(obj, "path", index, subPath, value)
|
|
187
|
+
if (typeof first === 'string' && typeof second === 'number') {
|
|
188
|
+
// console.log('len 2', obj, params)
|
|
189
|
+
if (params.length === 3) {
|
|
190
|
+
return setValueAtIndex(obj, first, second, third);
|
|
191
|
+
}
|
|
192
|
+
else if (params.length === 4) {
|
|
193
|
+
return setIndexWithSubPath(obj, first, second, third, fourth);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Pattern 5 & 6: set(obj, "path", indices, value) or set(obj, "path", indices, subPath, value)
|
|
197
|
+
if (typeof first === 'string' && Array.isArray(second)) {
|
|
198
|
+
if (params.length === 3) {
|
|
199
|
+
return setValueWithIndices(obj, first, second, third);
|
|
200
|
+
}
|
|
201
|
+
else if (params.length === 4) {
|
|
202
|
+
return setValueWithIndicesWithSubPath(obj, first, second, third, fourth);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Pattern 7 & 8: set(obj, "path", range, value) or set(obj, "path", range, subPath, value)
|
|
206
|
+
if (typeof first === 'string' &&
|
|
207
|
+
typeof second === 'object' &&
|
|
208
|
+
!Array.isArray(second) &&
|
|
209
|
+
second !== null) {
|
|
210
|
+
const range = second;
|
|
211
|
+
if ('from' in range || 'to' in range) {
|
|
212
|
+
if (params.length === 3) {
|
|
213
|
+
return setValueBetweenRange(obj, first, range, third);
|
|
214
|
+
}
|
|
215
|
+
else if (params.length === 4) {
|
|
216
|
+
return setValueBetweenRangeWithSubPath(obj, first, range, third, fourth);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Pattern 9: set(obj, updateObject)
|
|
221
|
+
if (typeof first === 'object' &&
|
|
222
|
+
!Array.isArray(first) &&
|
|
223
|
+
params.length === 1) {
|
|
224
|
+
return applyDiff(obj, first, true);
|
|
225
|
+
}
|
|
226
|
+
// Pattern 10: set(obj, "path", updateObject)
|
|
227
|
+
if (typeof first === 'string' &&
|
|
228
|
+
typeof second === 'object' &&
|
|
229
|
+
!Array.isArray(second) &&
|
|
230
|
+
params.length === 2) {
|
|
231
|
+
return mergeUpdateAtPath(obj, first, second);
|
|
232
|
+
}
|
|
233
|
+
// console.log('we here', params.length)
|
|
234
|
+
// Pattern 11 & 12: set(obj, "path", filter, value) or set(obj, "path", filter, subPath, value)
|
|
235
|
+
if (typeof first === 'string' && typeof second === 'function') {
|
|
236
|
+
if (params.length === 3) {
|
|
237
|
+
// console.log('set filter')
|
|
238
|
+
return setValueWithFilter(obj, first, second, third);
|
|
239
|
+
}
|
|
240
|
+
else if (params.length === 4) {
|
|
241
|
+
return setValueWithFilterWithSubPath(obj, first, second, third, fourth);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (typeof first === 'function' && typeof second === 'string') {
|
|
245
|
+
if (params.length === 3) {
|
|
246
|
+
// console.log('set filter')
|
|
247
|
+
return setArrayValueWithFilter(obj, first, second, third);
|
|
248
|
+
}
|
|
249
|
+
else if (params.length === 4) {
|
|
250
|
+
return setValueWithFilterWithSubPath(obj, second, first, third, fourth);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// Pattern 13: Array selectors
|
|
254
|
+
// console.log('last pattern', params, _obj)
|
|
255
|
+
if (Array.isArray(first)) {
|
|
256
|
+
if (params.length === 2 &&
|
|
257
|
+
typeof second === 'object' &&
|
|
258
|
+
!Array.isArray(second)) {
|
|
259
|
+
// Array of selectors with update object
|
|
260
|
+
return setValueWithArraySelectors(obj, first, second);
|
|
261
|
+
}
|
|
262
|
+
else if (params.length === 2) {
|
|
263
|
+
// Array of selectors with single value
|
|
264
|
+
return setValueWithArraySelectors(obj, first, second);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
throw new Error(`Invalid set parameters: ${JSON.stringify(params)}`);
|
|
268
|
+
}
|
|
269
|
+
function handleFunctionParams(selector, data, value, isArray = false) {
|
|
270
|
+
const paths = parseSelector(selector);
|
|
271
|
+
// console.log('p', { paths, selector, res: paths[paths.length - 1] });
|
|
272
|
+
if (paths.length === 0)
|
|
273
|
+
return value;
|
|
274
|
+
const isWildcard = paths[paths.length - 1].type === 'wildcard';
|
|
275
|
+
const containWildcard = paths.some((path) => path.type === 'wildcard') && Array.isArray(data);
|
|
276
|
+
let finalValue;
|
|
277
|
+
if (isWildcard ||
|
|
278
|
+
(containWildcard && isArray) ||
|
|
279
|
+
(containWildcard && paths.length === 2)) {
|
|
280
|
+
if (Array.isArray(data)) {
|
|
281
|
+
for (let i = 0; i < data.length; i++) {
|
|
282
|
+
const v = typeof value === 'function'
|
|
283
|
+
? value(data[i])
|
|
284
|
+
: typeof data[i] === 'object'
|
|
285
|
+
? applyDiff(data[i], value, true)
|
|
286
|
+
: value;
|
|
287
|
+
if (!finalValue)
|
|
288
|
+
finalValue = [];
|
|
289
|
+
finalValue.push(v);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (!isWildcard && !finalValue) {
|
|
294
|
+
finalValue = typeof value === 'function' ? value(data) : value;
|
|
295
|
+
}
|
|
296
|
+
// if (selector === 'person.wife.childs') {
|
|
297
|
+
// console.log('final', { finalValue, data, value });
|
|
298
|
+
// }
|
|
299
|
+
return finalValue;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* utility function to check if an object is a range object
|
|
303
|
+
* @param obj - The Object to check
|
|
304
|
+
* @example
|
|
305
|
+
* isRangeObject({ from: 0, to: 10 }); // true
|
|
306
|
+
* isRangeObject({ from: 0, to: 10, by: 1 }); // true
|
|
307
|
+
* isRangeObject({ from: 0, to: 10, by: '1' }); // true
|
|
308
|
+
* isRangeObject({ by: 2 }); // false
|
|
309
|
+
*/
|
|
310
|
+
export function isRangeObject(obj) {
|
|
311
|
+
return (typeof obj === 'object' &&
|
|
312
|
+
obj !== null &&
|
|
313
|
+
'from' in obj &&
|
|
314
|
+
'to' in obj &&
|
|
315
|
+
typeof obj.from === 'number' &&
|
|
316
|
+
typeof obj.to === 'number' &&
|
|
317
|
+
obj.from < obj.to &&
|
|
318
|
+
obj.from >= 0 &&
|
|
319
|
+
obj.to > 0);
|
|
320
|
+
}
|
|
321
|
+
function isArrayOfString(value) {
|
|
322
|
+
return (Array.isArray(value) && value.every((item) => typeof item === 'string'));
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Sets a value at a path
|
|
326
|
+
* @param obj - The Object to set the value at
|
|
327
|
+
* @param selector - The path to set the value at
|
|
328
|
+
* @param value - The value to set
|
|
329
|
+
* @param subPath - The subpath to set the value at
|
|
330
|
+
* @returns The updated object
|
|
331
|
+
* @example
|
|
332
|
+
* setValueAtPath({ a: { b: { c: 1 } } }, 'a.b.c', 2); // { a: { b: { c: 2 } } }
|
|
333
|
+
* setValueAtPath({ a: { b: { c: 1 } } }, 'a.b.c', (prev) => prev + 1); // { a: { b: { c: 2 } } }
|
|
334
|
+
* setValueAtPath({ a: { b: { c: 1 } } }, 'a.b.c', 2, 'd'); // { a: { b: { c: { d: 2 } } } }
|
|
335
|
+
* setValueAtPath({ a: { b: { c: 1 } } }, 'a.b.c', (prev) => prev + 1, 'd'); // { a: { b: { c: { d: 2 } } } }
|
|
336
|
+
*/
|
|
337
|
+
export function setValueAtPath(obj, selector, value, subPath) {
|
|
338
|
+
// console.log('set value', selector);
|
|
339
|
+
const paths = parseSelector(selector);
|
|
340
|
+
let target = ValueOfKey(obj, selector, {
|
|
341
|
+
paths,
|
|
342
|
+
createMissing: true,
|
|
343
|
+
});
|
|
344
|
+
// if (selector === 'variantRows') {
|
|
345
|
+
// console.log('set at p', target, value);
|
|
346
|
+
// }
|
|
347
|
+
let get_value;
|
|
348
|
+
if (subPath) {
|
|
349
|
+
if (typeof value === 'function') {
|
|
350
|
+
get_value = get(target, subPath);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const data = subPath ? get_value : target;
|
|
354
|
+
const finalValue = handleFunctionParams(selector, data, value, Array.isArray(obj));
|
|
355
|
+
// console.log('set value at path', {data, finalValue, selector, obj});
|
|
356
|
+
// if (selector === '[*]') {
|
|
357
|
+
// console.log('finalValue', finalValue, selector);
|
|
358
|
+
// }
|
|
359
|
+
if (subPath) {
|
|
360
|
+
const result = setAtPath(obj, `${selector}.${subPath}`, finalValue, {
|
|
361
|
+
createMissing: true,
|
|
362
|
+
});
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
const r = setAtPath(obj, selector, finalValue, { createMissing: true });
|
|
367
|
+
// if (selector === '[*]') {
|
|
368
|
+
// // console.log('r', r);
|
|
369
|
+
// // console.log('r', obj);
|
|
370
|
+
// }
|
|
371
|
+
// console.log('r', obj);
|
|
372
|
+
return r;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Sets value at index
|
|
377
|
+
* @param obj - The Object to set the value at
|
|
378
|
+
* @param selector - The path to set the value at
|
|
379
|
+
* @param index - The index to set the value at
|
|
380
|
+
* @param value - The value to set
|
|
381
|
+
* @returns The updated object
|
|
382
|
+
* @example
|
|
383
|
+
* setValueAtIndex({ a: { b: [1, 2, 3] } }, 'a.b', 1, 4); // { a: { b: [1, 4, 3] } }
|
|
384
|
+
* setValueAtIndex({ a: { b: [1, 2, 3] } }, 'a.b', 1, (prev) => prev + 1); // { a: { b: [1, 3, 3] } }
|
|
385
|
+
* setValueAtIndex({ a: { b: [1, 2, 3] } }, 'a.b', 1, (prev) => prev + 1); // { a: { b: [1, 3, 3] } }
|
|
386
|
+
*/
|
|
387
|
+
export function setValueAtIndex(obj, selector, index, value) {
|
|
388
|
+
const target = ValueOfKey(obj, selector, { createMissing: true });
|
|
389
|
+
if (!Array.isArray(target)) {
|
|
390
|
+
throw new Error(`Target at ${selector} is not an array ${index}`);
|
|
391
|
+
}
|
|
392
|
+
const target_copy = structuredClone(target);
|
|
393
|
+
const finalValue = handleFunctionParams(selector, target_copy[index], value);
|
|
394
|
+
// console.log('set value at index', {obj, selector, index, finalValue, value, target_copy});
|
|
395
|
+
if (index < 0) {
|
|
396
|
+
// Negative index counts from end
|
|
397
|
+
const actualIndex = target.length + index;
|
|
398
|
+
if (actualIndex < 0) {
|
|
399
|
+
throw new Error(`Index ${index} is out of bounds`);
|
|
400
|
+
}
|
|
401
|
+
return setAtPath(obj, `${selector}[${actualIndex}]`, finalValue, {
|
|
402
|
+
createMissing: true,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
// console.log('set index', index, `${selector}[${index}]`)
|
|
406
|
+
return setAtPath(obj, `${selector}[${index}]`, finalValue, {
|
|
407
|
+
createMissing: true,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Sets value at index with sub-path
|
|
412
|
+
* @param obj - The object to set the value on
|
|
413
|
+
* @param selector - The selector to set the value at
|
|
414
|
+
* @param index - The index to set the value at
|
|
415
|
+
* @param subPath - The sub-path to set the value at
|
|
416
|
+
* @param value - The value to set
|
|
417
|
+
* @example
|
|
418
|
+
* const obj = { a: [1, 2, 3] };
|
|
419
|
+
* setIndexWithSubPath(obj, 'a', 1, 'b', 4);
|
|
420
|
+
* console.log(obj); // { a: [1, { b: 4 }, 3] }
|
|
421
|
+
*/
|
|
422
|
+
const setIndexWithSubPath = (obj, selector, index, subPath, value) => {
|
|
423
|
+
// console.log('sub is fn', {obj, selector, index, subPath, value});
|
|
424
|
+
if (typeof subPath === 'function') {
|
|
425
|
+
return setValueAtPath(obj, `${selector}[${index}]`, subPath);
|
|
426
|
+
}
|
|
427
|
+
if (typeof subPath === 'string') {
|
|
428
|
+
return setValueAtPath(obj, `${selector}[${index}]`, value, subPath);
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
};
|
|
432
|
+
/**
|
|
433
|
+
* Sets value at multiple indices
|
|
434
|
+
* @param obj - The object to set the value on
|
|
435
|
+
* @param selector - The selector to set the value at
|
|
436
|
+
* @param indices - The indices to set the value at
|
|
437
|
+
* @param value - The value to set
|
|
438
|
+
* @example
|
|
439
|
+
* const obj = { a: [1, 2, 3] };
|
|
440
|
+
* setValueWithIndices(obj, 'a', [1, 2], 4);
|
|
441
|
+
* console.log(obj); // { a: [1, 4, 4] }
|
|
442
|
+
*/
|
|
443
|
+
const setValueWithIndices = (obj, selector, indices, value) => {
|
|
444
|
+
const target = ValueOfKey(obj, selector);
|
|
445
|
+
// console.log('setting index', target)
|
|
446
|
+
if (!Array.isArray(target)) {
|
|
447
|
+
throw new Error(`Target at ${selector} is not an array`);
|
|
448
|
+
}
|
|
449
|
+
for (const index of indices) {
|
|
450
|
+
if (typeof index === 'number') {
|
|
451
|
+
setValueAtIndex(obj, selector, index, value);
|
|
452
|
+
}
|
|
453
|
+
else if (typeof index === 'string') {
|
|
454
|
+
setValueAtPath(obj, selector, value, index);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return obj;
|
|
458
|
+
};
|
|
459
|
+
/**
|
|
460
|
+
* Sets value at multiple indices with sub-path
|
|
461
|
+
* @param obj - The object to set the value on
|
|
462
|
+
* @param selector - The selector to set the value at
|
|
463
|
+
* @param indices - The indices to set the value at
|
|
464
|
+
* @param subPath - The sub-path to set the value at
|
|
465
|
+
* @param value - The value to set
|
|
466
|
+
* @example
|
|
467
|
+
* const obj = { a: [1, 2, 3] };
|
|
468
|
+
* setValueWithIndicesWithSubPath(obj, 'a', [1, 2], 'b', 4);
|
|
469
|
+
* console.log(obj); // { a: [1, { b: 4 }, { b: 4 }] }
|
|
470
|
+
*/
|
|
471
|
+
const setValueWithIndicesWithSubPath = (obj, selector, indices, subPath, value) => {
|
|
472
|
+
const updater = (prev) => {
|
|
473
|
+
const newValue = typeof value === 'function'
|
|
474
|
+
? value(prev ? ValueOfKey(prev, subPath) : undefined)
|
|
475
|
+
: value;
|
|
476
|
+
const result = prev ? { ...prev } : {};
|
|
477
|
+
setAtPath(result, subPath, newValue);
|
|
478
|
+
return result;
|
|
479
|
+
};
|
|
480
|
+
return setValueWithIndices(obj, selector, indices, updater);
|
|
481
|
+
};
|
|
482
|
+
/**
|
|
483
|
+
* Sets value in a range
|
|
484
|
+
* @param obj - The object to set the value on
|
|
485
|
+
* @param selector - The selector to set the value at
|
|
486
|
+
* @param range - The range to set the value in
|
|
487
|
+
* @param value - The value to set
|
|
488
|
+
* @example
|
|
489
|
+
* const obj = { a: [1, 2, 3] };
|
|
490
|
+
* setValueBetweenRange(obj, 'a', { from: 1, to: 2 }, 4);
|
|
491
|
+
* console.log(obj); // { a: [1, 4, 4] }
|
|
492
|
+
*/
|
|
493
|
+
export function setValueBetweenRange(obj, selector, range, value) {
|
|
494
|
+
const target = ValueOfKey(obj, selector);
|
|
495
|
+
if (!Array.isArray(target)) {
|
|
496
|
+
throw new Error(`Target at ${selector} is not an array`);
|
|
497
|
+
}
|
|
498
|
+
const from = range.from ?? 0;
|
|
499
|
+
const to = range.to ?? target.length - 1;
|
|
500
|
+
const by = range.by ?? 1;
|
|
501
|
+
const indices = [];
|
|
502
|
+
for (let i = from; i <= to; i += by) {
|
|
503
|
+
if (i >= 0 && i < target.length) {
|
|
504
|
+
indices.push(i);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return setValueWithIndices(obj, selector, indices, value);
|
|
508
|
+
}
|
|
509
|
+
function getValueFnParams(prev, subPath) {
|
|
510
|
+
return prev ? ValueOfKey(prev, subPath) : undefined;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Sets value in a range with sub-path
|
|
514
|
+
* @param obj - The object to set the value on
|
|
515
|
+
* @param selector - The selector to set the value at
|
|
516
|
+
* @param range - The range to set the value in
|
|
517
|
+
* @param subPath - The sub-path to set the value at
|
|
518
|
+
* @param value - The value to set
|
|
519
|
+
* @example
|
|
520
|
+
* const obj = { a: [1, 2, 3] };
|
|
521
|
+
* setValueBetweenRangeWithSubPath(obj, 'a', { from: 1, to: 2 }, 'b', 4);
|
|
522
|
+
* console.log(obj); // { a: [1, { b: 4 }, { b: 4 }] }
|
|
523
|
+
*/
|
|
524
|
+
export const setValueBetweenRangeWithSubPath = (obj, selector, range, subPath, value) => {
|
|
525
|
+
const updater = (prev) => {
|
|
526
|
+
const newValue = typeof value === 'function'
|
|
527
|
+
? value(prev ? ValueOfKey(prev, subPath) : undefined)
|
|
528
|
+
: value;
|
|
529
|
+
const result = prev ? { ...prev } : {};
|
|
530
|
+
setAtPath(result, subPath, newValue);
|
|
531
|
+
return result;
|
|
532
|
+
};
|
|
533
|
+
return setValueBetweenRange(obj, selector, range, updater);
|
|
534
|
+
};
|
|
535
|
+
/**
|
|
536
|
+
* Casts properties to a given type
|
|
537
|
+
*/
|
|
538
|
+
export function castProps(arr, props) {
|
|
539
|
+
return Object.assign(arr, props);
|
|
540
|
+
// return arr as T;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Sets value for filtered items
|
|
544
|
+
* @param obj - The object to modify.
|
|
545
|
+
* @param selector - The selector for the property to modify.
|
|
546
|
+
* @param filterFn - The filter function to apply to the items.
|
|
547
|
+
* @param value - The value to set for the filtered items.
|
|
548
|
+
* @example
|
|
549
|
+
* const arr = [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }];
|
|
550
|
+
* setValueWithFilter(arr, 'id', id => id === 1, { name: 'John Doe' });
|
|
551
|
+
* console.log(arr); // [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane' }]
|
|
552
|
+
*/
|
|
553
|
+
export function setValueWithFilter(obj, selector, filterFn, value) {
|
|
554
|
+
const target = ValueOfKey(obj, selector);
|
|
555
|
+
if (!Array.isArray(target)) {
|
|
556
|
+
throw new Error(`Target at ${selector} is not an array`);
|
|
557
|
+
}
|
|
558
|
+
const indices = [];
|
|
559
|
+
const target_copy = [...target];
|
|
560
|
+
target.forEach((item, index) => {
|
|
561
|
+
if (filterFn(item, index)) {
|
|
562
|
+
// console.log('set filter for done', index)
|
|
563
|
+
indices.push(index);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
return setValueWithIndices(obj, selector, indices, value);
|
|
567
|
+
}
|
|
568
|
+
export function setArrayValueWithFilter(obj, filterFn, selector, value) {
|
|
569
|
+
let target = obj;
|
|
570
|
+
// const target = ValueOfKey(obj, selector);
|
|
571
|
+
if (!Array.isArray(target)) {
|
|
572
|
+
throw new Error(`Target at ${selector} is not an array`);
|
|
573
|
+
}
|
|
574
|
+
// return setValueAtPath(obj, selector, value);
|
|
575
|
+
// console.log('updating', selector, value, target)
|
|
576
|
+
const indices = [];
|
|
577
|
+
const target_copy = [...target];
|
|
578
|
+
target.forEach((item, index) => {
|
|
579
|
+
const n = setAtPath(item, selector, value, { createMissing: true });
|
|
580
|
+
item = n;
|
|
581
|
+
// if (filterFn(item, index)) {
|
|
582
|
+
// // console.log('set filter for done', index)
|
|
583
|
+
// indices.push(index);
|
|
584
|
+
// }
|
|
585
|
+
});
|
|
586
|
+
return target;
|
|
587
|
+
// return setValueWithIndices(obj, selector, indices, value);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Sets value for filtered items with sub-path
|
|
591
|
+
* @param obj - The object to modify.
|
|
592
|
+
* @param selector - The selector for the property to modify.
|
|
593
|
+
* @param filterFn - The filter function to apply to the items.
|
|
594
|
+
* @param subPath - The sub-path to modify.
|
|
595
|
+
* @param value - The value to set for the filtered items.
|
|
596
|
+
* @example
|
|
597
|
+
* const arr = [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }];
|
|
598
|
+
* setValueWithFilterWithSubPath(arr, 'id', id => id === 1, 'name', 'John Doe');
|
|
599
|
+
* console.log(arr); // [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane' }]
|
|
600
|
+
*/
|
|
601
|
+
export function setValueWithFilterWithSubPath(obj, selector, filterFn, subPath, value) {
|
|
602
|
+
const updater = (prev) => {
|
|
603
|
+
const newValue = typeof value === 'function'
|
|
604
|
+
? value(prev ? ValueOfKey(prev, subPath) : undefined)
|
|
605
|
+
: value;
|
|
606
|
+
const result = prev ? { ...prev } : {};
|
|
607
|
+
setAtPath(result, subPath, newValue);
|
|
608
|
+
return result;
|
|
609
|
+
};
|
|
610
|
+
return setValueWithFilter(obj, selector, filterFn, updater);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Merge update object at path
|
|
614
|
+
* @param obj - The object to update
|
|
615
|
+
* @param selector - The path to the object to update
|
|
616
|
+
* @param updates - The object to merge with the existing object
|
|
617
|
+
* @example
|
|
618
|
+
* const obj = { a: { b: { c: 1 } } };
|
|
619
|
+
* mergeUpdateAtPath(obj, 'a.b', { c: 2 });
|
|
620
|
+
* console.log(obj); // { a: { b: { c: 2 } } }
|
|
621
|
+
*/
|
|
622
|
+
export function mergeUpdateAtPath(obj, selector, updates) {
|
|
623
|
+
const current = ValueOfKey(obj, selector, { createMissing: true });
|
|
624
|
+
const merged = applyDiff(current || {}, updates, true);
|
|
625
|
+
return setAtPath(obj, selector, merged, { createMissing: true });
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Set with array selectors
|
|
629
|
+
* @param obj - The object to update
|
|
630
|
+
* @param selectors - The array of paths to update
|
|
631
|
+
* @param value - The value to set for each path
|
|
632
|
+
* @example
|
|
633
|
+
* const obj = { a: { b: { c: 1 } } };
|
|
634
|
+
* setValueWithArraySelectors(obj, ['a.b', 'a.c'], { d: 2 });
|
|
635
|
+
* console.log(obj); // { a: { b: { d: 2 }, c: { d: 2 } } }
|
|
636
|
+
*/
|
|
637
|
+
export function setValueWithArraySelectors(obj, selectors, value) {
|
|
638
|
+
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
639
|
+
// Update object for multiple selectors
|
|
640
|
+
for (const [path, val] of Object.entries(value)) {
|
|
641
|
+
if (selectors.includes(path)) {
|
|
642
|
+
setAtPath(obj, path, val, { createMissing: true });
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
// Single value for all selectors
|
|
648
|
+
for (const selector of selectors) {
|
|
649
|
+
const finalValue = handleFunctionParams(selector, ValueOfKey(obj, selector), value);
|
|
650
|
+
setAtPath(obj, selector, finalValue, { createMissing: true });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return obj;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Gets value at path with createMissing option
|
|
657
|
+
* @param obj - The object to get the value from.
|
|
658
|
+
* @param selector - The path to the value.
|
|
659
|
+
* @param options - Options for the operation.
|
|
660
|
+
* @returns The value at the path.
|
|
661
|
+
* @example
|
|
662
|
+
* const a = { b: { c: 1 } };
|
|
663
|
+
* ValueOfKey(a, 'b.c', { createMissing: true });
|
|
664
|
+
* console.log(a); // 1
|
|
665
|
+
*/
|
|
666
|
+
export function ValueOfKey(obj, selector, options = {}) {
|
|
667
|
+
const { createMissing = false, add = false, debug = false } = options;
|
|
668
|
+
const segments = parseSelector(selector);
|
|
669
|
+
let current = obj;
|
|
670
|
+
let updateEvenIfNotlast = false;
|
|
671
|
+
let chained_property = '';
|
|
672
|
+
const stringify = selector;
|
|
673
|
+
if (typeof current === 'object' &&
|
|
674
|
+
!Array.isArray(current) &&
|
|
675
|
+
stringify.length > 0) {
|
|
676
|
+
if (current[stringify]) {
|
|
677
|
+
current = current[stringify];
|
|
678
|
+
return current;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
for (let i = 0; i < segments.length; i++) {
|
|
682
|
+
const segment = segments[i];
|
|
683
|
+
const isLast = segments.length > 1 ? i === segments.length - 1 : false;
|
|
684
|
+
if (current === null && !createMissing) {
|
|
685
|
+
return undefined;
|
|
686
|
+
}
|
|
687
|
+
const nextSegment = segments[i + 1];
|
|
688
|
+
switch (segment.type) {
|
|
689
|
+
case 'property':
|
|
690
|
+
if (current && typeof current === 'object') {
|
|
691
|
+
if (Array.isArray(current)) {
|
|
692
|
+
// Handle numeric property as array index
|
|
693
|
+
if (typeof segment.value === 'number') {
|
|
694
|
+
const index = segment.value;
|
|
695
|
+
if (createMissing && index >= current.length) {
|
|
696
|
+
for (let j = current.length; j <= index; j++) {
|
|
697
|
+
current.push(undefined);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (createMissing &&
|
|
701
|
+
(current[index] === null || current[index] === undefined) &&
|
|
702
|
+
(!isLast || updateEvenIfNotlast)) {
|
|
703
|
+
if (nextSegment) {
|
|
704
|
+
current[index] =
|
|
705
|
+
nextSegment.type === 'index' ||
|
|
706
|
+
nextSegment.type === 'slice' ||
|
|
707
|
+
nextSegment.type === 'union' ||
|
|
708
|
+
nextSegment.type === 'wildcard'
|
|
709
|
+
? []
|
|
710
|
+
: {};
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
current[index] = null;
|
|
714
|
+
}
|
|
715
|
+
updateEvenIfNotlast = false;
|
|
716
|
+
}
|
|
717
|
+
current = current[index];
|
|
718
|
+
if (nextSegment) {
|
|
719
|
+
updateEvenIfNotlast = true;
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
else if (add) {
|
|
724
|
+
current.push({
|
|
725
|
+
[String(segment.value)]: null,
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
current = current.map((item) => item[String(segment.value)]);
|
|
730
|
+
}
|
|
731
|
+
if (nextSegment) {
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
return filterDefined(current);
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
if (current[chained_property] &&
|
|
738
|
+
chained_property.split('.').length >= 2) {
|
|
739
|
+
current = current[chained_property];
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (createMissing &&
|
|
743
|
+
(current[String(segment.value)] === null ||
|
|
744
|
+
current[String(segment.value)] === undefined) &&
|
|
745
|
+
(!isLast || updateEvenIfNotlast)) {
|
|
746
|
+
if (nextSegment) {
|
|
747
|
+
current[String(segment.value)] =
|
|
748
|
+
nextSegment.type === 'index' ||
|
|
749
|
+
nextSegment.type === 'slice' ||
|
|
750
|
+
nextSegment.type === 'union' ||
|
|
751
|
+
nextSegment.type === 'wildcard'
|
|
752
|
+
? []
|
|
753
|
+
: {};
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
current[String(segment.value)] = null;
|
|
757
|
+
}
|
|
758
|
+
updateEvenIfNotlast = false;
|
|
759
|
+
current = current[String(segment.value)];
|
|
760
|
+
if (nextSegment) {
|
|
761
|
+
updateEvenIfNotlast = true;
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
// break;
|
|
765
|
+
}
|
|
766
|
+
if (!current) {
|
|
767
|
+
current = { [String(segment.value)]: null };
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
if (!current[String(segment.value)] && createMissing) {
|
|
771
|
+
current[String(segment.value)] = null;
|
|
772
|
+
}
|
|
773
|
+
// if (
|
|
774
|
+
// selector === 'person.wife.childs' &&
|
|
775
|
+
// segment.value === 'childs'
|
|
776
|
+
// ) {
|
|
777
|
+
// console.log('cp', { current, segment });
|
|
778
|
+
// }
|
|
779
|
+
current = current[String(segment.value)];
|
|
780
|
+
}
|
|
781
|
+
if (nextSegment) {
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
else if (createMissing) {
|
|
787
|
+
// const nextSegment = segments[i + 1];
|
|
788
|
+
current = {};
|
|
789
|
+
current[String(segment.value)] =
|
|
790
|
+
nextSegment.type === 'index' ||
|
|
791
|
+
nextSegment.type === 'slice' ||
|
|
792
|
+
nextSegment.type === 'union' ||
|
|
793
|
+
nextSegment.type === 'wildcard' ||
|
|
794
|
+
options.type === 'array'
|
|
795
|
+
? []
|
|
796
|
+
: {};
|
|
797
|
+
current = current[String(segment.value)];
|
|
798
|
+
}
|
|
799
|
+
else {
|
|
800
|
+
return undefined;
|
|
801
|
+
}
|
|
802
|
+
break;
|
|
803
|
+
case 'index':
|
|
804
|
+
case 'wildcard':
|
|
805
|
+
if (Array.isArray(current)) {
|
|
806
|
+
if (segment.type === 'index') {
|
|
807
|
+
const index = segment.value;
|
|
808
|
+
if (index < 0) {
|
|
809
|
+
current = current[current.length - Math.abs(index)];
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (createMissing &&
|
|
813
|
+
(index >= current.length ||
|
|
814
|
+
current[index] == null ||
|
|
815
|
+
current[index] == undefined) &&
|
|
816
|
+
!isLast) {
|
|
817
|
+
for (let j = current.length; j <= index; j++) {
|
|
818
|
+
current.push(undefined);
|
|
819
|
+
}
|
|
820
|
+
if (nextSegment) {
|
|
821
|
+
current[index] =
|
|
822
|
+
nextSegment.type === 'index' ||
|
|
823
|
+
nextSegment.type === 'slice' ||
|
|
824
|
+
nextSegment.type === 'union' ||
|
|
825
|
+
nextSegment.type === 'wildcard'
|
|
826
|
+
? []
|
|
827
|
+
: {};
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
current = current[index];
|
|
831
|
+
// if(!isLast){
|
|
832
|
+
// console.log('not')
|
|
833
|
+
// continue
|
|
834
|
+
// }
|
|
835
|
+
// console.log('index is', current, index, i, nextSegment)
|
|
836
|
+
// if(nextSegment){
|
|
837
|
+
// continue
|
|
838
|
+
// }
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
if (!isLast) {
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
else if (createMissing) {
|
|
847
|
+
const nextSegment = segments[i + 1];
|
|
848
|
+
current = [];
|
|
849
|
+
const index = segment.value;
|
|
850
|
+
for (let j = 0; j <= index; j++) {
|
|
851
|
+
current.push(undefined);
|
|
852
|
+
}
|
|
853
|
+
current[index] =
|
|
854
|
+
nextSegment.type === 'index' ||
|
|
855
|
+
nextSegment.type === 'slice' ||
|
|
856
|
+
nextSegment.type === 'union' ||
|
|
857
|
+
nextSegment.type === 'wildcard' ||
|
|
858
|
+
options.type === 'array'
|
|
859
|
+
? []
|
|
860
|
+
: {};
|
|
861
|
+
current = current[index];
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
if (segment.type === 'index' && typeof current === 'object') {
|
|
865
|
+
// Here we need to check if the current is an object and if potentially the segment value is key of the object
|
|
866
|
+
if (current && current[String(segment.value)] !== undefined) {
|
|
867
|
+
current = current[String(segment.value)];
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (typeof current === 'object') {
|
|
872
|
+
const values = Object.values(current);
|
|
873
|
+
current = values;
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
return undefined;
|
|
877
|
+
}
|
|
878
|
+
break;
|
|
879
|
+
default:
|
|
880
|
+
return undefined;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return filterDefined(current);
|
|
884
|
+
}
|
|
885
|
+
function filterDefined(obj) {
|
|
886
|
+
if (Array.isArray(obj)) {
|
|
887
|
+
return obj.filter((item) => typeof item !== 'undefined');
|
|
888
|
+
}
|
|
889
|
+
return obj;
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Sets value at path (internal)
|
|
893
|
+
*/
|
|
894
|
+
export const setAtPath = (obj, selector, value, options = {}) => {
|
|
895
|
+
if (selector === '[*]') {
|
|
896
|
+
return applyDiff(obj, value, true);
|
|
897
|
+
}
|
|
898
|
+
const { createMissing = true } = options;
|
|
899
|
+
const segments = parseSelector(selector);
|
|
900
|
+
if (segments.length === 0) {
|
|
901
|
+
return obj;
|
|
902
|
+
}
|
|
903
|
+
const lastSegment = segments[segments.length - 1];
|
|
904
|
+
let current = obj;
|
|
905
|
+
// Navigate to parent of last segment
|
|
906
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
907
|
+
// console.log('i', i)
|
|
908
|
+
const segment = segments[i];
|
|
909
|
+
if (current == null) {
|
|
910
|
+
if (!createMissing)
|
|
911
|
+
return obj;
|
|
912
|
+
throw new Error(`Cannot navigate through null/undefined at segment ${i}`);
|
|
913
|
+
}
|
|
914
|
+
const nextSegment = segments[i + 1];
|
|
915
|
+
// let shoul
|
|
916
|
+
const shouldBeArray = nextSegment.type === 'index' ||
|
|
917
|
+
nextSegment.type === 'slice' ||
|
|
918
|
+
nextSegment.type === 'union' ||
|
|
919
|
+
nextSegment.type === 'wildcard';
|
|
920
|
+
if (segment.type === 'property') {
|
|
921
|
+
// if (selector === '[*].meta.count') {
|
|
922
|
+
// console.log('on property first', { selector, current });
|
|
923
|
+
// }
|
|
924
|
+
// Handle numeric property on array as index
|
|
925
|
+
if (Array.isArray(current) && typeof typeof segment.value === 'number') {
|
|
926
|
+
const index = segment.value;
|
|
927
|
+
if (index < 0) {
|
|
928
|
+
throw new Error(`Negative index ${index} not allowed`);
|
|
929
|
+
}
|
|
930
|
+
if (index >= current.length && createMissing) {
|
|
931
|
+
for (let j = current.length; j <= index; j++) {
|
|
932
|
+
current.push(undefined);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if ((current[index] === null || current[index] === undefined) &&
|
|
936
|
+
createMissing) {
|
|
937
|
+
current[index] = shouldBeArray ? [] : {};
|
|
938
|
+
}
|
|
939
|
+
current = current[index];
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
if ((current[String(segment.value)] === null ||
|
|
943
|
+
current[String(segment.value)] === undefined) &&
|
|
944
|
+
createMissing) {
|
|
945
|
+
// console.log('not exist', String(segment.value))
|
|
946
|
+
current[String(segment.value)] = shouldBeArray ? [] : {};
|
|
947
|
+
}
|
|
948
|
+
if (typeof current === 'object' && !Array.isArray(current)) {
|
|
949
|
+
current = current[String(segment.value)];
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
// console.log('is obj', current, nextSegment)
|
|
953
|
+
// console.log('prop', current)
|
|
954
|
+
if (nextSegment) {
|
|
955
|
+
// console.log('next seg', nextSegment)
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
break;
|
|
959
|
+
}
|
|
960
|
+
else if (segment.type === 'index' || segment.type === 'wildcard') {
|
|
961
|
+
// console.log('ind', current)
|
|
962
|
+
// if (selector === '[*].meta.count') {
|
|
963
|
+
// console.log('on index or wildcard first', { selector, current });
|
|
964
|
+
// }
|
|
965
|
+
if (!Array.isArray(current)) {
|
|
966
|
+
if (!createMissing)
|
|
967
|
+
return obj;
|
|
968
|
+
current = [];
|
|
969
|
+
}
|
|
970
|
+
if (segment.type === 'index') {
|
|
971
|
+
const index = segment.value;
|
|
972
|
+
if (index < 0) {
|
|
973
|
+
throw new Error(`Negative index ${index} not allowed`);
|
|
974
|
+
}
|
|
975
|
+
// console.log('is index')
|
|
976
|
+
if (index >= current.length && createMissing) {
|
|
977
|
+
// console.log('creating new element')
|
|
978
|
+
for (let j = current.length; j <= index; j++) {
|
|
979
|
+
current.push(undefined);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
if ((current[index] === null || current[index] === undefined) &&
|
|
983
|
+
createMissing) {
|
|
984
|
+
current[index] = shouldBeArray ? [] : undefined;
|
|
985
|
+
}
|
|
986
|
+
current = current[index];
|
|
987
|
+
}
|
|
988
|
+
if (nextSegment) {
|
|
989
|
+
// console.log('next seg', nextSegment)
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
break;
|
|
993
|
+
}
|
|
994
|
+
else {
|
|
995
|
+
throw new Error(`Invalid segment type for set: ${segment.type}`);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
// if (selector === 'person.wife.childs') {
|
|
999
|
+
// console.log('is array data', { current, value, lastSegment });
|
|
1000
|
+
// }
|
|
1001
|
+
// Set the value
|
|
1002
|
+
switch (lastSegment.type) {
|
|
1003
|
+
case 'property':
|
|
1004
|
+
// Handle numeric property on array as index
|
|
1005
|
+
if (Array.isArray(current) && typeof lastSegment.value === 'number') {
|
|
1006
|
+
const index = lastSegment.value;
|
|
1007
|
+
if (index < 0) {
|
|
1008
|
+
throw new Error(`Negative index ${index} not allowed`);
|
|
1009
|
+
}
|
|
1010
|
+
if (index >= current.length) {
|
|
1011
|
+
for (let j = current.length; j <= index; j++) {
|
|
1012
|
+
current.push(undefined);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
if (Array.isArray(value)) {
|
|
1016
|
+
current[index] = applyDiff(structuredClone(current[index]), value[index], true);
|
|
1017
|
+
}
|
|
1018
|
+
else {
|
|
1019
|
+
current[index] = value;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
else {
|
|
1023
|
+
if (!current)
|
|
1024
|
+
current = {};
|
|
1025
|
+
if (Array.isArray(current)) {
|
|
1026
|
+
const paths = selector.split('.');
|
|
1027
|
+
let ob;
|
|
1028
|
+
if (paths.length >= 2) {
|
|
1029
|
+
paths.shift();
|
|
1030
|
+
// if (Array.isArray(value)) {
|
|
1031
|
+
// }else{
|
|
1032
|
+
// }
|
|
1033
|
+
// if (selector === 'person.wife.childs') {
|
|
1034
|
+
// console.log('is array data', { current, value });
|
|
1035
|
+
// }
|
|
1036
|
+
for (let i = 0; i < current.length; i++) {
|
|
1037
|
+
if (Array.isArray(value)) {
|
|
1038
|
+
ob = fromPath(paths.join('.'), value[i]);
|
|
1039
|
+
current[i] =
|
|
1040
|
+
typeof current[i] === 'object'
|
|
1041
|
+
? applyDiff(current[i], ob, true)
|
|
1042
|
+
: ob;
|
|
1043
|
+
}
|
|
1044
|
+
else {
|
|
1045
|
+
ob = fromPath(paths.join('.'), value);
|
|
1046
|
+
current[i] =
|
|
1047
|
+
typeof current[i] === 'object'
|
|
1048
|
+
? applyDiff(current[i], ob, true)
|
|
1049
|
+
: ob;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
ob = {
|
|
1055
|
+
[String(lastSegment.value)]: value,
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
else {
|
|
1060
|
+
// if (selector === 'person.wife.childs') {
|
|
1061
|
+
// console.log('on property', {
|
|
1062
|
+
// current,
|
|
1063
|
+
// lastSegment,
|
|
1064
|
+
// value,
|
|
1065
|
+
// key: String(lastSegment.value),
|
|
1066
|
+
// });
|
|
1067
|
+
// }
|
|
1068
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
1069
|
+
if (typeof current[String(lastSegment.value)] === 'undefined') {
|
|
1070
|
+
current[String(lastSegment.value)] = [];
|
|
1071
|
+
}
|
|
1072
|
+
else {
|
|
1073
|
+
current[String(lastSegment.value)] = [];
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
else {
|
|
1077
|
+
current[String(lastSegment.value)] = value;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
break;
|
|
1082
|
+
case 'index':
|
|
1083
|
+
case 'wildcard':
|
|
1084
|
+
if (!Array.isArray(current)) {
|
|
1085
|
+
throw new Error(`Cannot use index on non-array for final segment`);
|
|
1086
|
+
}
|
|
1087
|
+
// if (selector === '[*].meta.count') {
|
|
1088
|
+
// console.log('on index or Wildcard last', { selector, current });
|
|
1089
|
+
// }
|
|
1090
|
+
const index = lastSegment.value;
|
|
1091
|
+
if (lastSegment.type === 'index') {
|
|
1092
|
+
if (index < 0) {
|
|
1093
|
+
throw new Error(`Negative index ${index} not allowed`);
|
|
1094
|
+
}
|
|
1095
|
+
if (index >= current.length) {
|
|
1096
|
+
for (let j = current.length; j <= index; j++) {
|
|
1097
|
+
current.push(undefined);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
if (Array.isArray(value)) {
|
|
1101
|
+
if (typeof current[index] === typeof value[index]) {
|
|
1102
|
+
try {
|
|
1103
|
+
current[index] = applyDiff(deepClone(current[index]), value[index], true);
|
|
1104
|
+
}
|
|
1105
|
+
catch (e) {
|
|
1106
|
+
// console.log('expected on array', {
|
|
1107
|
+
// selector,
|
|
1108
|
+
// current,
|
|
1109
|
+
// value,
|
|
1110
|
+
// index,
|
|
1111
|
+
// });
|
|
1112
|
+
// console.log('current', current);
|
|
1113
|
+
throw Error('expected on array');
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
else {
|
|
1117
|
+
// current[index] = value[index];
|
|
1118
|
+
// setValueAtIndex(current, selector, index, value[index]);
|
|
1119
|
+
current[index] = value;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
else {
|
|
1123
|
+
current[index] = value;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
else if (lastSegment.type === 'wildcard') {
|
|
1127
|
+
if (Array.isArray(value)) {
|
|
1128
|
+
for (let i = 0; i < current.length; i++) {
|
|
1129
|
+
if (typeof current[i] === 'object') {
|
|
1130
|
+
current[i] = applyDiff(current[i], value[i], true);
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
current[i] = value[i];
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
else {
|
|
1138
|
+
for (let i = 0; i < current.length; i++) {
|
|
1139
|
+
if (typeof current[i] === 'object') {
|
|
1140
|
+
current[i] = applyDiff(current[i], value, true);
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
current[i] = value;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
break;
|
|
1149
|
+
default:
|
|
1150
|
+
throw new Error(`Invalid final segment type for set: ${lastSegment.type}`);
|
|
1151
|
+
}
|
|
1152
|
+
return obj;
|
|
1153
|
+
};
|
|
1154
|
+
const updateLastKey = (object, value) => {
|
|
1155
|
+
let obj = object ?? {};
|
|
1156
|
+
const keys = Object.keys(obj);
|
|
1157
|
+
let lastKey = keys[keys.length - 1];
|
|
1158
|
+
if (lastKey === undefined) {
|
|
1159
|
+
obj = value;
|
|
1160
|
+
return obj;
|
|
1161
|
+
}
|
|
1162
|
+
while (lastKey !== undefined) {
|
|
1163
|
+
const current = obj[lastKey];
|
|
1164
|
+
if (typeof current !== 'object' || Array.isArray(current)) {
|
|
1165
|
+
if (Array.isArray(current)) {
|
|
1166
|
+
if (current.length === 0) {
|
|
1167
|
+
current.push(value);
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
for (let i = 0; i < current.length; i++) {
|
|
1171
|
+
if (typeof current[i] === 'object' && !Array.isArray(current[i])) {
|
|
1172
|
+
updateLastKey(current[i], value);
|
|
1173
|
+
}
|
|
1174
|
+
else {
|
|
1175
|
+
current[i] = applyDiff(current[i], value, true);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
else {
|
|
1181
|
+
obj[lastKey] = value;
|
|
1182
|
+
}
|
|
1183
|
+
break;
|
|
1184
|
+
}
|
|
1185
|
+
const currentKeys = Object.keys(current);
|
|
1186
|
+
const nextKey = currentKeys[currentKeys.length - 1];
|
|
1187
|
+
obj = current;
|
|
1188
|
+
lastKey = nextKey;
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
/**
|
|
1192
|
+
* Creates a nested object structure from a path string
|
|
1193
|
+
*
|
|
1194
|
+
* This function generates an object hierarchy based on a path selector and assigns
|
|
1195
|
+
* a value at the deepest level. It builds the structure from the inside out,
|
|
1196
|
+
* handling properties, array indices, wildcards, and other selector types.
|
|
1197
|
+
*
|
|
1198
|
+
* @template T - The expected return type
|
|
1199
|
+
* @param path - The path string describing the structure to create
|
|
1200
|
+
* @param value - The value to place at the end of the path (optional)
|
|
1201
|
+
* @returns The generated object/array structure, or undefined if no segments
|
|
1202
|
+
*
|
|
1203
|
+
* @example
|
|
1204
|
+
* // Simple nested object
|
|
1205
|
+
* fromPath('user.profile.name', 'John')
|
|
1206
|
+
* // Returns: { user: { profile: { name: 'John' } } }
|
|
1207
|
+
*
|
|
1208
|
+
* @example
|
|
1209
|
+
* // Array with specific index
|
|
1210
|
+
* fromPath('users[0].name', 'Alice')
|
|
1211
|
+
* // Returns: { users: [{ name: 'Alice' }] }
|
|
1212
|
+
*
|
|
1213
|
+
* @example
|
|
1214
|
+
* // Array with wildcard
|
|
1215
|
+
* fromPath('items[*].active', true)
|
|
1216
|
+
* // Returns: { items: [true] }
|
|
1217
|
+
*
|
|
1218
|
+
* @example
|
|
1219
|
+
* // Deep nested with arrays
|
|
1220
|
+
* fromPath('company.departments[2].manager.name', 'Bob')
|
|
1221
|
+
* // Returns: { company: { departments: [undefined, undefined, { manager: { name: 'Bob' } }] } }
|
|
1222
|
+
*
|
|
1223
|
+
* @example
|
|
1224
|
+
* // Single property
|
|
1225
|
+
* fromPath('count', 42)
|
|
1226
|
+
* // Returns: { count: 42 }
|
|
1227
|
+
*/
|
|
1228
|
+
export const fromPath = (path, value) => {
|
|
1229
|
+
const segments = parseSelector(path);
|
|
1230
|
+
if (segments.length === 0) {
|
|
1231
|
+
return value;
|
|
1232
|
+
}
|
|
1233
|
+
// Build the structure from the end to the beginning
|
|
1234
|
+
let result = value;
|
|
1235
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
1236
|
+
const segment = segments[i];
|
|
1237
|
+
if (segment.type === 'property') {
|
|
1238
|
+
// Create an object with this property
|
|
1239
|
+
result = { [String(segment.value)]: result };
|
|
1240
|
+
}
|
|
1241
|
+
else if (segment.type === 'index') {
|
|
1242
|
+
// Create an array with the value at the specified index
|
|
1243
|
+
const index = parseInt(String(segment.value));
|
|
1244
|
+
const arr = [];
|
|
1245
|
+
arr[index] = result;
|
|
1246
|
+
result = arr;
|
|
1247
|
+
}
|
|
1248
|
+
else if (segment.type === 'wildcard') {
|
|
1249
|
+
// Create an array with one element (or wrap existing value)
|
|
1250
|
+
if (Array.isArray(result)) {
|
|
1251
|
+
// If result is already an array, keep it as is
|
|
1252
|
+
result = result;
|
|
1253
|
+
}
|
|
1254
|
+
else {
|
|
1255
|
+
// Wrap in array
|
|
1256
|
+
result = [result];
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
else if (segment.type === 'slice') {
|
|
1260
|
+
// For slices, create an array with the value
|
|
1261
|
+
// The actual slice logic would be handled when reading, not creating
|
|
1262
|
+
if (!Array.isArray(result)) {
|
|
1263
|
+
result = [result];
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
else if (segment.type === 'union') {
|
|
1267
|
+
// For union (multiple indices), create an array
|
|
1268
|
+
if (!Array.isArray(result)) {
|
|
1269
|
+
result = [result];
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
else if (segment.type === 'filter') {
|
|
1273
|
+
// For filters, create an array
|
|
1274
|
+
if (!Array.isArray(result)) {
|
|
1275
|
+
result = [result];
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
else if (segment.type === 'descendant') {
|
|
1279
|
+
// For descendant selectors, wrap in current structure
|
|
1280
|
+
// The actual recursive logic would be handled when reading
|
|
1281
|
+
result = result;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
return result;
|
|
1285
|
+
};
|
|
1286
|
+
/**
|
|
1287
|
+
* Selects values from an object (legacy method, uses get internally)
|
|
1288
|
+
* @deprecated Use get() instead
|
|
1289
|
+
*/
|
|
1290
|
+
export const select = (obj, selector, options = {}) => {
|
|
1291
|
+
return get(obj, selector, { ...options, preserveStructure: false });
|
|
1292
|
+
};
|
|
1293
|
+
/**
|
|
1294
|
+
* Checks if a selector matches any value in object
|
|
1295
|
+
* @param obj - Source object
|
|
1296
|
+
* @param selector - Selector string or array
|
|
1297
|
+
* @param options - Selector options
|
|
1298
|
+
* @returns True if selector matches at least one value (or array of booleans for array selector)
|
|
1299
|
+
*/
|
|
1300
|
+
export const has = (obj, selector, options = {}) => {
|
|
1301
|
+
// Handle array of selectors
|
|
1302
|
+
if (Array.isArray(selector)) {
|
|
1303
|
+
return selector.map((sel) => has(obj, sel, options));
|
|
1304
|
+
}
|
|
1305
|
+
const result = get(obj, selector, { ...options, preserveStructure: false });
|
|
1306
|
+
if (Array.isArray(result)) {
|
|
1307
|
+
if (result.length > 0) {
|
|
1308
|
+
return result.every((r) => r !== undefined);
|
|
1309
|
+
}
|
|
1310
|
+
return false;
|
|
1311
|
+
}
|
|
1312
|
+
return result !== undefined;
|
|
1313
|
+
};
|
|
1314
|
+
/**
|
|
1315
|
+
* Deletes values from object using selector
|
|
1316
|
+
* @param obj - Target object
|
|
1317
|
+
* @param selector - Selector string or array
|
|
1318
|
+
* @param options - Selector options
|
|
1319
|
+
* @returns True if any values were deleted (or array of booleans for array selector)
|
|
1320
|
+
*/
|
|
1321
|
+
export const deletePath = (obj, selector, options = {}) => {
|
|
1322
|
+
const { throwOnError = false } = options;
|
|
1323
|
+
// Handle array of selectors
|
|
1324
|
+
if (Array.isArray(selector)) {
|
|
1325
|
+
return selector.map((sel) => deletePath(obj, sel, options));
|
|
1326
|
+
}
|
|
1327
|
+
const segments = parseSelector(selector, { throwOnError });
|
|
1328
|
+
if (segments.length === 0) {
|
|
1329
|
+
return false;
|
|
1330
|
+
}
|
|
1331
|
+
const lastSegment = segments[segments.length - 1];
|
|
1332
|
+
// For wildcards, we need to find all matching paths and delete them
|
|
1333
|
+
if (segments.some((s) => s.type === 'wildcard' || s.type === 'slice' || s.type === 'union')) {
|
|
1334
|
+
const paths = getPaths(obj, selector, options);
|
|
1335
|
+
let deleted = false;
|
|
1336
|
+
for (const path of paths) {
|
|
1337
|
+
if (deleteSinglePath(obj, path)) {
|
|
1338
|
+
deleted = true;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return deleted;
|
|
1342
|
+
}
|
|
1343
|
+
// For single path deletion
|
|
1344
|
+
let current = obj;
|
|
1345
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1346
|
+
const segment = segments[i];
|
|
1347
|
+
if (current == null) {
|
|
1348
|
+
return false;
|
|
1349
|
+
}
|
|
1350
|
+
switch (segment.type) {
|
|
1351
|
+
case 'property':
|
|
1352
|
+
current = current[String(segment.value)];
|
|
1353
|
+
break;
|
|
1354
|
+
case 'index':
|
|
1355
|
+
if (!Array.isArray(current))
|
|
1356
|
+
return false;
|
|
1357
|
+
const index = segment.value;
|
|
1358
|
+
if (index < 0 || index >= current.length)
|
|
1359
|
+
return false;
|
|
1360
|
+
current = current[index];
|
|
1361
|
+
break;
|
|
1362
|
+
default:
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (current == null) {
|
|
1367
|
+
return false;
|
|
1368
|
+
}
|
|
1369
|
+
switch (lastSegment.type) {
|
|
1370
|
+
case 'property':
|
|
1371
|
+
if (String(lastSegment.value) in current) {
|
|
1372
|
+
delete current[String(lastSegment.value)];
|
|
1373
|
+
return true;
|
|
1374
|
+
}
|
|
1375
|
+
break;
|
|
1376
|
+
case 'index':
|
|
1377
|
+
if (!Array.isArray(current))
|
|
1378
|
+
return false;
|
|
1379
|
+
const index = lastSegment.value;
|
|
1380
|
+
if (index >= 0 && index < current.length) {
|
|
1381
|
+
current.splice(index, 1);
|
|
1382
|
+
return true;
|
|
1383
|
+
}
|
|
1384
|
+
break;
|
|
1385
|
+
}
|
|
1386
|
+
return false;
|
|
1387
|
+
};
|
|
1388
|
+
/**
|
|
1389
|
+
* Helper for deleting a single path
|
|
1390
|
+
*/
|
|
1391
|
+
const deleteSinglePath = (obj, selector) => {
|
|
1392
|
+
const segments = parseSelector(selector);
|
|
1393
|
+
if (segments.length === 0) {
|
|
1394
|
+
return false;
|
|
1395
|
+
}
|
|
1396
|
+
const lastSegment = segments[segments.length - 1];
|
|
1397
|
+
let current = obj;
|
|
1398
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1399
|
+
const segment = segments[i];
|
|
1400
|
+
if (current == null) {
|
|
1401
|
+
return false;
|
|
1402
|
+
}
|
|
1403
|
+
switch (segment.type) {
|
|
1404
|
+
case 'property':
|
|
1405
|
+
current = current[String(segment.value)];
|
|
1406
|
+
break;
|
|
1407
|
+
case 'index':
|
|
1408
|
+
if (!Array.isArray(current))
|
|
1409
|
+
return false;
|
|
1410
|
+
const index = segment.value;
|
|
1411
|
+
if (index < 0 || index >= current.length)
|
|
1412
|
+
return false;
|
|
1413
|
+
current = current[index];
|
|
1414
|
+
break;
|
|
1415
|
+
default:
|
|
1416
|
+
return false;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
if (current == null) {
|
|
1420
|
+
return false;
|
|
1421
|
+
}
|
|
1422
|
+
switch (lastSegment.type) {
|
|
1423
|
+
case 'property':
|
|
1424
|
+
if (String(lastSegment.value) in current) {
|
|
1425
|
+
delete current[String(lastSegment.value)];
|
|
1426
|
+
return true;
|
|
1427
|
+
}
|
|
1428
|
+
break;
|
|
1429
|
+
case 'index':
|
|
1430
|
+
if (!Array.isArray(current))
|
|
1431
|
+
return false;
|
|
1432
|
+
const index = lastSegment.value;
|
|
1433
|
+
if (index >= 0 && index < current.length) {
|
|
1434
|
+
current.splice(index, 1);
|
|
1435
|
+
return true;
|
|
1436
|
+
}
|
|
1437
|
+
break;
|
|
1438
|
+
}
|
|
1439
|
+
return false;
|
|
1440
|
+
};
|
|
1441
|
+
/**
|
|
1442
|
+
* Gets all matching paths from an object
|
|
1443
|
+
* @param obj - Source object
|
|
1444
|
+
* @param selector - Selector string or array (optional, if not provided returns all paths)
|
|
1445
|
+
* @param options - Selector options
|
|
1446
|
+
* @returns Array of path strings
|
|
1447
|
+
*/
|
|
1448
|
+
export const getPaths = (obj, selector, options = {}) => {
|
|
1449
|
+
const { format = 'selector', maxDepth = 20 } = options;
|
|
1450
|
+
// Handle array of selectors
|
|
1451
|
+
if (selector && Array.isArray(selector)) {
|
|
1452
|
+
const allPaths = [];
|
|
1453
|
+
for (const sel of selector) {
|
|
1454
|
+
const paths = getPaths(obj, sel, options);
|
|
1455
|
+
allPaths.push(...paths);
|
|
1456
|
+
}
|
|
1457
|
+
return allPaths;
|
|
1458
|
+
}
|
|
1459
|
+
if (selector) {
|
|
1460
|
+
// Get paths matching the selector
|
|
1461
|
+
const segments = parseSelector(selector, options);
|
|
1462
|
+
const results = traverseWithPaths(obj, segments, 0, maxDepth);
|
|
1463
|
+
return results.map((r) => format === 'selector' ? r.path : r.stringPath);
|
|
1464
|
+
}
|
|
1465
|
+
// Get all paths
|
|
1466
|
+
const allPaths = [];
|
|
1467
|
+
const traverseAll = (current, path = [], stringPath = [], depth = 0) => {
|
|
1468
|
+
if (depth > maxDepth)
|
|
1469
|
+
return;
|
|
1470
|
+
// Add current path
|
|
1471
|
+
if (path.length > 0) {
|
|
1472
|
+
const pathStr = stringifySelector(path);
|
|
1473
|
+
allPaths.push({ path: pathStr, stringPath: stringPath.join('.') });
|
|
1474
|
+
}
|
|
1475
|
+
if (current && typeof current === 'object') {
|
|
1476
|
+
if (Array.isArray(current)) {
|
|
1477
|
+
for (let i = 0; i < current.length; i++) {
|
|
1478
|
+
traverseAll(current[i], [...path, { type: 'index', value: i, raw: `[${i}]` }], [...stringPath, `[${i}]`], depth + 1);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
else {
|
|
1482
|
+
for (const key in current) {
|
|
1483
|
+
if (current.hasOwnProperty(key)) {
|
|
1484
|
+
traverseAll(current[key], [...path, { type: 'property', value: key, raw: key }], [...stringPath, key], depth + 1);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
traverseAll(obj);
|
|
1491
|
+
return allPaths.map((p) => format === 'selector' ? p.path : p.stringPath);
|
|
1492
|
+
};
|
|
1493
|
+
/**
|
|
1494
|
+
* Finds all paths that match a predicate
|
|
1495
|
+
* @param obj - Source object
|
|
1496
|
+
* @param predicate - Function to test each path/value pair
|
|
1497
|
+
* @returns Array of matching path strings
|
|
1498
|
+
* @example
|
|
1499
|
+
* findPaths({a: 1, b: {c: 2}}, (path, value) => value === 2) // ['b.c']
|
|
1500
|
+
*/
|
|
1501
|
+
export const findPaths = (obj, predicate) => {
|
|
1502
|
+
const paths = getPaths(obj);
|
|
1503
|
+
return paths.filter((path) => {
|
|
1504
|
+
const value = get(obj, path);
|
|
1505
|
+
return predicate(path, value);
|
|
1506
|
+
});
|
|
1507
|
+
};
|
|
1508
|
+
/**
|
|
1509
|
+
* Flattens an object with array notation in keys
|
|
1510
|
+
* @param obj - Source object
|
|
1511
|
+
* @param separator - Separator for nested keys (default: '.')
|
|
1512
|
+
* @returns Flattened object with array notation
|
|
1513
|
+
* @example
|
|
1514
|
+
* flattenWithArrays({a: [{b: 1}, {b: 2}]}) // {'a[0].b': 1, 'a[1].b': 2}
|
|
1515
|
+
*/
|
|
1516
|
+
export const flattenWithArrays = (obj, separator = '.') => {
|
|
1517
|
+
const result = {};
|
|
1518
|
+
const traverse = (current, path = []) => {
|
|
1519
|
+
if (current == null)
|
|
1520
|
+
return;
|
|
1521
|
+
const trav = traverseStructure(current, path, 0, 20);
|
|
1522
|
+
if (trav !== undefined) {
|
|
1523
|
+
result[stringifySelector(path)] = trav;
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
if (Array.isArray(current)) {
|
|
1527
|
+
for (let i = 0; i < current.length; i++) {
|
|
1528
|
+
const newPath = [
|
|
1529
|
+
...path,
|
|
1530
|
+
{ type: 'index', value: i, raw: `[${i}]` },
|
|
1531
|
+
];
|
|
1532
|
+
traverse(current[i], newPath);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
else if (isObject(current)) {
|
|
1536
|
+
for (const key in current) {
|
|
1537
|
+
if (current.hasOwnProperty(key)) {
|
|
1538
|
+
const newPath = [
|
|
1539
|
+
...path,
|
|
1540
|
+
{ type: 'property', value: key, raw: key },
|
|
1541
|
+
];
|
|
1542
|
+
traverse(current[key], newPath);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
// Handle symbols
|
|
1546
|
+
const symbols = Object.getOwnPropertySymbols(current);
|
|
1547
|
+
for (const symbol of symbols) {
|
|
1548
|
+
const newPath = [
|
|
1549
|
+
...path,
|
|
1550
|
+
{
|
|
1551
|
+
type: 'property',
|
|
1552
|
+
value: symbol.toString(),
|
|
1553
|
+
raw: symbol.toString(),
|
|
1554
|
+
},
|
|
1555
|
+
];
|
|
1556
|
+
traverse(current[symbol], newPath);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
else {
|
|
1560
|
+
// Primitive value - add to result
|
|
1561
|
+
const pathStr = stringifySelector(path);
|
|
1562
|
+
result[pathStr] = current;
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
traverse(obj);
|
|
1566
|
+
return result;
|
|
1567
|
+
};
|
|
1568
|
+
/**
|
|
1569
|
+
* Unflattens an object with array notation in keys
|
|
1570
|
+
* @param obj - Source object
|
|
1571
|
+
* @returns Unflattened object
|
|
1572
|
+
* @example
|
|
1573
|
+
* unflattenWithArrays({'a[0].b': 1, 'a[1].b': 2}) // {a: [{b: 1}, {b: 2}]}
|
|
1574
|
+
*/
|
|
1575
|
+
export const unflattenWithArrays = (obj) => {
|
|
1576
|
+
const result = {};
|
|
1577
|
+
for (const key in obj) {
|
|
1578
|
+
set(result, key, obj[key]);
|
|
1579
|
+
}
|
|
1580
|
+
return result;
|
|
1581
|
+
};
|
|
1582
|
+
/**
|
|
1583
|
+
* Merges objects with array notation support for paths
|
|
1584
|
+
* @param objects - Objects to merge
|
|
1585
|
+
* @returns Merged object
|
|
1586
|
+
*/
|
|
1587
|
+
export const deepMergeWithArrays = (...objects) => {
|
|
1588
|
+
const result = {};
|
|
1589
|
+
for (const obj of objects) {
|
|
1590
|
+
if (!isObject(obj))
|
|
1591
|
+
continue;
|
|
1592
|
+
const flat = flattenWithArrays(obj);
|
|
1593
|
+
for (const path in flat) {
|
|
1594
|
+
set(result, path, flat[path]);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
return result;
|
|
1598
|
+
};
|
|
1599
|
+
/**
|
|
1600
|
+
* Traverse with path tracking
|
|
1601
|
+
* @param current - Current value
|
|
1602
|
+
* @param segments - Selector segments
|
|
1603
|
+
* @param depth - Current depth
|
|
1604
|
+
* @param maxDepth - Maximum depth
|
|
1605
|
+
* @param currentPath - Current path array
|
|
1606
|
+
* @param currentStringPath - Current string path array
|
|
1607
|
+
* @returns Array of values with paths
|
|
1608
|
+
* @example
|
|
1609
|
+
* traverseWithPaths({ a: { b: 1 } }, ['a', 'b'], 0, 2);
|
|
1610
|
+
* // [{ value: 1, path: 'a.b', stringPath: 'a.b' }]
|
|
1611
|
+
*/
|
|
1612
|
+
export function traverseWithPaths(current, segments, depth, maxDepth, currentPath = [], currentStringPath = []) {
|
|
1613
|
+
if (depth > maxDepth)
|
|
1614
|
+
return [];
|
|
1615
|
+
if (segments.length === 0)
|
|
1616
|
+
return [
|
|
1617
|
+
{
|
|
1618
|
+
value: current,
|
|
1619
|
+
path: stringifySelector(currentPath),
|
|
1620
|
+
stringPath: currentStringPath.join('.'),
|
|
1621
|
+
},
|
|
1622
|
+
];
|
|
1623
|
+
const segment = segments[0];
|
|
1624
|
+
const remaining = segments.slice(1);
|
|
1625
|
+
const results = [];
|
|
1626
|
+
// Handle descendant selector
|
|
1627
|
+
if (segment.type === 'descendant') {
|
|
1628
|
+
// Current level
|
|
1629
|
+
results.push(...traverseWithPaths(current, remaining, depth + 1, maxDepth, currentPath, currentStringPath));
|
|
1630
|
+
// Recursive descent
|
|
1631
|
+
if (current && typeof current === 'object') {
|
|
1632
|
+
if (Array.isArray(current)) {
|
|
1633
|
+
for (let i = 0; i < current.length; i++) {
|
|
1634
|
+
results.push(...traverseWithPaths(current[i], segments, // Use same segments for recursion
|
|
1635
|
+
depth + 1, maxDepth, [...currentPath, { type: 'index', value: i, raw: `[${i}]` }], [...currentStringPath, `[${i}]`]));
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
else {
|
|
1639
|
+
for (const key in current) {
|
|
1640
|
+
if (current.hasOwnProperty(key)) {
|
|
1641
|
+
results.push(...traverseWithPaths(current[key], segments, depth + 1, maxDepth, [...currentPath, { type: 'property', value: key, raw: key }], [...currentStringPath, key]));
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
return results;
|
|
1647
|
+
}
|
|
1648
|
+
// Handle other segment types
|
|
1649
|
+
switch (segment.type) {
|
|
1650
|
+
case 'property':
|
|
1651
|
+
if (current &&
|
|
1652
|
+
typeof current === 'object' &&
|
|
1653
|
+
String(segment.value) in current) {
|
|
1654
|
+
const next = current[String(segment.value)];
|
|
1655
|
+
results.push(...traverseWithPaths(next, remaining, depth + 1, maxDepth, [...currentPath, segment], [...currentStringPath, String(segment.value)]));
|
|
1656
|
+
}
|
|
1657
|
+
break;
|
|
1658
|
+
case 'index':
|
|
1659
|
+
if (Array.isArray(current)) {
|
|
1660
|
+
const index = segment.value;
|
|
1661
|
+
if (index >= 0 && index < current.length) {
|
|
1662
|
+
results.push(...traverseWithPaths(current[index], remaining, depth + 1, maxDepth, [...currentPath, segment], [...currentStringPath, `[${index}]`]));
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
else if (current && typeof current === 'object') {
|
|
1666
|
+
const key = String(segment.value);
|
|
1667
|
+
if (key in current) {
|
|
1668
|
+
results.push(...traverseWithPaths(current[key], remaining, depth + 1, maxDepth, [...currentPath, { ...segment, type: 'property' }], [...currentStringPath, key]));
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
break;
|
|
1672
|
+
case 'wildcard':
|
|
1673
|
+
if (current && typeof current === 'object') {
|
|
1674
|
+
if (Array.isArray(current)) {
|
|
1675
|
+
for (let i = 0; i < current.length; i++) {
|
|
1676
|
+
results.push(...traverseWithPaths(current[i], remaining, depth + 1, maxDepth, [...currentPath, { type: 'index', value: i, raw: `[${i}]` }], [...currentStringPath, `[${i}]`]));
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
else {
|
|
1680
|
+
for (const key in current) {
|
|
1681
|
+
if (current.hasOwnProperty(key)) {
|
|
1682
|
+
results.push(...traverseWithPaths(current[key], remaining, depth + 1, maxDepth, [...currentPath, { type: 'property', value: key, raw: key }], [...currentStringPath, key]));
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
break;
|
|
1688
|
+
case 'slice':
|
|
1689
|
+
if (Array.isArray(current)) {
|
|
1690
|
+
const [start, end] = segment.value;
|
|
1691
|
+
const actualStart = Math.max(0, start);
|
|
1692
|
+
const actualEnd = Math.min(current.length, end === Infinity ? current.length : end);
|
|
1693
|
+
for (let i = actualStart; i < actualEnd; i++) {
|
|
1694
|
+
results.push(...traverseWithPaths(current[i], remaining, depth + 1, maxDepth, [...currentPath, { type: 'index', value: i, raw: `[${i}]` }], [...currentStringPath, `[${i}]`]));
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
break;
|
|
1698
|
+
case 'union':
|
|
1699
|
+
if (Array.isArray(current)) {
|
|
1700
|
+
const indices = segment.value;
|
|
1701
|
+
for (const index of indices) {
|
|
1702
|
+
if (index >= 0 && index < current.length) {
|
|
1703
|
+
results.push(...traverseWithPaths(current[index], remaining, depth + 1, maxDepth, [
|
|
1704
|
+
...currentPath,
|
|
1705
|
+
{ type: 'index', value: index, raw: `[${index}]` },
|
|
1706
|
+
], [...currentStringPath, `[${index}]`]));
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
break;
|
|
1711
|
+
case 'filter':
|
|
1712
|
+
if (Array.isArray(current)) {
|
|
1713
|
+
const filterFn = segment.value;
|
|
1714
|
+
for (let i = 0; i < current.length; i++) {
|
|
1715
|
+
if (filterFn(current[i])) {
|
|
1716
|
+
results.push(...traverseWithPaths(current[i], remaining, depth + 1, maxDepth, [...currentPath, { type: 'index', value: i, raw: `[${i}]` }], [...currentStringPath, `[${i}]`]));
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
break;
|
|
1721
|
+
}
|
|
1722
|
+
return results;
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Updates a value at a path using a transformation function
|
|
1726
|
+
* @param obj - Target object
|
|
1727
|
+
* @param path - Path string
|
|
1728
|
+
* @param updater - Function to transform the current value
|
|
1729
|
+
* @returns Modified object
|
|
1730
|
+
* @example
|
|
1731
|
+
* update({a: {b: 1}}, 'a.b', (val) => val + 1) // {a: {b: 2}}
|
|
1732
|
+
* update({items: [{count: 1}]}, 'items[0].count', (count) => count * 2) // {items: [{count: 2}]}
|
|
1733
|
+
*/
|
|
1734
|
+
export const update = (obj, path, updater) => {
|
|
1735
|
+
const currentValue = get(obj, path);
|
|
1736
|
+
const newValue = updater(currentValue);
|
|
1737
|
+
return setValueAtPath(obj, path, newValue);
|
|
1738
|
+
};
|
|
1739
|
+
export const updateWithCallBack = (obj, updater) => {
|
|
1740
|
+
const newValue = updater(obj);
|
|
1741
|
+
// console.log('update trigger', obj, path, newValue)
|
|
1742
|
+
// if(typeof newValue === 'number'){
|
|
1743
|
+
// return setAtPath(obj, path, newValue);
|
|
1744
|
+
// }
|
|
1745
|
+
// return setValueAtPath(obj as any, path as any, newValue as any);
|
|
1746
|
+
// console.log('callback update before', Array.isArray(obj) ? [...obj] : {...obj} , newValue);
|
|
1747
|
+
const result = applyDiff(obj, newValue, true);
|
|
1748
|
+
// const result = deepMerge(obj, newValue);
|
|
1749
|
+
// console.log('callback update', {obj, result, updater, newValue})
|
|
1750
|
+
// return obj;
|
|
1751
|
+
return result;
|
|
1752
|
+
};
|
|
1753
|
+
/**
|
|
1754
|
+
* Gets the type of value at a path
|
|
1755
|
+
* @param obj - Source object
|
|
1756
|
+
* @param path - Path string
|
|
1757
|
+
* @returns Type string or undefined
|
|
1758
|
+
* @example
|
|
1759
|
+
* getTypeAtPath({a: [{b: 1}]}, 'a[0].b') // 'number'
|
|
1760
|
+
* getTypeAtPath({a: null}, 'a.b') // undefined
|
|
1761
|
+
*/
|
|
1762
|
+
export const getTypeAtPath = (obj, path) => {
|
|
1763
|
+
const value = get(obj, path);
|
|
1764
|
+
if (value === undefined)
|
|
1765
|
+
return undefined;
|
|
1766
|
+
if (value === null)
|
|
1767
|
+
return 'null';
|
|
1768
|
+
if (Array.isArray(value))
|
|
1769
|
+
return 'array';
|
|
1770
|
+
return typeof value;
|
|
1771
|
+
};
|