@helpers4/object 3.0.1 → 3.0.3
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 +11 -0
- package/lib/index.d.ts +174 -1
- package/lib/index.js +304 -35
- package/lib/index.js.map +1 -1
- package/llms.txt +293 -2
- package/meta/api.json +287 -1
- package/meta/examples.json +85 -0
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ A set of helpers for working with URLs.
|
|
|
12
12
|
[https://helpers4.dev/typescript/categories/object/](https://helpers4.dev/typescript/categories/object/)
|
|
13
13
|
|
|
14
14
|
<!-- AUTOMATIC-METHODS -->
|
|
15
|
+
- camelCaseKeys
|
|
15
16
|
- clone
|
|
16
17
|
- cloneDeep
|
|
17
18
|
- compact
|
|
@@ -24,16 +25,22 @@ A set of helpers for working with URLs.
|
|
|
24
25
|
- invert
|
|
25
26
|
- isEmpty
|
|
26
27
|
- isNonEmpty
|
|
28
|
+
- kebabCaseKeys
|
|
27
29
|
- map
|
|
30
|
+
- mapDeep
|
|
28
31
|
- mergeDeep
|
|
29
32
|
- omit
|
|
30
33
|
- omitBy
|
|
31
34
|
- parsePropertyPath
|
|
35
|
+
- pascalCaseKeys
|
|
32
36
|
- pick
|
|
33
37
|
- pickBy
|
|
34
38
|
- removeUndefinedNull
|
|
35
39
|
- safeJsonParse
|
|
36
40
|
- set
|
|
41
|
+
- snakeCaseKeys
|
|
42
|
+
- sortKeys
|
|
43
|
+
- titleCaseKeys
|
|
37
44
|
- unflatten
|
|
38
45
|
- unset
|
|
39
46
|
- update
|
|
@@ -50,11 +57,13 @@ A set of helpers for working with URLs.
|
|
|
50
57
|
- [function](../function)
|
|
51
58
|
- [guard](../guard)
|
|
52
59
|
- [id](../id)
|
|
60
|
+
- [map](../map)
|
|
53
61
|
- [markdown](../markdown)
|
|
54
62
|
- [node](../node)
|
|
55
63
|
- [number](../number)
|
|
56
64
|
- [observable](../observable)
|
|
57
65
|
- [promise](../promise)
|
|
66
|
+
- [set](../set)
|
|
58
67
|
- [string](../string)
|
|
59
68
|
- [type](../type)
|
|
60
69
|
- [url](../url)
|
|
@@ -74,12 +83,14 @@ A set of helpers for working with URLs.
|
|
|
74
83
|
| function | [@helpers4/function](https://www.npmjs.com/package/@helpers4/function) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/function) | Function utilities and type guards |
|
|
75
84
|
| guard | [@helpers4/guard](https://www.npmjs.com/package/@helpers4/guard) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/guard) | Runtime type guard utilities |
|
|
76
85
|
| id | [@helpers4/id](https://www.npmjs.com/package/@helpers4/id) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/id) | Unique identifier generation utilities |
|
|
86
|
+
| map | [@helpers4/map](https://www.npmjs.com/package/@helpers4/map) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/map) | Native Map manipulation utilities |
|
|
77
87
|
| markdown | [@helpers4/markdown](https://www.npmjs.com/package/@helpers4/markdown) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/markdown) | Markdown formatting and escaping utilities |
|
|
78
88
|
| node | [@helpers4/node](https://www.npmjs.com/package/@helpers4/node) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/node) | Node.js-specific utilities |
|
|
79
89
|
| number | [@helpers4/number](https://www.npmjs.com/package/@helpers4/number) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/number) | Number utility functions and validation |
|
|
80
90
|
| object | [@helpers4/object](https://www.npmjs.com/package/@helpers4/object) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/object) | Object manipulation utilities |
|
|
81
91
|
| observable | [@helpers4/observable](https://www.npmjs.com/package/@helpers4/observable) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/observable) | Observable utilities and combinators |
|
|
82
92
|
| promise | [@helpers4/promise](https://www.npmjs.com/package/@helpers4/promise) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/promise) | Promise utilities and error handling |
|
|
93
|
+
| set | [@helpers4/set](https://www.npmjs.com/package/@helpers4/set) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/set) | Native Set manipulation utilities |
|
|
83
94
|
| string | [@helpers4/string](https://www.npmjs.com/package/@helpers4/string) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/string) | String manipulation and formatting utilities |
|
|
84
95
|
| type | [@helpers4/type](https://www.npmjs.com/package/@helpers4/type) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/type) | Compile-time TypeScript utility types |
|
|
85
96
|
| url | [@helpers4/url](https://www.npmjs.com/package/@helpers4/url) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/url) | URL parsing and manipulation utilities |
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of helpers4.
|
|
3
|
+
* Copyright (C) 2025 baxyz
|
|
4
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
8
|
+
* nested objects) to camelCase. Handles snake_case, kebab-case, PascalCase, and space-separated
|
|
9
|
+
* keys. Non-plain-object values (arrays' items aside) — `Date`, `Map`, `Set`, class instances,
|
|
10
|
+
* primitives — are left untouched, only their position is walked into. An entry whose
|
|
11
|
+
* transformed key is a prototype-polluting string (`__proto__`, `constructor`, `prototype`) is
|
|
12
|
+
* silently skipped, same as the rest of `@helpers4/object`.
|
|
13
|
+
* @param value - The object (or array of objects) to transform
|
|
14
|
+
* @returns A new value with every plain-object key converted to camelCase
|
|
15
|
+
* @example
|
|
16
|
+
* camelCaseKeys({ user_name: 'Alice', home_address: { zip_code: '12345' } })
|
|
17
|
+
* // => { userName: 'Alice', homeAddress: { zipCode: '12345' } }
|
|
18
|
+
* @since 3.0.3
|
|
19
|
+
*/
|
|
20
|
+
declare function camelCaseKeys<T>(value: T): T;
|
|
21
|
+
|
|
1
22
|
/**
|
|
2
23
|
* This file is part of helpers4.
|
|
3
24
|
* Copyright (C) 2025 baxyz
|
|
@@ -530,6 +551,27 @@ declare function isEmpty(value: Record<PropertyKey, unknown> | null | undefined)
|
|
|
530
551
|
*/
|
|
531
552
|
declare function isNonEmpty(value: Record<PropertyKey, unknown> | null | undefined): value is Record<PropertyKey, unknown>;
|
|
532
553
|
|
|
554
|
+
/**
|
|
555
|
+
* This file is part of helpers4.
|
|
556
|
+
* Copyright (C) 2025 baxyz
|
|
557
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
558
|
+
*/
|
|
559
|
+
/**
|
|
560
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
561
|
+
* nested objects) to kebab-case. Handles snake_case, camelCase, PascalCase, and space-separated
|
|
562
|
+
* keys. Non-plain-object values — `Date`, `Map`, `Set`, class instances, primitives — are left
|
|
563
|
+
* untouched, only their position is walked into. An entry whose transformed key is a
|
|
564
|
+
* prototype-polluting string (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
565
|
+
* same as the rest of `@helpers4/object`.
|
|
566
|
+
* @param value - The object (or array of objects) to transform
|
|
567
|
+
* @returns A new value with every plain-object key converted to kebab-case
|
|
568
|
+
* @example
|
|
569
|
+
* kebabCaseKeys({ userName: 'Alice', homeAddress: { zipCode: '12345' } })
|
|
570
|
+
* // => { 'user-name': 'Alice', 'home-address': { 'zip-code': '12345' } }
|
|
571
|
+
* @since 3.0.3
|
|
572
|
+
*/
|
|
573
|
+
declare function kebabCaseKeys<T>(value: T): T;
|
|
574
|
+
|
|
533
575
|
/**
|
|
534
576
|
* This file is part of helpers4.
|
|
535
577
|
* Copyright (C) 2025 baxyz
|
|
@@ -565,6 +607,46 @@ declare function isNonEmpty(value: Record<PropertyKey, unknown> | null | undefin
|
|
|
565
607
|
*/
|
|
566
608
|
declare function map<TObj extends Record<string, unknown>, TVal = TObj[keyof TObj], TKey extends PropertyKey = keyof TObj>(obj: TObj | null | undefined, mapValue?: (value: TObj[keyof TObj], key: keyof TObj) => TVal, mapKey?: (key: keyof TObj, value: TObj[keyof TObj]) => TKey): Record<TKey, TVal>;
|
|
567
609
|
|
|
610
|
+
/**
|
|
611
|
+
* This file is part of helpers4.
|
|
612
|
+
* Copyright (C) 2025 baxyz
|
|
613
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
614
|
+
*/
|
|
615
|
+
/**
|
|
616
|
+
* Recursively transforms the keys and/or values of a plain object — the deep counterpart to
|
|
617
|
+
* {@link map}, which only transforms the top level. Descends into nested plain objects and
|
|
618
|
+
* arrays of them; `Date`, `Map`, `Set`, class instances, and primitives are left untouched
|
|
619
|
+
* (only their position is walked into, matching {@link cloneDeep}'s notion of what counts
|
|
620
|
+
* as a plain-data node worth recursing into).
|
|
621
|
+
*
|
|
622
|
+
* Both callbacks are optional and default to identity (no transformation). `mapKey` is called
|
|
623
|
+
* with the *original* key and value (before any recursive value transform); `mapValue` receives
|
|
624
|
+
* the already-recursed value. Entries whose mapped key is a prototype-polluting string
|
|
625
|
+
* (`__proto__`, `constructor`, `prototype`) are silently skipped, same as {@link map}.
|
|
626
|
+
*
|
|
627
|
+
* Array elements also go through `mapValue` (with their stringified index as `key`, since
|
|
628
|
+
* arrays have no property keys of their own) — so values inside a plain array, not just object
|
|
629
|
+
* properties, get transformed too. `mapKey` is never called for array elements, since there is
|
|
630
|
+
* no key to rename.
|
|
631
|
+
*
|
|
632
|
+
* @param obj - The value to transform (typically an object, or an array of objects)
|
|
633
|
+
* @param mapValue - Callback called with `(value, key)` for each entry's (already-recursed) value.
|
|
634
|
+
* For array elements, `key` is the element's index as a string. Defaults to identity.
|
|
635
|
+
* @param mapKey - Callback called with `(key, value)` for each object entry. Defaults to identity.
|
|
636
|
+
* @returns A new value with every plain-object key/value transformed
|
|
637
|
+
* @example
|
|
638
|
+
* mapDeep({ a: { b: 1 } }, v => (typeof v === 'number' ? v * 10 : v))
|
|
639
|
+
* // => { a: { b: 10 } }
|
|
640
|
+
* @example
|
|
641
|
+
* mapDeep({ user_name: 'Alice' }, undefined, key => key.toUpperCase())
|
|
642
|
+
* // => { USER_NAME: 'Alice' }
|
|
643
|
+
* @example
|
|
644
|
+
* mapDeep({ tags: [1, 2, 3] }, v => (typeof v === 'number' ? v * 10 : v))
|
|
645
|
+
* // => { tags: [10, 20, 30] }
|
|
646
|
+
* @since 3.0.3
|
|
647
|
+
*/
|
|
648
|
+
declare function mapDeep<T>(obj: T, mapValue?: (value: unknown, key: string) => unknown, mapKey?: (key: string, value: unknown) => PropertyKey): T;
|
|
649
|
+
|
|
568
650
|
/**
|
|
569
651
|
* This file is part of helpers4.
|
|
570
652
|
* Copyright (C) 2025 baxyz
|
|
@@ -700,6 +782,27 @@ declare function omitBy<T extends Record<string, unknown>>(obj: T | undefined |
|
|
|
700
782
|
*/
|
|
701
783
|
declare function parsePropertyPath(path: string): readonly (string | number)[];
|
|
702
784
|
|
|
785
|
+
/**
|
|
786
|
+
* This file is part of helpers4.
|
|
787
|
+
* Copyright (C) 2025 baxyz
|
|
788
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
789
|
+
*/
|
|
790
|
+
/**
|
|
791
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
792
|
+
* nested objects) to PascalCase. Handles camelCase, snake_case, kebab-case, and space-separated
|
|
793
|
+
* keys. Non-plain-object values — `Date`, `Map`, `Set`, class instances, primitives — are left
|
|
794
|
+
* untouched, only their position is walked into. An entry whose transformed key is a
|
|
795
|
+
* prototype-polluting string (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
796
|
+
* same as the rest of `@helpers4/object`.
|
|
797
|
+
* @param value - The object (or array of objects) to transform
|
|
798
|
+
* @returns A new value with every plain-object key converted to PascalCase
|
|
799
|
+
* @example
|
|
800
|
+
* pascalCaseKeys({ user_name: 'Alice', home_address: { zip_code: '12345' } })
|
|
801
|
+
* // => { UserName: 'Alice', HomeAddress: { ZipCode: '12345' } }
|
|
802
|
+
* @since 3.0.3
|
|
803
|
+
*/
|
|
804
|
+
declare function pascalCaseKeys<T>(value: T): T;
|
|
805
|
+
|
|
703
806
|
/**
|
|
704
807
|
* This file is part of helpers4.
|
|
705
808
|
* Copyright (C) 2025 baxyz
|
|
@@ -844,6 +947,76 @@ declare function safeJsonParse<T>(input: string, fallback: T): T;
|
|
|
844
947
|
*/
|
|
845
948
|
declare function set<T extends object, const P extends string | readonly PropertyKey[], V extends DeepGet<T, ParsePath<P>>>(obj: T, path: P, value: V): string extends P ? T : readonly PropertyKey[] extends P ? T : ContainsUnsafeStringKey<ParsePath<P>> extends true ? T : DeepSet<T, ParsePath<P>, V>;
|
|
846
949
|
|
|
950
|
+
/**
|
|
951
|
+
* This file is part of helpers4.
|
|
952
|
+
* Copyright (C) 2025 baxyz
|
|
953
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
954
|
+
*/
|
|
955
|
+
/**
|
|
956
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
957
|
+
* nested objects) to snake_case. Handles camelCase, PascalCase, kebab-case, and space-separated
|
|
958
|
+
* keys. Non-plain-object values — `Date`, `Map`, `Set`, class instances, primitives — are left
|
|
959
|
+
* untouched, only their position is walked into. An entry whose transformed key is a
|
|
960
|
+
* prototype-polluting string (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
961
|
+
* same as the rest of `@helpers4/object`.
|
|
962
|
+
* @param value - The object (or array of objects) to transform
|
|
963
|
+
* @returns A new value with every plain-object key converted to snake_case
|
|
964
|
+
* @example
|
|
965
|
+
* snakeCaseKeys({ userName: 'Alice', homeAddress: { zipCode: '12345' } })
|
|
966
|
+
* // => { user_name: 'Alice', home_address: { zip_code: '12345' } }
|
|
967
|
+
* @since 3.0.3
|
|
968
|
+
*/
|
|
969
|
+
declare function snakeCaseKeys<T>(value: T): T;
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* This file is part of helpers4.
|
|
973
|
+
* Copyright (C) 2025 baxyz
|
|
974
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
975
|
+
*/
|
|
976
|
+
/**
|
|
977
|
+
* Creates a new object with the same entries as the input, but with its own keys sorted.
|
|
978
|
+
* Shallow only — nested objects are copied as-is, their keys are not re-sorted.
|
|
979
|
+
* Key order matters for things like `JSON.stringify` diffs and snapshot stability; this does
|
|
980
|
+
* not change equality (`{a:1,b:2}` and `{b:2,a:1}` are already equal), just iteration/serialization order.
|
|
981
|
+
* Note: integer-index-like keys (`"0"`, `"1"`, `"42"`...) are always iterated first, in numeric
|
|
982
|
+
* order, by the JS engine itself — no object key ordering can override that language behavior.
|
|
983
|
+
* A prototype-polluting key (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
984
|
+
* same as the rest of `@helpers4/object`.
|
|
985
|
+
* @param obj - The object whose keys to sort
|
|
986
|
+
* @param compareFn - Optional custom comparator, passed directly to `Array.prototype.sort`
|
|
987
|
+
* @returns A new object with the same entries, own keys in sorted order
|
|
988
|
+
* @example
|
|
989
|
+
* sortKeys({ b: 2, a: 1, c: 3 })
|
|
990
|
+
* // => { a: 1, b: 2, c: 3 }
|
|
991
|
+
* @since 3.0.3
|
|
992
|
+
*/
|
|
993
|
+
declare function sortKeys<T extends Record<string, unknown>>(obj: T, compareFn?: (a: string, b: string) => number): T;
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* This file is part of helpers4.
|
|
997
|
+
* Copyright (C) 2025 baxyz
|
|
998
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
999
|
+
*/
|
|
1000
|
+
/**
|
|
1001
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
1002
|
+
* nested objects) to Title Case. Handles camelCase, snake_case, kebab-case, and space-separated
|
|
1003
|
+
* keys. Non-plain-object values — `Date`, `Map`, `Set`, class instances, primitives — are left
|
|
1004
|
+
* untouched, only their position is walked into. An entry whose transformed key is a
|
|
1005
|
+
* prototype-polluting string (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
1006
|
+
* same as the rest of `@helpers4/object`.
|
|
1007
|
+
*
|
|
1008
|
+
* Note: unlike the other `*CaseKeys` variants, `titleCase` joins words with spaces (e.g.
|
|
1009
|
+
* `'User Name'`) — those keys are only usable via bracket access (`obj['User Name']`), not dot
|
|
1010
|
+
* notation. Useful for display-facing structures (form labels, table headers), not wire formats.
|
|
1011
|
+
* @param value - The object (or array of objects) to transform
|
|
1012
|
+
* @returns A new value with every plain-object key converted to Title Case
|
|
1013
|
+
* @example
|
|
1014
|
+
* titleCaseKeys({ user_name: 'Alice', home_address: { zip_code: '12345' } })
|
|
1015
|
+
* // => { 'User Name': 'Alice', 'Home Address': { 'Zip Code': '12345' } }
|
|
1016
|
+
* @since 3.0.3
|
|
1017
|
+
*/
|
|
1018
|
+
declare function titleCaseKeys<T>(value: T): T;
|
|
1019
|
+
|
|
847
1020
|
/**
|
|
848
1021
|
* This file is part of helpers4.
|
|
849
1022
|
* Copyright (C) 2025 baxyz
|
|
@@ -927,5 +1100,5 @@ declare function unset<T extends object>(obj: T, path: string | readonly Propert
|
|
|
927
1100
|
*/
|
|
928
1101
|
declare function update<T extends object, const P extends string | readonly PropertyKey[], V extends DeepGet<T, ParsePath<P>>>(obj: T, path: P, updater: (current: DeepGet<T, ParsePath<P>> | undefined) => V): string extends P ? T : readonly PropertyKey[] extends P ? T : ContainsUnsafeStringKey<ParsePath<P>> extends true ? T : DeepSet<T, ParsePath<P>, V>;
|
|
929
1102
|
|
|
930
|
-
export { clone, cloneDeep, compact, diff, equalsDeep, equalsShallow, flatten, get, groupBy, invert, isEmpty, isNonEmpty, map, mergeDeep, omit, omitBy, parsePropertyPath, pick, pickBy, removeUndefinedNull, safeJsonParse, set, unflatten, unset, update };
|
|
1103
|
+
export { camelCaseKeys, clone, cloneDeep, compact, diff, equalsDeep, equalsShallow, flatten, get, groupBy, invert, isEmpty, isNonEmpty, kebabCaseKeys, map, mapDeep, mergeDeep, omit, omitBy, parsePropertyPath, pascalCaseKeys, pick, pickBy, removeUndefinedNull, safeJsonParse, set, snakeCaseKeys, sortKeys, titleCaseKeys, unflatten, unset, update };
|
|
931
1104
|
export type { DiffOptions, DiffResult };
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,128 @@
|
|
|
1
|
+
//#region helpers/string/camelCase.ts
|
|
2
|
+
function camelCase(str) {
|
|
3
|
+
if (str === void 0 || str === null) return str;
|
|
4
|
+
const words = str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean);
|
|
5
|
+
if (words.length === 0) return "";
|
|
6
|
+
return words[0].toLowerCase() + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
7
|
+
}
|
|
1
8
|
var UNSAFE_KEYS = /* @__PURE__ */ new Set([
|
|
2
9
|
"__proto__",
|
|
3
10
|
"constructor",
|
|
4
11
|
"prototype"
|
|
5
12
|
]);
|
|
6
13
|
//#endregion
|
|
14
|
+
//#region helpers/guard/isPlainObject.ts
|
|
15
|
+
/**
|
|
16
|
+
* This file is part of helpers4.
|
|
17
|
+
* Copyright (C) 2025 baxyz
|
|
18
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a value is a plain object.
|
|
22
|
+
*
|
|
23
|
+
* A plain object is created by `{}`, `new Object()`, or `Object.create(null)`.
|
|
24
|
+
* Returns `false` for arrays, Date, Map, Set, RegExp, class instances, etc.
|
|
25
|
+
*
|
|
26
|
+
* @param value - The value to check
|
|
27
|
+
* @returns True if value is a plain object
|
|
28
|
+
* @example
|
|
29
|
+
* isPlainObject({}) // => true
|
|
30
|
+
* isPlainObject({ a: 1 }) // => true
|
|
31
|
+
* isPlainObject(new Date()) // => false
|
|
32
|
+
* isPlainObject([]) // => false
|
|
33
|
+
* isPlainObject(null) // => false
|
|
34
|
+
* @since 2.0.0
|
|
35
|
+
*/
|
|
36
|
+
function isPlainObject(value) {
|
|
37
|
+
if (value === null || typeof value !== "object") return false;
|
|
38
|
+
const proto = Object.getPrototypeOf(value);
|
|
39
|
+
return proto === Object.prototype || proto === null;
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region helpers/object/mapDeep.ts
|
|
43
|
+
/**
|
|
44
|
+
* This file is part of helpers4.
|
|
45
|
+
* Copyright (C) 2025 baxyz
|
|
46
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
47
|
+
*/
|
|
48
|
+
function transform(value, mapValue, mapKey) {
|
|
49
|
+
if (Array.isArray(value)) return value.map((item, index) => {
|
|
50
|
+
const recursed = transform(item, mapValue, mapKey);
|
|
51
|
+
return mapValue ? mapValue(recursed, String(index)) : recursed;
|
|
52
|
+
});
|
|
53
|
+
if (isPlainObject(value)) {
|
|
54
|
+
const result = {};
|
|
55
|
+
for (const [key, val] of Object.entries(value)) {
|
|
56
|
+
const newKey = mapKey ? mapKey(key, val) : key;
|
|
57
|
+
if (typeof newKey === "string" && UNSAFE_KEYS.has(newKey)) continue;
|
|
58
|
+
const recursed = transform(val, mapValue, mapKey);
|
|
59
|
+
result[newKey] = mapValue ? mapValue(recursed, key) : recursed;
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Recursively transforms the keys and/or values of a plain object — the deep counterpart to
|
|
67
|
+
* {@link map}, which only transforms the top level. Descends into nested plain objects and
|
|
68
|
+
* arrays of them; `Date`, `Map`, `Set`, class instances, and primitives are left untouched
|
|
69
|
+
* (only their position is walked into, matching {@link cloneDeep}'s notion of what counts
|
|
70
|
+
* as a plain-data node worth recursing into).
|
|
71
|
+
*
|
|
72
|
+
* Both callbacks are optional and default to identity (no transformation). `mapKey` is called
|
|
73
|
+
* with the *original* key and value (before any recursive value transform); `mapValue` receives
|
|
74
|
+
* the already-recursed value. Entries whose mapped key is a prototype-polluting string
|
|
75
|
+
* (`__proto__`, `constructor`, `prototype`) are silently skipped, same as {@link map}.
|
|
76
|
+
*
|
|
77
|
+
* Array elements also go through `mapValue` (with their stringified index as `key`, since
|
|
78
|
+
* arrays have no property keys of their own) — so values inside a plain array, not just object
|
|
79
|
+
* properties, get transformed too. `mapKey` is never called for array elements, since there is
|
|
80
|
+
* no key to rename.
|
|
81
|
+
*
|
|
82
|
+
* @param obj - The value to transform (typically an object, or an array of objects)
|
|
83
|
+
* @param mapValue - Callback called with `(value, key)` for each entry's (already-recursed) value.
|
|
84
|
+
* For array elements, `key` is the element's index as a string. Defaults to identity.
|
|
85
|
+
* @param mapKey - Callback called with `(key, value)` for each object entry. Defaults to identity.
|
|
86
|
+
* @returns A new value with every plain-object key/value transformed
|
|
87
|
+
* @example
|
|
88
|
+
* mapDeep({ a: { b: 1 } }, v => (typeof v === 'number' ? v * 10 : v))
|
|
89
|
+
* // => { a: { b: 10 } }
|
|
90
|
+
* @example
|
|
91
|
+
* mapDeep({ user_name: 'Alice' }, undefined, key => key.toUpperCase())
|
|
92
|
+
* // => { USER_NAME: 'Alice' }
|
|
93
|
+
* @example
|
|
94
|
+
* mapDeep({ tags: [1, 2, 3] }, v => (typeof v === 'number' ? v * 10 : v))
|
|
95
|
+
* // => { tags: [10, 20, 30] }
|
|
96
|
+
* @since 3.0.3
|
|
97
|
+
*/
|
|
98
|
+
function mapDeep(obj, mapValue, mapKey) {
|
|
99
|
+
return transform(obj, mapValue, mapKey);
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region helpers/object/camelCaseKeys.ts
|
|
103
|
+
/**
|
|
104
|
+
* This file is part of helpers4.
|
|
105
|
+
* Copyright (C) 2025 baxyz
|
|
106
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
107
|
+
*/
|
|
108
|
+
/**
|
|
109
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
110
|
+
* nested objects) to camelCase. Handles snake_case, kebab-case, PascalCase, and space-separated
|
|
111
|
+
* keys. Non-plain-object values (arrays' items aside) — `Date`, `Map`, `Set`, class instances,
|
|
112
|
+
* primitives — are left untouched, only their position is walked into. An entry whose
|
|
113
|
+
* transformed key is a prototype-polluting string (`__proto__`, `constructor`, `prototype`) is
|
|
114
|
+
* silently skipped, same as the rest of `@helpers4/object`.
|
|
115
|
+
* @param value - The object (or array of objects) to transform
|
|
116
|
+
* @returns A new value with every plain-object key converted to camelCase
|
|
117
|
+
* @example
|
|
118
|
+
* camelCaseKeys({ user_name: 'Alice', home_address: { zip_code: '12345' } })
|
|
119
|
+
* // => { userName: 'Alice', homeAddress: { zipCode: '12345' } }
|
|
120
|
+
* @since 3.0.3
|
|
121
|
+
*/
|
|
122
|
+
function camelCaseKeys(value) {
|
|
123
|
+
return mapDeep(value, void 0, camelCase);
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
7
126
|
//#region helpers/object/clone.ts
|
|
8
127
|
/**
|
|
9
128
|
* This file is part of helpers4.
|
|
@@ -98,34 +217,6 @@ function compact(obj) {
|
|
|
98
217
|
return result;
|
|
99
218
|
}
|
|
100
219
|
//#endregion
|
|
101
|
-
//#region helpers/guard/isPlainObject.ts
|
|
102
|
-
/**
|
|
103
|
-
* This file is part of helpers4.
|
|
104
|
-
* Copyright (C) 2025 baxyz
|
|
105
|
-
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
106
|
-
*/
|
|
107
|
-
/**
|
|
108
|
-
* Checks if a value is a plain object.
|
|
109
|
-
*
|
|
110
|
-
* A plain object is created by `{}`, `new Object()`, or `Object.create(null)`.
|
|
111
|
-
* Returns `false` for arrays, Date, Map, Set, RegExp, class instances, etc.
|
|
112
|
-
*
|
|
113
|
-
* @param value - The value to check
|
|
114
|
-
* @returns True if value is a plain object
|
|
115
|
-
* @example
|
|
116
|
-
* isPlainObject({}) // => true
|
|
117
|
-
* isPlainObject({ a: 1 }) // => true
|
|
118
|
-
* isPlainObject(new Date()) // => false
|
|
119
|
-
* isPlainObject([]) // => false
|
|
120
|
-
* isPlainObject(null) // => false
|
|
121
|
-
* @since 2.0.0
|
|
122
|
-
*/
|
|
123
|
-
function isPlainObject(value) {
|
|
124
|
-
if (value === null || typeof value !== "object") return false;
|
|
125
|
-
const proto = Object.getPrototypeOf(value);
|
|
126
|
-
return proto === Object.prototype || proto === null;
|
|
127
|
-
}
|
|
128
|
-
//#endregion
|
|
129
220
|
//#region helpers/array/equalsDeep.ts
|
|
130
221
|
/**
|
|
131
222
|
* This file is part of helpers4.
|
|
@@ -530,6 +621,32 @@ function flatten(obj) {
|
|
|
530
621
|
return result;
|
|
531
622
|
}
|
|
532
623
|
//#endregion
|
|
624
|
+
//#region helpers/_shared/_walkPropertyPath.ts
|
|
625
|
+
/**
|
|
626
|
+
* This file is part of helpers4.
|
|
627
|
+
* Copyright (C) 2025 baxyz
|
|
628
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
629
|
+
*/
|
|
630
|
+
/**
|
|
631
|
+
* Walks a value through a sequence of keys, returning the resolved value or `undefined` if any
|
|
632
|
+
* step along the way is missing or not an object. Pure traversal only — string-path parsing
|
|
633
|
+
* (`'a.b.c'` → keys) and default-value substitution are the caller's job.
|
|
634
|
+
*
|
|
635
|
+
* Shared by `object/get.ts` and any `*By`-style accessor elsewhere that needs the same
|
|
636
|
+
* "resolve a path against a value" runtime behavior — kept dependency-free (per the `_shared/`
|
|
637
|
+
* convention) so it can be used from any category without adding a cross-category import for
|
|
638
|
+
* this specific piece.
|
|
639
|
+
* @ignore
|
|
640
|
+
*/
|
|
641
|
+
function walkPropertyPath(value, keys) {
|
|
642
|
+
let result = value;
|
|
643
|
+
for (const key of keys) {
|
|
644
|
+
if (result == null || typeof result !== "object") return;
|
|
645
|
+
result = result[key];
|
|
646
|
+
}
|
|
647
|
+
return result;
|
|
648
|
+
}
|
|
649
|
+
//#endregion
|
|
533
650
|
//#region helpers/object/parsePropertyPath.ts
|
|
534
651
|
/**
|
|
535
652
|
* This file is part of helpers4.
|
|
@@ -600,12 +717,7 @@ function parseUncached(path) {
|
|
|
600
717
|
*/
|
|
601
718
|
function get(obj, path, defaultValue) {
|
|
602
719
|
if (obj == null) return defaultValue;
|
|
603
|
-
const
|
|
604
|
-
let result = obj;
|
|
605
|
-
for (const key of keys) {
|
|
606
|
-
if (result == null || typeof result !== "object") return defaultValue;
|
|
607
|
-
result = result[key];
|
|
608
|
-
}
|
|
720
|
+
const result = walkPropertyPath(obj, typeof path === "string" ? parsePropertyPath(path) : path);
|
|
609
721
|
return result !== void 0 ? result : defaultValue;
|
|
610
722
|
}
|
|
611
723
|
//#endregion
|
|
@@ -737,6 +849,36 @@ function isNonEmpty(value) {
|
|
|
737
849
|
return value != null && Object.keys(value).length > 0;
|
|
738
850
|
}
|
|
739
851
|
//#endregion
|
|
852
|
+
//#region helpers/string/kebabCase.ts
|
|
853
|
+
function kebabCase(str) {
|
|
854
|
+
if (str === void 0 || str === null) return str;
|
|
855
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean).join("-").toLowerCase();
|
|
856
|
+
}
|
|
857
|
+
//#endregion
|
|
858
|
+
//#region helpers/object/kebabCaseKeys.ts
|
|
859
|
+
/**
|
|
860
|
+
* This file is part of helpers4.
|
|
861
|
+
* Copyright (C) 2025 baxyz
|
|
862
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
863
|
+
*/
|
|
864
|
+
/**
|
|
865
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
866
|
+
* nested objects) to kebab-case. Handles snake_case, camelCase, PascalCase, and space-separated
|
|
867
|
+
* keys. Non-plain-object values — `Date`, `Map`, `Set`, class instances, primitives — are left
|
|
868
|
+
* untouched, only their position is walked into. An entry whose transformed key is a
|
|
869
|
+
* prototype-polluting string (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
870
|
+
* same as the rest of `@helpers4/object`.
|
|
871
|
+
* @param value - The object (or array of objects) to transform
|
|
872
|
+
* @returns A new value with every plain-object key converted to kebab-case
|
|
873
|
+
* @example
|
|
874
|
+
* kebabCaseKeys({ userName: 'Alice', homeAddress: { zipCode: '12345' } })
|
|
875
|
+
* // => { 'user-name': 'Alice', 'home-address': { 'zip-code': '12345' } }
|
|
876
|
+
* @since 3.0.3
|
|
877
|
+
*/
|
|
878
|
+
function kebabCaseKeys(value) {
|
|
879
|
+
return mapDeep(value, void 0, kebabCase);
|
|
880
|
+
}
|
|
881
|
+
//#endregion
|
|
740
882
|
//#region helpers/object/map.ts
|
|
741
883
|
/**
|
|
742
884
|
* This file is part of helpers4.
|
|
@@ -850,6 +992,36 @@ function omitBy(obj, predicate) {
|
|
|
850
992
|
return result;
|
|
851
993
|
}
|
|
852
994
|
//#endregion
|
|
995
|
+
//#region helpers/string/pascalCase.ts
|
|
996
|
+
function pascalCase(str) {
|
|
997
|
+
if (str === void 0 || str === null) return str;
|
|
998
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
999
|
+
}
|
|
1000
|
+
//#endregion
|
|
1001
|
+
//#region helpers/object/pascalCaseKeys.ts
|
|
1002
|
+
/**
|
|
1003
|
+
* This file is part of helpers4.
|
|
1004
|
+
* Copyright (C) 2025 baxyz
|
|
1005
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
1006
|
+
*/
|
|
1007
|
+
/**
|
|
1008
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
1009
|
+
* nested objects) to PascalCase. Handles camelCase, snake_case, kebab-case, and space-separated
|
|
1010
|
+
* keys. Non-plain-object values — `Date`, `Map`, `Set`, class instances, primitives — are left
|
|
1011
|
+
* untouched, only their position is walked into. An entry whose transformed key is a
|
|
1012
|
+
* prototype-polluting string (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
1013
|
+
* same as the rest of `@helpers4/object`.
|
|
1014
|
+
* @param value - The object (or array of objects) to transform
|
|
1015
|
+
* @returns A new value with every plain-object key converted to PascalCase
|
|
1016
|
+
* @example
|
|
1017
|
+
* pascalCaseKeys({ user_name: 'Alice', home_address: { zip_code: '12345' } })
|
|
1018
|
+
* // => { UserName: 'Alice', HomeAddress: { ZipCode: '12345' } }
|
|
1019
|
+
* @since 3.0.3
|
|
1020
|
+
*/
|
|
1021
|
+
function pascalCaseKeys(value) {
|
|
1022
|
+
return mapDeep(value, void 0, pascalCase);
|
|
1023
|
+
}
|
|
1024
|
+
//#endregion
|
|
853
1025
|
//#region helpers/object/pick.ts
|
|
854
1026
|
/**
|
|
855
1027
|
* This file is part of helpers4.
|
|
@@ -971,6 +1143,103 @@ function set(obj, path, value) {
|
|
|
971
1143
|
return obj;
|
|
972
1144
|
}
|
|
973
1145
|
//#endregion
|
|
1146
|
+
//#region helpers/string/snakeCase.ts
|
|
1147
|
+
function snakeCase(str) {
|
|
1148
|
+
if (str === void 0 || str === null) return str;
|
|
1149
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
|
|
1150
|
+
}
|
|
1151
|
+
//#endregion
|
|
1152
|
+
//#region helpers/object/snakeCaseKeys.ts
|
|
1153
|
+
/**
|
|
1154
|
+
* This file is part of helpers4.
|
|
1155
|
+
* Copyright (C) 2025 baxyz
|
|
1156
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
1157
|
+
*/
|
|
1158
|
+
/**
|
|
1159
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
1160
|
+
* nested objects) to snake_case. Handles camelCase, PascalCase, kebab-case, and space-separated
|
|
1161
|
+
* keys. Non-plain-object values — `Date`, `Map`, `Set`, class instances, primitives — are left
|
|
1162
|
+
* untouched, only their position is walked into. An entry whose transformed key is a
|
|
1163
|
+
* prototype-polluting string (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
1164
|
+
* same as the rest of `@helpers4/object`.
|
|
1165
|
+
* @param value - The object (or array of objects) to transform
|
|
1166
|
+
* @returns A new value with every plain-object key converted to snake_case
|
|
1167
|
+
* @example
|
|
1168
|
+
* snakeCaseKeys({ userName: 'Alice', homeAddress: { zipCode: '12345' } })
|
|
1169
|
+
* // => { user_name: 'Alice', home_address: { zip_code: '12345' } }
|
|
1170
|
+
* @since 3.0.3
|
|
1171
|
+
*/
|
|
1172
|
+
function snakeCaseKeys(value) {
|
|
1173
|
+
return mapDeep(value, void 0, snakeCase);
|
|
1174
|
+
}
|
|
1175
|
+
//#endregion
|
|
1176
|
+
//#region helpers/object/sortKeys.ts
|
|
1177
|
+
/**
|
|
1178
|
+
* This file is part of helpers4.
|
|
1179
|
+
* Copyright (C) 2025 baxyz
|
|
1180
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
1181
|
+
*/
|
|
1182
|
+
/**
|
|
1183
|
+
* Creates a new object with the same entries as the input, but with its own keys sorted.
|
|
1184
|
+
* Shallow only — nested objects are copied as-is, their keys are not re-sorted.
|
|
1185
|
+
* Key order matters for things like `JSON.stringify` diffs and snapshot stability; this does
|
|
1186
|
+
* not change equality (`{a:1,b:2}` and `{b:2,a:1}` are already equal), just iteration/serialization order.
|
|
1187
|
+
* Note: integer-index-like keys (`"0"`, `"1"`, `"42"`...) are always iterated first, in numeric
|
|
1188
|
+
* order, by the JS engine itself — no object key ordering can override that language behavior.
|
|
1189
|
+
* A prototype-polluting key (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
1190
|
+
* same as the rest of `@helpers4/object`.
|
|
1191
|
+
* @param obj - The object whose keys to sort
|
|
1192
|
+
* @param compareFn - Optional custom comparator, passed directly to `Array.prototype.sort`
|
|
1193
|
+
* @returns A new object with the same entries, own keys in sorted order
|
|
1194
|
+
* @example
|
|
1195
|
+
* sortKeys({ b: 2, a: 1, c: 3 })
|
|
1196
|
+
* // => { a: 1, b: 2, c: 3 }
|
|
1197
|
+
* @since 3.0.3
|
|
1198
|
+
*/
|
|
1199
|
+
function sortKeys(obj, compareFn) {
|
|
1200
|
+
const sortedKeys = Object.keys(obj).toSorted(compareFn);
|
|
1201
|
+
const result = {};
|
|
1202
|
+
for (const key of sortedKeys) {
|
|
1203
|
+
if (UNSAFE_KEYS.has(key)) continue;
|
|
1204
|
+
result[key] = obj[key];
|
|
1205
|
+
}
|
|
1206
|
+
return result;
|
|
1207
|
+
}
|
|
1208
|
+
//#endregion
|
|
1209
|
+
//#region helpers/string/titleCase.ts
|
|
1210
|
+
function titleCase(str) {
|
|
1211
|
+
if (str === void 0 || str === null) return str;
|
|
1212
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
1213
|
+
}
|
|
1214
|
+
//#endregion
|
|
1215
|
+
//#region helpers/object/titleCaseKeys.ts
|
|
1216
|
+
/**
|
|
1217
|
+
* This file is part of helpers4.
|
|
1218
|
+
* Copyright (C) 2025 baxyz
|
|
1219
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
1220
|
+
*/
|
|
1221
|
+
/**
|
|
1222
|
+
* Recursively transforms every key of a plain object (including keys nested inside arrays and
|
|
1223
|
+
* nested objects) to Title Case. Handles camelCase, snake_case, kebab-case, and space-separated
|
|
1224
|
+
* keys. Non-plain-object values — `Date`, `Map`, `Set`, class instances, primitives — are left
|
|
1225
|
+
* untouched, only their position is walked into. An entry whose transformed key is a
|
|
1226
|
+
* prototype-polluting string (`__proto__`, `constructor`, `prototype`) is silently skipped,
|
|
1227
|
+
* same as the rest of `@helpers4/object`.
|
|
1228
|
+
*
|
|
1229
|
+
* Note: unlike the other `*CaseKeys` variants, `titleCase` joins words with spaces (e.g.
|
|
1230
|
+
* `'User Name'`) — those keys are only usable via bracket access (`obj['User Name']`), not dot
|
|
1231
|
+
* notation. Useful for display-facing structures (form labels, table headers), not wire formats.
|
|
1232
|
+
* @param value - The object (or array of objects) to transform
|
|
1233
|
+
* @returns A new value with every plain-object key converted to Title Case
|
|
1234
|
+
* @example
|
|
1235
|
+
* titleCaseKeys({ user_name: 'Alice', home_address: { zip_code: '12345' } })
|
|
1236
|
+
* // => { 'User Name': 'Alice', 'Home Address': { 'Zip Code': '12345' } }
|
|
1237
|
+
* @since 3.0.3
|
|
1238
|
+
*/
|
|
1239
|
+
function titleCaseKeys(value) {
|
|
1240
|
+
return mapDeep(value, void 0, titleCase);
|
|
1241
|
+
}
|
|
1242
|
+
//#endregion
|
|
974
1243
|
//#region helpers/object/unflatten.ts
|
|
975
1244
|
/**
|
|
976
1245
|
* This file is part of helpers4.
|
|
@@ -1050,6 +1319,6 @@ function update(obj, path, updater) {
|
|
|
1050
1319
|
return set(obj, path, updater(get(obj, path)));
|
|
1051
1320
|
}
|
|
1052
1321
|
//#endregion
|
|
1053
|
-
export { clone, cloneDeep, compact, diff, equalsDeep, equalsShallow, flatten, get, groupBy, invert, isEmpty, isNonEmpty, map, mergeDeep, omit, omitBy, parsePropertyPath, pick, pickBy, removeUndefinedNull, safeJsonParse, set, unflatten, unset, update };
|
|
1322
|
+
export { camelCaseKeys, clone, cloneDeep, compact, diff, equalsDeep, equalsShallow, flatten, get, groupBy, invert, isEmpty, isNonEmpty, kebabCaseKeys, map, mapDeep, mergeDeep, omit, omitBy, parsePropertyPath, pascalCaseKeys, pick, pickBy, removeUndefinedNull, safeJsonParse, set, snakeCaseKeys, sortKeys, titleCaseKeys, unflatten, unset, update };
|
|
1054
1323
|
|
|
1055
1324
|
//# sourceMappingURL=index.js.map
|