@helpers4/object 2.0.4 → 3.0.0-alpha.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/README.md +5 -3
- package/lib/index.d.ts +243 -78
- package/lib/index.js +121 -69
- package/lib/index.js.map +1 -1
- package/llms.txt +179 -151
- package/meta/api.json +150 -202
- package/meta/examples.json +20 -20
- package/meta/native-alternatives.json +1 -1
- package/package.json +3 -3
package/lib/index.js
CHANGED
|
@@ -1,49 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
function compact(obj) {
|
|
3
|
-
if (obj === void 0 || obj === null) return obj;
|
|
4
|
-
const result = {};
|
|
5
|
-
for (const key of Object.keys(obj)) if (obj[key]) result[key] = obj[key];
|
|
6
|
-
return result;
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
|
-
//#region helpers/object/_unsafeKeys.ts
|
|
10
|
-
/**
|
|
11
|
-
* This file is part of helpers4.
|
|
12
|
-
* Copyright (C) 2025 baxyz
|
|
13
|
-
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
14
|
-
*/
|
|
15
|
-
/**
|
|
16
|
-
* Keys that must never be set on a plain object (prototype-pollution guard).
|
|
17
|
-
* @internal
|
|
18
|
-
*/
|
|
19
|
-
var UNSAFE_KEYS = new Set([
|
|
1
|
+
var UNSAFE_KEYS = /* @__PURE__ */ new Set([
|
|
20
2
|
"__proto__",
|
|
21
3
|
"constructor",
|
|
22
4
|
"prototype"
|
|
23
5
|
]);
|
|
24
6
|
//#endregion
|
|
25
|
-
//#region helpers/object/
|
|
7
|
+
//#region helpers/object/cloneDeep.ts
|
|
26
8
|
/**
|
|
27
9
|
* This file is part of helpers4.
|
|
28
10
|
* Copyright (C) 2025 baxyz
|
|
29
11
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
30
12
|
*/
|
|
31
13
|
/**
|
|
32
|
-
* Creates a deep copy of an object or array
|
|
33
|
-
*
|
|
34
|
-
*
|
|
14
|
+
* Creates a deep copy of an object or array.
|
|
15
|
+
*
|
|
16
|
+
* Recursively clones own enumerable string keys. `Date` instances are
|
|
17
|
+
* reconstructed with the same epoch value. Prototype-polluting keys
|
|
18
|
+
* (`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
19
|
+
* Does **not** handle circular references.
|
|
20
|
+
*
|
|
21
|
+
* **Limitation:** symbol-keyed own properties are **not** copied — only string
|
|
22
|
+
* keys are processed. Use `structuredClone` if symbol propagation is required.
|
|
23
|
+
*
|
|
24
|
+
* @param obj - The value to clone
|
|
25
|
+
* @returns A deep copy of `obj`
|
|
26
|
+
* @example
|
|
27
|
+
* cloneDeep({ a: { b: [1, 2] } })
|
|
28
|
+
* // => { a: { b: [1, 2] } } — fully independent copy
|
|
29
|
+
*
|
|
30
|
+
* cloneDeep(new Date('2024-01-01'))
|
|
31
|
+
* // => Date with the same timestamp
|
|
32
|
+
*
|
|
33
|
+
* // Primitives pass through unchanged
|
|
34
|
+
* cloneDeep(42) // => 42
|
|
35
|
+
* cloneDeep(null) // => null
|
|
35
36
|
* @since 1.9.0
|
|
36
37
|
*/
|
|
37
|
-
function
|
|
38
|
+
function cloneDeep(obj) {
|
|
38
39
|
if (obj === null || typeof obj !== "object") return obj;
|
|
39
40
|
if (obj instanceof Date) return new Date(obj.getTime());
|
|
40
|
-
if (Array.isArray(obj)) return obj.map((item) =>
|
|
41
|
+
if (Array.isArray(obj)) return obj.map((item) => cloneDeep(item));
|
|
41
42
|
const cloned = {};
|
|
42
|
-
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key) && !UNSAFE_KEYS.has(key)) cloned[key] =
|
|
43
|
+
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key) && !UNSAFE_KEYS.has(key)) cloned[key] = cloneDeep(obj[key]);
|
|
43
44
|
return cloned;
|
|
44
45
|
}
|
|
45
46
|
//#endregion
|
|
46
|
-
//#region helpers/
|
|
47
|
+
//#region helpers/object/compact.ts
|
|
48
|
+
/**
|
|
49
|
+
* This file is part of helpers4.
|
|
50
|
+
* Copyright (C) 2025 baxyz
|
|
51
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
52
|
+
*/
|
|
53
|
+
function compact(obj) {
|
|
54
|
+
if (obj === void 0 || obj === null) return obj;
|
|
55
|
+
const result = {};
|
|
56
|
+
for (const key of Object.keys(obj)) {
|
|
57
|
+
if (UNSAFE_KEYS.has(key)) continue;
|
|
58
|
+
if (obj[key]) result[key] = obj[key];
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region helpers/guard/isPlainObject.ts
|
|
47
64
|
/**
|
|
48
65
|
* This file is part of helpers4.
|
|
49
66
|
* Copyright (C) 2025 baxyz
|
|
@@ -71,36 +88,6 @@ function isPlainObject(value) {
|
|
|
71
88
|
return proto === Object.prototype || proto === null;
|
|
72
89
|
}
|
|
73
90
|
//#endregion
|
|
74
|
-
//#region helpers/object/deepMerge.ts
|
|
75
|
-
/**
|
|
76
|
-
* This file is part of helpers4.
|
|
77
|
-
* Copyright (C) 2025 baxyz
|
|
78
|
-
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
79
|
-
*/
|
|
80
|
-
function mergeKey(target, source, key) {
|
|
81
|
-
const targetValue = target[key];
|
|
82
|
-
const sourceValue = source[key];
|
|
83
|
-
if (isPlainObject(targetValue) && isPlainObject(sourceValue)) target[key] = deepMerge(targetValue, sourceValue);
|
|
84
|
-
else if (sourceValue !== void 0) target[key] = sourceValue;
|
|
85
|
-
}
|
|
86
|
-
function deepMerge(target, ...sources) {
|
|
87
|
-
if (target === void 0 || target === null) return target;
|
|
88
|
-
if (!sources.length) return target;
|
|
89
|
-
const source = sources.shift();
|
|
90
|
-
if (!source) return deepMerge(target, ...sources);
|
|
91
|
-
const t = target;
|
|
92
|
-
const s = source;
|
|
93
|
-
for (const key in source) {
|
|
94
|
-
if (!Object.hasOwn(source, key) || UNSAFE_KEYS.has(key)) continue;
|
|
95
|
-
mergeKey(t, s, key);
|
|
96
|
-
}
|
|
97
|
-
for (const sym of Object.getOwnPropertySymbols(source)) {
|
|
98
|
-
if (!Object.prototype.propertyIsEnumerable.call(source, sym)) continue;
|
|
99
|
-
mergeKey(t, s, sym);
|
|
100
|
-
}
|
|
101
|
-
return deepMerge(target, ...sources);
|
|
102
|
-
}
|
|
103
|
-
//#endregion
|
|
104
91
|
//#region helpers/array/equalsDeep.ts
|
|
105
92
|
/**
|
|
106
93
|
* This file is part of helpers4.
|
|
@@ -265,7 +252,7 @@ function compare(dateA, dateB, options = {}) {
|
|
|
265
252
|
}
|
|
266
253
|
}
|
|
267
254
|
//#endregion
|
|
268
|
-
//#region helpers/
|
|
255
|
+
//#region helpers/guard/isSpecialObject.ts
|
|
269
256
|
/**
|
|
270
257
|
* This file is part of helpers4.
|
|
271
258
|
* Copyright (C) 2025 baxyz
|
|
@@ -345,7 +332,7 @@ function diff(objA, objB, options = {}) {
|
|
|
345
332
|
return diffPlainObjects(objA, objB, depth);
|
|
346
333
|
}
|
|
347
334
|
function diffPlainObjects(objA, objB, depth) {
|
|
348
|
-
const keys = Array.from(new Set([...Object.keys(objA), ...Object.keys(objB)]));
|
|
335
|
+
const keys = Array.from(/* @__PURE__ */ new Set([...Object.keys(objA), ...Object.keys(objB)]));
|
|
349
336
|
const differences = Object.create(null);
|
|
350
337
|
for (const key of keys) {
|
|
351
338
|
const hasA = Object.prototype.hasOwnProperty.call(objA, key);
|
|
@@ -476,7 +463,7 @@ function equalsShallow(objA, objB) {
|
|
|
476
463
|
* - Non-bracket text trailing a `]` within the same dot-segment throws a RangeError;
|
|
477
464
|
* use a dot separator instead (`'a[0].b'` not `'a[0]b'`).
|
|
478
465
|
*
|
|
479
|
-
* @
|
|
466
|
+
* @ignore
|
|
480
467
|
* @example
|
|
481
468
|
* parsePath('a.b.c') // => ['a', 'b', 'c'] (all strings)
|
|
482
469
|
* parsePath('layers.1.x') // => ['layers', '1', 'x'] (all strings)
|
|
@@ -521,6 +508,7 @@ function parsePathUncached(path) {
|
|
|
521
508
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
522
509
|
*/
|
|
523
510
|
function get(obj, path, defaultValue) {
|
|
511
|
+
if (obj == null) return defaultValue;
|
|
524
512
|
const keys = typeof path === "string" ? parsePath(path) : path;
|
|
525
513
|
let result = obj;
|
|
526
514
|
for (const key of keys) {
|
|
@@ -541,6 +529,9 @@ function get(obj, path, defaultValue) {
|
|
|
541
529
|
*
|
|
542
530
|
* A thin, typed wrapper around `Object.groupBy` (ES2024) that works on
|
|
543
531
|
* older targets and provides stricter return-type inference.
|
|
532
|
+
* `null` and `undefined` are treated as empty arrays and return `{}`.
|
|
533
|
+
* Items whose computed key is a prototype-polluting string (`__proto__`,
|
|
534
|
+
* `constructor`, `prototype`) are silently skipped.
|
|
544
535
|
*
|
|
545
536
|
* @param items - The array to group
|
|
546
537
|
* @param keyFn - Function that returns the group key for each item
|
|
@@ -548,12 +539,16 @@ function get(obj, path, defaultValue) {
|
|
|
548
539
|
* @example
|
|
549
540
|
* groupBy([1, 2, 3, 4], n => n % 2 === 0 ? 'even' : 'odd')
|
|
550
541
|
* // => { odd: [1, 3], even: [2, 4] }
|
|
542
|
+
* groupBy(null, n => n % 2 === 0 ? 'even' : 'odd')
|
|
543
|
+
* // => {}
|
|
551
544
|
* @since 2.0.0
|
|
552
545
|
*/
|
|
553
546
|
function groupBy(items, keyFn) {
|
|
554
547
|
const result = {};
|
|
548
|
+
if (items == null) return result;
|
|
555
549
|
for (const item of items) {
|
|
556
550
|
const key = keyFn(item);
|
|
551
|
+
if (typeof key === "string" && UNSAFE_KEYS.has(key)) continue;
|
|
557
552
|
if (result[key] === void 0) result[key] = [];
|
|
558
553
|
result[key].push(item);
|
|
559
554
|
}
|
|
@@ -569,6 +564,9 @@ function groupBy(items, keyFn) {
|
|
|
569
564
|
/**
|
|
570
565
|
* Returns a new object with keys and values swapped.
|
|
571
566
|
* If multiple keys share the same value, the last one wins.
|
|
567
|
+
* `null` and `undefined` are treated as empty objects and return `{}`.
|
|
568
|
+
* Entries whose source key **or** value is a prototype-polluting string
|
|
569
|
+
* (`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
572
570
|
*
|
|
573
571
|
* @param obj - The object whose keys and values are to be swapped
|
|
574
572
|
* @returns A new object with values as keys and original keys as values
|
|
@@ -578,11 +576,19 @@ function groupBy(items, keyFn) {
|
|
|
578
576
|
*
|
|
579
577
|
* invert({ one: 1, two: 2 })
|
|
580
578
|
* // => { 1: 'one', 2: 'two' }
|
|
579
|
+
*
|
|
580
|
+
* invert(null)
|
|
581
|
+
* // => {}
|
|
581
582
|
* @since 2.0.0
|
|
582
583
|
*/
|
|
583
584
|
function invert(obj) {
|
|
584
585
|
const result = {};
|
|
585
|
-
|
|
586
|
+
if (obj == null) return result;
|
|
587
|
+
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key) && !UNSAFE_KEYS.has(key)) {
|
|
588
|
+
const val = obj[key];
|
|
589
|
+
if (typeof val === "string" && UNSAFE_KEYS.has(val)) continue;
|
|
590
|
+
result[val] = key;
|
|
591
|
+
}
|
|
586
592
|
return result;
|
|
587
593
|
}
|
|
588
594
|
//#endregion
|
|
@@ -594,20 +600,23 @@ function invert(obj) {
|
|
|
594
600
|
*/
|
|
595
601
|
/**
|
|
596
602
|
* Checks if a plain object has no own enumerable string-keyed properties.
|
|
603
|
+
* `null` and `undefined` are treated as empty objects and return `true`.
|
|
597
604
|
*
|
|
598
605
|
* Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
599
606
|
* separately if symbol keys matter for your use case.
|
|
600
607
|
*
|
|
601
608
|
* @param value - The object to check
|
|
602
|
-
* @returns `true` if the object has no own enumerable string-keyed properties
|
|
609
|
+
* @returns `true` if the object has no own enumerable string-keyed properties, or if `value` is `null`/`undefined`
|
|
603
610
|
* @example
|
|
604
611
|
* isEmpty({}) // => true
|
|
612
|
+
* isEmpty(null) // => true
|
|
613
|
+
* isEmpty(undefined) // => true
|
|
605
614
|
* isEmpty({ a: 1 }) // => false
|
|
606
615
|
* isEmpty({ a: undefined }) // => false (key exists)
|
|
607
616
|
* @since 2.0.3
|
|
608
617
|
*/
|
|
609
618
|
function isEmpty(value) {
|
|
610
|
-
return Object.keys(value).length === 0;
|
|
619
|
+
return value == null || Object.keys(value).length === 0;
|
|
611
620
|
}
|
|
612
621
|
//#endregion
|
|
613
622
|
//#region helpers/object/isNonEmpty.ts
|
|
@@ -618,19 +627,23 @@ function isEmpty(value) {
|
|
|
618
627
|
*/
|
|
619
628
|
/**
|
|
620
629
|
* Checks if a plain object has at least one own enumerable string-keyed property.
|
|
630
|
+
* `null` and `undefined` are treated as empty objects and return `false`.
|
|
621
631
|
*
|
|
622
632
|
* Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
623
633
|
* separately if symbol keys matter for your use case.
|
|
624
634
|
*
|
|
625
635
|
* @param value - The object to check
|
|
626
|
-
* @returns `true` if the object has at least one own enumerable string-keyed property
|
|
636
|
+
* @returns `true` if the object has at least one own enumerable string-keyed property; `false` for empty, `null`, or `undefined`.
|
|
637
|
+
* Acts as a type guard: the `if` branch narrows `Record<PropertyKey, unknown> | null | undefined` to `Record<PropertyKey, unknown>`.
|
|
627
638
|
* @example
|
|
628
639
|
* isNonEmpty({ a: 1 }) // => true
|
|
629
640
|
* isNonEmpty({}) // => false
|
|
641
|
+
* isNonEmpty(null) // => false
|
|
642
|
+
* isNonEmpty(undefined) // => false
|
|
630
643
|
* @since 2.0.3
|
|
631
644
|
*/
|
|
632
645
|
function isNonEmpty(value) {
|
|
633
|
-
return Object.keys(value).length > 0;
|
|
646
|
+
return value != null && Object.keys(value).length > 0;
|
|
634
647
|
}
|
|
635
648
|
//#endregion
|
|
636
649
|
//#region helpers/object/map.ts
|
|
@@ -647,7 +660,8 @@ function isNonEmpty(value) {
|
|
|
647
660
|
* when `mapKey` is omitted the original keys are preserved.
|
|
648
661
|
*
|
|
649
662
|
* Note: if two different keys map to the same output key the last one wins
|
|
650
|
-
* (insertion order).
|
|
663
|
+
* (insertion order). Entries whose mapped key is a prototype-polluting string
|
|
664
|
+
* (`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
651
665
|
*
|
|
652
666
|
* @param obj - The source object
|
|
653
667
|
* @param mapValue - Callback called with `(value, key)` for each entry.
|
|
@@ -668,14 +682,44 @@ function isNonEmpty(value) {
|
|
|
668
682
|
*/
|
|
669
683
|
function map(obj, mapValue, mapKey) {
|
|
670
684
|
const result = {};
|
|
685
|
+
if (obj == null) return result;
|
|
671
686
|
for (const key of Object.keys(obj)) {
|
|
672
687
|
const value = obj[key];
|
|
673
688
|
const newKey = mapKey ? mapKey(key, value) : key;
|
|
689
|
+
if (typeof newKey === "string" && UNSAFE_KEYS.has(newKey)) continue;
|
|
674
690
|
result[newKey] = mapValue ? mapValue(value, key) : value;
|
|
675
691
|
}
|
|
676
692
|
return result;
|
|
677
693
|
}
|
|
678
694
|
//#endregion
|
|
695
|
+
//#region helpers/object/mergeDeep.ts
|
|
696
|
+
/**
|
|
697
|
+
* This file is part of helpers4.
|
|
698
|
+
* Copyright (C) 2025 baxyz
|
|
699
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
700
|
+
*/
|
|
701
|
+
function applyKey(result, source, key) {
|
|
702
|
+
const resultValue = result[key];
|
|
703
|
+
const sourceValue = source[key];
|
|
704
|
+
if (isPlainObject(resultValue) && isPlainObject(sourceValue)) result[key] = mergeDeep(resultValue, sourceValue);
|
|
705
|
+
else if (sourceValue !== void 0) result[key] = isPlainObject(sourceValue) || Array.isArray(sourceValue) ? cloneDeep(sourceValue) : sourceValue;
|
|
706
|
+
}
|
|
707
|
+
function applySource(result, source) {
|
|
708
|
+
for (const key in source) {
|
|
709
|
+
if (!Object.hasOwn(source, key) || UNSAFE_KEYS.has(key)) continue;
|
|
710
|
+
applyKey(result, source, key);
|
|
711
|
+
}
|
|
712
|
+
for (const sym of Object.getOwnPropertySymbols(source)) {
|
|
713
|
+
if (!Object.prototype.propertyIsEnumerable.call(source, sym)) continue;
|
|
714
|
+
applyKey(result, source, sym);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
function mergeDeep(...sources) {
|
|
718
|
+
const result = {};
|
|
719
|
+
for (const source of sources) if (source !== null && source !== void 0) applySource(result, source);
|
|
720
|
+
return result;
|
|
721
|
+
}
|
|
722
|
+
//#endregion
|
|
679
723
|
//#region helpers/object/omit.ts
|
|
680
724
|
function omit(obj, keys) {
|
|
681
725
|
if (obj === void 0 || obj === null) return obj;
|
|
@@ -685,14 +729,22 @@ function omit(obj, keys) {
|
|
|
685
729
|
}
|
|
686
730
|
//#endregion
|
|
687
731
|
//#region helpers/object/pick.ts
|
|
732
|
+
/**
|
|
733
|
+
* This file is part of helpers4.
|
|
734
|
+
* Copyright (C) 2025 baxyz
|
|
735
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
736
|
+
*/
|
|
688
737
|
function pick(obj, keys) {
|
|
689
738
|
if (obj === void 0 || obj === null) return obj;
|
|
690
739
|
const result = {};
|
|
691
|
-
for (const key of keys)
|
|
740
|
+
for (const key of keys) {
|
|
741
|
+
if (typeof key === "string" && UNSAFE_KEYS.has(key)) continue;
|
|
742
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) result[key] = obj[key];
|
|
743
|
+
}
|
|
692
744
|
return result;
|
|
693
745
|
}
|
|
694
746
|
//#endregion
|
|
695
|
-
//#region helpers/
|
|
747
|
+
//#region helpers/guard/isNullish.ts
|
|
696
748
|
/**
|
|
697
749
|
* This file is part of helpers4.
|
|
698
750
|
* Copyright (C) 2025 baxyz
|
|
@@ -765,6 +817,6 @@ function set(obj, path, value) {
|
|
|
765
817
|
return obj;
|
|
766
818
|
}
|
|
767
819
|
//#endregion
|
|
768
|
-
export {
|
|
820
|
+
export { cloneDeep, compact, diff, equalsDeep, equalsShallow, get, groupBy, invert, isEmpty, isNonEmpty, map, mergeDeep, omit, pick, removeUndefinedNull, safeJsonParse, set };
|
|
769
821
|
|
|
770
822
|
//# sourceMappingURL=index.js.map
|