@helpers4/object 2.0.0-alpha.8 → 2.0.0-beta.0
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 -5
- package/lib/index.d.ts +92 -11
- package/lib/index.js +227 -45
- package/lib/index.js.map +1 -1
- package/llms.txt +577 -0
- package/meta/api.json +400 -37
- package/meta/category.json +6 -0
- package/meta/examples.json +69 -4
- package/meta/licenses.json +1 -8
- package/meta/native-alternatives.json +50 -0
- package/package.json +10 -6
package/README.md
CHANGED
|
@@ -12,19 +12,24 @@ 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
|
+
- compact
|
|
15
16
|
- deepClone
|
|
16
17
|
- deepCompare
|
|
17
18
|
- deepMerge
|
|
18
19
|
- get
|
|
19
|
-
-
|
|
20
|
+
- omit
|
|
21
|
+
- pick
|
|
20
22
|
- removeUndefinedNull
|
|
23
|
+
- safeJsonParse
|
|
21
24
|
- set
|
|
25
|
+
- shallowEquals
|
|
22
26
|
<!-- /AUTOMATIC-METHODS -->
|
|
23
27
|
|
|
24
28
|
## See
|
|
25
29
|
|
|
26
30
|
<!-- AUTOMATIC-SIBLINGS -->
|
|
27
31
|
- [array](../array)
|
|
32
|
+
- [commit](../commit)
|
|
28
33
|
- [date](../date)
|
|
29
34
|
- [function](../function)
|
|
30
35
|
- [math](../math)
|
|
@@ -43,15 +48,16 @@ A set of helpers for working with URLs.
|
|
|
43
48
|
| Name | Package | Source Code | Description |
|
|
44
49
|
|------|---------|-------------|-------------|
|
|
45
50
|
| array | [@helpers4/array](https://www.npmjs.com/package/@helpers4/array) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/array) | Array manipulation utilities |
|
|
46
|
-
|
|
|
51
|
+
| commit | [@helpers4/commit](https://www.npmjs.com/package/@helpers4/commit) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/commit) | Conventional Commits parsing and analysis |
|
|
52
|
+
| date | [@helpers4/date](https://www.npmjs.com/package/@helpers4/date) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/date) | Date and time utility functions |
|
|
47
53
|
| 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 |
|
|
48
|
-
| math | [@helpers4/math](https://www.npmjs.com/package/@helpers4/math) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/math) |
|
|
49
|
-
| number | [@helpers4/number](https://www.npmjs.com/package/@helpers4/number) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/number) |
|
|
54
|
+
| math | [@helpers4/math](https://www.npmjs.com/package/@helpers4/math) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/math) | Mathematical utility functions and calculations |
|
|
55
|
+
| 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 |
|
|
50
56
|
| object | [@helpers4/object](https://www.npmjs.com/package/@helpers4/object) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/object) | Object manipulation utilities |
|
|
51
57
|
| observable | [@helpers4/observable](https://www.npmjs.com/package/@helpers4/observable) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/observable) | Observable utilities and combinators |
|
|
52
58
|
| 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 |
|
|
53
59
|
| 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 |
|
|
54
|
-
| type | [@helpers4/type](https://www.npmjs.com/package/@helpers4/type) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/type) |
|
|
60
|
+
| type | [@helpers4/type](https://www.npmjs.com/package/@helpers4/type) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/type) | Type checking and validation utilities |
|
|
55
61
|
| 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 |
|
|
56
62
|
| version | [@helpers4/version](https://www.npmjs.com/package/@helpers4/version) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/version) | Version string manipulation utilities |
|
|
57
63
|
<!-- /AUTOMATIC-CATEGORIES-TABLE -->
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
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
|
+
* Removes all entries with falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`) from an object.
|
|
8
|
+
* @param obj - The source object
|
|
9
|
+
* @returns A new object containing only entries with truthy values
|
|
10
|
+
* @example
|
|
11
|
+
* compact({ a: 1, b: null, c: '', d: 0, e: 'hello' })
|
|
12
|
+
* // => { a: 1, e: 'hello' }
|
|
13
|
+
* @since 2.0.0
|
|
14
|
+
*/
|
|
15
|
+
declare function compact<T extends Record<string, unknown>>(obj: T): Partial<T>;
|
|
16
|
+
declare function compact(obj: undefined): undefined;
|
|
17
|
+
declare function compact(obj: null): null;
|
|
18
|
+
|
|
1
19
|
/**
|
|
2
20
|
* This file is part of helpers4.
|
|
3
21
|
* Copyright (C) 2025 baxyz
|
|
@@ -45,7 +63,9 @@ declare function deepCompare(objA: object | undefined | null, objB: object | und
|
|
|
45
63
|
* @returns The merged object
|
|
46
64
|
* @since 1.9.0
|
|
47
65
|
*/
|
|
48
|
-
declare function deepMerge<T extends Record<string,
|
|
66
|
+
declare function deepMerge<T extends Record<string, unknown>>(target: T, ...sources: Record<string, unknown>[]): T;
|
|
67
|
+
declare function deepMerge(target: undefined, ...sources: Record<string, unknown>[]): undefined;
|
|
68
|
+
declare function deepMerge(target: null, ...sources: Record<string, unknown>[]): null;
|
|
49
69
|
|
|
50
70
|
/**
|
|
51
71
|
* This file is part of helpers4.
|
|
@@ -60,7 +80,7 @@ declare function deepMerge<T extends Record<string, any>>(target: T, ...sources:
|
|
|
60
80
|
* @returns The value at the path or default value
|
|
61
81
|
* @since 1.9.0
|
|
62
82
|
*/
|
|
63
|
-
declare function get<T =
|
|
83
|
+
declare function get<T = unknown>(obj: unknown, path: string, defaultValue?: T): T | undefined;
|
|
64
84
|
|
|
65
85
|
/**
|
|
66
86
|
* This file is part of helpers4.
|
|
@@ -68,15 +88,37 @@ declare function get<T = any>(obj: any, path: string, defaultValue?: T): T | und
|
|
|
68
88
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
69
89
|
*/
|
|
70
90
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @
|
|
75
|
-
* @
|
|
76
|
-
*
|
|
91
|
+
* Creates a new object without the specified keys.
|
|
92
|
+
* @param obj - The source object
|
|
93
|
+
* @param keys - The keys to omit
|
|
94
|
+
* @returns A new object without the omitted keys
|
|
95
|
+
* @example
|
|
96
|
+
* omit({ a: 1, b: 2, c: 3 }, ['b'])
|
|
97
|
+
* // => { a: 1, c: 3 }
|
|
98
|
+
* @since 2.0.0
|
|
99
|
+
*/
|
|
100
|
+
declare function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: readonly K[]): Omit<T, K>;
|
|
101
|
+
declare function omit(obj: undefined, keys: readonly string[]): undefined;
|
|
102
|
+
declare function omit(obj: null, keys: readonly string[]): null;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* This file is part of helpers4.
|
|
106
|
+
* Copyright (C) 2025 baxyz
|
|
107
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
108
|
+
*/
|
|
109
|
+
/**
|
|
110
|
+
* Creates a new object with only the specified keys.
|
|
111
|
+
* @param obj - The source object
|
|
112
|
+
* @param keys - The keys to pick
|
|
113
|
+
* @returns A new object with only the picked keys
|
|
114
|
+
* @example
|
|
115
|
+
* pick({ a: 1, b: 2, c: 3 }, ['a', 'c'])
|
|
116
|
+
* // => { a: 1, c: 3 }
|
|
77
117
|
* @since 2.0.0
|
|
78
118
|
*/
|
|
79
|
-
declare function
|
|
119
|
+
declare function pick<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: readonly K[]): Pick<T, K>;
|
|
120
|
+
declare function pick(obj: undefined, keys: readonly string[]): undefined;
|
|
121
|
+
declare function pick(obj: null, keys: readonly string[]): null;
|
|
80
122
|
|
|
81
123
|
/**
|
|
82
124
|
* This file is part of helpers4.
|
|
@@ -108,6 +150,29 @@ declare function removeUndefinedNull(obj: null): null;
|
|
|
108
150
|
*/
|
|
109
151
|
declare function removeUndefinedNull(obj: undefined): undefined;
|
|
110
152
|
|
|
153
|
+
/**
|
|
154
|
+
* This file is part of helpers4.
|
|
155
|
+
* Copyright (C) 2025 baxyz
|
|
156
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
157
|
+
*/
|
|
158
|
+
/**
|
|
159
|
+
* Parses a JSON string, returning `null` (or a fallback) on any parse failure.
|
|
160
|
+
*
|
|
161
|
+
* Unlike `JSON.parse`, this never throws. Invalid JSON strings and other
|
|
162
|
+
* parsing edge-cases resolve to `null` or the provided `fallback`.
|
|
163
|
+
*
|
|
164
|
+
* @param input - The JSON string to parse.
|
|
165
|
+
* @param fallback - Value returned on failure. Defaults to `null` when omitted.
|
|
166
|
+
* @returns The parsed value typed as `T`, or `fallback` on failure.
|
|
167
|
+
* @example
|
|
168
|
+
* safeJsonParse<{ a: number }>('{"a":1}') // => { a: 1 }
|
|
169
|
+
* safeJsonParse('invalid') // => null
|
|
170
|
+
* safeJsonParse('invalid', []) // => []
|
|
171
|
+
* @since next
|
|
172
|
+
*/
|
|
173
|
+
declare function safeJsonParse<T>(input: string): T | null;
|
|
174
|
+
declare function safeJsonParse<T>(input: string, fallback: T): T;
|
|
175
|
+
|
|
111
176
|
/**
|
|
112
177
|
* This file is part of helpers4.
|
|
113
178
|
* Copyright (C) 2025 baxyz
|
|
@@ -121,7 +186,23 @@ declare function removeUndefinedNull(obj: undefined): undefined;
|
|
|
121
186
|
* @returns The modified object
|
|
122
187
|
* @since 1.9.0
|
|
123
188
|
*/
|
|
124
|
-
declare function set(obj: Record<string,
|
|
189
|
+
declare function set(obj: Record<string, unknown>, path: string, value: unknown): Record<string, unknown>;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* This file is part of helpers4.
|
|
193
|
+
* Copyright (C) 2025 baxyz
|
|
194
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
195
|
+
*/
|
|
196
|
+
/**
|
|
197
|
+
* Quick comparison of two objects using JSON.stringify.
|
|
198
|
+
* This is a fast but simple comparison that may not work for all edge cases.
|
|
199
|
+
*
|
|
200
|
+
* @param objA - First object to compare
|
|
201
|
+
* @param objB - Second object to compare
|
|
202
|
+
* @returns `true` if objects are identical according to JSON.stringify, `false` otherwise
|
|
203
|
+
* @since 2.0.0
|
|
204
|
+
*/
|
|
205
|
+
declare function shallowEquals(objA: unknown, objB: unknown): boolean;
|
|
125
206
|
|
|
126
|
-
export { deepClone, deepCompare, deepMerge, get,
|
|
207
|
+
export { compact, deepClone, deepCompare, deepMerge, get, omit, pick, removeUndefinedNull, safeJsonParse, set, shallowEquals };
|
|
127
208
|
export type { DeepCompareResult };
|
package/lib/index.js
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
//#region helpers/object/compact.ts
|
|
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
|
|
2
9
|
//#region helpers/object/deepClone.ts
|
|
3
10
|
/**
|
|
4
11
|
* This file is part of helpers4.
|
|
5
12
|
* Copyright (C) 2025 baxyz
|
|
6
13
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
7
14
|
*/
|
|
15
|
+
var UNSAFE_KEYS$2 = new Set([
|
|
16
|
+
"__proto__",
|
|
17
|
+
"constructor",
|
|
18
|
+
"prototype"
|
|
19
|
+
]);
|
|
8
20
|
/**
|
|
9
21
|
* Creates a deep copy of an object or array
|
|
10
22
|
* @param obj - The object to clone
|
|
@@ -16,11 +28,11 @@ function deepClone(obj) {
|
|
|
16
28
|
if (obj instanceof Date) return new Date(obj.getTime());
|
|
17
29
|
if (Array.isArray(obj)) return obj.map((item) => deepClone(item));
|
|
18
30
|
const cloned = {};
|
|
19
|
-
for (const key in obj) if (
|
|
31
|
+
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key) && !UNSAFE_KEYS$2.has(key)) cloned[key] = deepClone(obj[key]);
|
|
20
32
|
return cloned;
|
|
21
33
|
}
|
|
22
34
|
//#endregion
|
|
23
|
-
//#region helpers/array/
|
|
35
|
+
//#region helpers/array/shallowEquals.ts
|
|
24
36
|
/**
|
|
25
37
|
* This file is part of helpers4.
|
|
26
38
|
* Copyright (C) 2025 baxyz
|
|
@@ -29,13 +41,13 @@ function deepClone(obj) {
|
|
|
29
41
|
/**
|
|
30
42
|
* Quick comparison of two arrays using JSON.stringify.
|
|
31
43
|
* This is a fast but simple comparison that may not work for all edge cases.
|
|
32
|
-
*
|
|
44
|
+
*
|
|
33
45
|
* @param arrA - First array to compare
|
|
34
46
|
* @param arrB - Second array to compare
|
|
35
47
|
* @returns `true` if arrays are identical according to JSON.stringify, `false` otherwise
|
|
36
48
|
* @since 2.0.0
|
|
37
49
|
*/
|
|
38
|
-
function
|
|
50
|
+
function shallowEquals$1(arrA, arrB) {
|
|
39
51
|
try {
|
|
40
52
|
return JSON.stringify(arrA) === JSON.stringify(arrB);
|
|
41
53
|
} catch {
|
|
@@ -43,26 +55,116 @@ function quickCompare$1(arrA, arrB) {
|
|
|
43
55
|
}
|
|
44
56
|
}
|
|
45
57
|
//#endregion
|
|
58
|
+
//#region helpers/date/timestamp.ts
|
|
59
|
+
/**
|
|
60
|
+
* Checks if a timestamp is likely in seconds (Java/Unix style) vs milliseconds (JavaScript style)
|
|
61
|
+
* @param timestamp - The timestamp to check
|
|
62
|
+
* @returns True if timestamp appears to be in seconds
|
|
63
|
+
* @since 1.9.0
|
|
64
|
+
*/
|
|
65
|
+
function isTimestampInSeconds(timestamp) {
|
|
66
|
+
return Math.abs(timestamp) < 1e10;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Converts a timestamp to JavaScript milliseconds format
|
|
70
|
+
* @param timestamp - The timestamp (in seconds or milliseconds)
|
|
71
|
+
* @returns Timestamp in milliseconds
|
|
72
|
+
* @since 1.9.0
|
|
73
|
+
*/
|
|
74
|
+
function normalizeTimestamp(timestamp) {
|
|
75
|
+
return isTimestampInSeconds(timestamp) ? timestamp * 1e3 : timestamp;
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region helpers/date/ensureDate.ts
|
|
79
|
+
/**
|
|
80
|
+
* This file is part of helpers4.
|
|
81
|
+
* Copyright (C) 2025 baxyz
|
|
82
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
83
|
+
*/
|
|
84
|
+
/**
|
|
85
|
+
* Type guard that checks whether a value satisfies the {@link EpochMilliseconds}
|
|
86
|
+
* structural interface (e.g. `Temporal.Instant`, `Temporal.ZonedDateTime`).
|
|
87
|
+
*/
|
|
88
|
+
function hasEpochMilliseconds(value) {
|
|
89
|
+
return typeof value === "object" && value !== null && "epochMilliseconds" in value && typeof value.epochMilliseconds === "number";
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Safely converts a date-like value to a valid `Date` object, or returns `null`.
|
|
93
|
+
*
|
|
94
|
+
* Accepts `Date`, timestamps (seconds or milliseconds, auto-detected), date strings,
|
|
95
|
+
* and objects with an `epochMilliseconds` property (e.g. `Temporal.Instant`,
|
|
96
|
+
* `Temporal.ZonedDateTime`).
|
|
97
|
+
* Returns `null` for `null`, `undefined`, empty strings, `0`, and any value that
|
|
98
|
+
* produces an invalid `Date`.
|
|
99
|
+
*
|
|
100
|
+
* This is the date equivalent of {@link ensureArray} — it normalizes flexible
|
|
101
|
+
* input into a guaranteed type (or a safe fallback).
|
|
102
|
+
*
|
|
103
|
+
* @param input - A date-like value to convert
|
|
104
|
+
* @returns A valid `Date` object, or `null` if the input is invalid
|
|
105
|
+
* @example
|
|
106
|
+
* ensureDate('2025-01-19T12:00:00Z') // => Date
|
|
107
|
+
* ensureDate(1737290400) // => Date (from Unix seconds)
|
|
108
|
+
* ensureDate(1737290400000) // => Date (from milliseconds)
|
|
109
|
+
* ensureDate(new Date()) // => Date (same reference)
|
|
110
|
+
* ensureDate(null) // => null
|
|
111
|
+
* ensureDate('invalid') // => null
|
|
112
|
+
*
|
|
113
|
+
* @see {@link DateLike} for the accepted input types
|
|
114
|
+
* @since 2.0.0
|
|
115
|
+
*/
|
|
116
|
+
function ensureDate(input) {
|
|
117
|
+
if (input === null || input === void 0 || input === "" || input === 0) return null;
|
|
118
|
+
if (input instanceof Date) return isNaN(input.getTime()) ? null : input;
|
|
119
|
+
if (hasEpochMilliseconds(input)) {
|
|
120
|
+
const date = new Date(input.epochMilliseconds);
|
|
121
|
+
return isNaN(date.getTime()) ? null : date;
|
|
122
|
+
}
|
|
123
|
+
if (typeof input === "number") {
|
|
124
|
+
const normalized = normalizeTimestamp(input);
|
|
125
|
+
const date = new Date(normalized);
|
|
126
|
+
return isNaN(date.getTime()) ? null : date;
|
|
127
|
+
}
|
|
128
|
+
if (typeof input === "string") {
|
|
129
|
+
const date = new Date(input);
|
|
130
|
+
return isNaN(date.getTime()) ? null : date;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
46
135
|
//#region helpers/date/compare.ts
|
|
47
136
|
/**
|
|
137
|
+
* This file is part of helpers4.
|
|
138
|
+
* Copyright (C) 2025 baxyz
|
|
139
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
140
|
+
*/
|
|
141
|
+
/**
|
|
48
142
|
* Comparison of two dates.
|
|
49
|
-
*
|
|
143
|
+
*
|
|
144
|
+
* Accepts any {@link DateLike} input (Date, timestamp, or date string).
|
|
145
|
+
*
|
|
50
146
|
* @param dateA - First date to compare
|
|
51
147
|
* @param dateB - Second date to compare
|
|
52
148
|
* @param options - Comparison options
|
|
53
149
|
* @returns `true` if dates are identical according to the specified precision, `false` otherwise
|
|
150
|
+
* @example
|
|
151
|
+
* compare('2025-01-19', new Date('2025-01-19T08:00:00Z'), { precision: 'days' })
|
|
152
|
+
* // => true
|
|
54
153
|
* @since 2.0.0
|
|
55
154
|
*/
|
|
56
155
|
function compare(dateA, dateB, options = {}) {
|
|
57
156
|
const { precision = "milliseconds" } = options;
|
|
58
|
-
|
|
59
|
-
|
|
157
|
+
const a = ensureDate(dateA);
|
|
158
|
+
const b = ensureDate(dateB);
|
|
159
|
+
if (a === null || b === null) return false;
|
|
60
160
|
switch (precision) {
|
|
61
|
-
case "
|
|
62
|
-
case "
|
|
63
|
-
case "
|
|
64
|
-
case "
|
|
65
|
-
|
|
161
|
+
case "years": return a.getFullYear() === b.getFullYear();
|
|
162
|
+
case "months": return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
163
|
+
case "days": return a.toDateString() === b.toDateString();
|
|
164
|
+
case "hours": return Math.floor(a.getTime() / (1e3 * 60 * 60)) === Math.floor(b.getTime() / (1e3 * 60 * 60));
|
|
165
|
+
case "minutes": return Math.floor(a.getTime() / (1e3 * 60)) === Math.floor(b.getTime() / (1e3 * 60));
|
|
166
|
+
case "seconds": return Math.floor(a.getTime() / 1e3) === Math.floor(b.getTime() / 1e3);
|
|
167
|
+
default: return a.getTime() === b.getTime();
|
|
66
168
|
}
|
|
67
169
|
}
|
|
68
170
|
//#endregion
|
|
@@ -113,7 +215,7 @@ function isSpecialObject(value) {
|
|
|
113
215
|
*/
|
|
114
216
|
/**
|
|
115
217
|
* Deep comparison of two objects that returns detailed information about differences.
|
|
116
|
-
*
|
|
218
|
+
*
|
|
117
219
|
* @param objA - First object to compare (can be object, undefined, or null)
|
|
118
220
|
* @param objB - Second object to compare (can be object, undefined, or null)
|
|
119
221
|
* @returns `true` if objects are identical, `false` if incompatible types, or a `DeepCompareResult` object detailing differences
|
|
@@ -123,7 +225,7 @@ function deepCompare(objA, objB) {
|
|
|
123
225
|
if (objA === objB) return true;
|
|
124
226
|
if (objA == null || objB == null) return false;
|
|
125
227
|
if (objA instanceof Date && objB instanceof Date) return compare(objA, objB);
|
|
126
|
-
if (Array.isArray(objA) && Array.isArray(objB)) return
|
|
228
|
+
if (Array.isArray(objA) && Array.isArray(objB)) return shallowEquals$1(objA, objB);
|
|
127
229
|
if (Array.isArray(objA) || Array.isArray(objB)) return false;
|
|
128
230
|
if (isSpecialObject(objA) || isSpecialObject(objB)) return objA === objB;
|
|
129
231
|
const keys = Array.from(new Set([...Object.keys(objA), ...Object.keys(objB)]));
|
|
@@ -137,7 +239,7 @@ function deepCompare(objA, objB) {
|
|
|
137
239
|
const valueA = objA[key];
|
|
138
240
|
const valueB = objB[key];
|
|
139
241
|
if (Array.isArray(valueA) && Array.isArray(valueB)) {
|
|
140
|
-
if (!
|
|
242
|
+
if (!shallowEquals$1(valueA, valueB)) differences[key] = false;
|
|
141
243
|
} else if (valueA instanceof Date && valueB instanceof Date) {
|
|
142
244
|
if (!compare(valueA, valueB)) differences[key] = false;
|
|
143
245
|
} else if (isSpecialObject(valueA) || isSpecialObject(valueB)) {
|
|
@@ -151,34 +253,59 @@ function deepCompare(objA, objB) {
|
|
|
151
253
|
return Object.keys(differences).length > 0 ? differences : true;
|
|
152
254
|
}
|
|
153
255
|
//#endregion
|
|
154
|
-
//#region helpers/
|
|
256
|
+
//#region helpers/type/isPlainObject.ts
|
|
155
257
|
/**
|
|
156
258
|
* This file is part of helpers4.
|
|
157
259
|
* Copyright (C) 2025 baxyz
|
|
158
260
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
159
261
|
*/
|
|
160
262
|
/**
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
263
|
+
* Checks if a value is a plain object.
|
|
264
|
+
*
|
|
265
|
+
* A plain object is created by `{}`, `new Object()`, or `Object.create(null)`.
|
|
266
|
+
* Returns `false` for arrays, Date, Map, Set, RegExp, class instances, etc.
|
|
267
|
+
*
|
|
268
|
+
* @param value - The value to check
|
|
269
|
+
* @returns True if value is a plain object
|
|
270
|
+
* @example
|
|
271
|
+
* isPlainObject({}) // => true
|
|
272
|
+
* isPlainObject({ a: 1 }) // => true
|
|
273
|
+
* isPlainObject(new Date()) // => false
|
|
274
|
+
* isPlainObject([]) // => false
|
|
275
|
+
* isPlainObject(null) // => false
|
|
276
|
+
* @since 2.0.0
|
|
166
277
|
*/
|
|
278
|
+
function isPlainObject(value) {
|
|
279
|
+
if (value === null || typeof value !== "object") return false;
|
|
280
|
+
const proto = Object.getPrototypeOf(value);
|
|
281
|
+
return proto === Object.prototype || proto === null;
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region helpers/object/deepMerge.ts
|
|
285
|
+
/**
|
|
286
|
+
* This file is part of helpers4.
|
|
287
|
+
* Copyright (C) 2025 baxyz
|
|
288
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
289
|
+
*/
|
|
290
|
+
var UNSAFE_KEYS$1 = new Set([
|
|
291
|
+
"__proto__",
|
|
292
|
+
"constructor",
|
|
293
|
+
"prototype"
|
|
294
|
+
]);
|
|
167
295
|
function deepMerge(target, ...sources) {
|
|
296
|
+
if (target === void 0 || target === null) return target;
|
|
168
297
|
if (!sources.length) return target;
|
|
169
298
|
const source = sources.shift();
|
|
170
299
|
if (!source) return deepMerge(target, ...sources);
|
|
171
300
|
for (const key in source) {
|
|
301
|
+
if (!Object.hasOwn(source, key) || UNSAFE_KEYS$1.has(key)) continue;
|
|
172
302
|
const targetValue = target[key];
|
|
173
303
|
const sourceValue = source[key];
|
|
174
|
-
if (
|
|
304
|
+
if (isPlainObject(targetValue) && isPlainObject(sourceValue)) target[key] = deepMerge(targetValue, sourceValue);
|
|
175
305
|
else if (sourceValue !== void 0) target[key] = sourceValue;
|
|
176
306
|
}
|
|
177
307
|
return deepMerge(target, ...sources);
|
|
178
308
|
}
|
|
179
|
-
function isObject(item) {
|
|
180
|
-
return item && typeof item === "object" && !Array.isArray(item);
|
|
181
|
-
}
|
|
182
309
|
//#endregion
|
|
183
310
|
//#region helpers/object/get.ts
|
|
184
311
|
/**
|
|
@@ -204,29 +331,44 @@ function get(obj, path, defaultValue) {
|
|
|
204
331
|
return result !== void 0 ? result : defaultValue;
|
|
205
332
|
}
|
|
206
333
|
//#endregion
|
|
207
|
-
//#region helpers/object/
|
|
334
|
+
//#region helpers/object/omit.ts
|
|
335
|
+
function omit(obj, keys) {
|
|
336
|
+
if (obj === void 0 || obj === null) return obj;
|
|
337
|
+
const result = { ...obj };
|
|
338
|
+
for (const key of keys) delete result[key];
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
//#endregion
|
|
342
|
+
//#region helpers/object/pick.ts
|
|
343
|
+
function pick(obj, keys) {
|
|
344
|
+
if (obj === void 0 || obj === null) return obj;
|
|
345
|
+
const result = {};
|
|
346
|
+
for (const key of keys) if (Object.prototype.hasOwnProperty.call(obj, key)) result[key] = obj[key];
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
349
|
+
//#endregion
|
|
350
|
+
//#region helpers/type/isNullish.ts
|
|
208
351
|
/**
|
|
209
352
|
* This file is part of helpers4.
|
|
210
353
|
* Copyright (C) 2025 baxyz
|
|
211
354
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
212
355
|
*/
|
|
213
356
|
/**
|
|
214
|
-
*
|
|
215
|
-
*
|
|
357
|
+
* Checks if a value is null or undefined (nullish).
|
|
358
|
+
* @param value - The value to check
|
|
359
|
+
* @returns True if value is null or undefined
|
|
360
|
+
* @example
|
|
361
|
+
* isNullish(null) // => true
|
|
362
|
+
* isNullish(undefined) // => true
|
|
363
|
+
* isNullish(0) // => false
|
|
364
|
+
* isNullish('') // => false
|
|
216
365
|
*
|
|
217
|
-
* @
|
|
218
|
-
* @
|
|
219
|
-
* @returns `true` if objects are identical according to JSON.stringify, `false` otherwise
|
|
366
|
+
* @see {@link isDefined} for the inverse check (`!isNullish`)
|
|
367
|
+
* @see {@link isFalsy} to also catch `0`, `""`, `false`, and `NaN`
|
|
220
368
|
* @since 2.0.0
|
|
221
369
|
*/
|
|
222
|
-
function
|
|
223
|
-
|
|
224
|
-
if (typeof objA === "function" || typeof objB === "function") return objA === objB;
|
|
225
|
-
try {
|
|
226
|
-
return JSON.stringify(objA) === JSON.stringify(objB);
|
|
227
|
-
} catch {
|
|
228
|
-
return objA === objB;
|
|
229
|
-
}
|
|
370
|
+
function isNullish(value) {
|
|
371
|
+
return value === null || value === void 0;
|
|
230
372
|
}
|
|
231
373
|
//#endregion
|
|
232
374
|
//#region helpers/object/removeUndefinedNull.ts
|
|
@@ -243,10 +385,19 @@ function quickCompare(objA, objB) {
|
|
|
243
385
|
* @since 1.0.0
|
|
244
386
|
*/
|
|
245
387
|
function removeUndefinedNull(obj) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
388
|
+
if (!obj) return obj;
|
|
389
|
+
const result = {};
|
|
390
|
+
for (const [k, v] of Object.entries(obj)) if (!isNullish(v)) result[k] = v;
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
//#endregion
|
|
394
|
+
//#region helpers/object/safeJsonParse.ts
|
|
395
|
+
function safeJsonParse(input, fallback) {
|
|
396
|
+
try {
|
|
397
|
+
return JSON.parse(input);
|
|
398
|
+
} catch {
|
|
399
|
+
return arguments.length < 2 ? null : fallback;
|
|
400
|
+
}
|
|
250
401
|
}
|
|
251
402
|
//#endregion
|
|
252
403
|
//#region helpers/object/set.ts
|
|
@@ -255,6 +406,11 @@ function removeUndefinedNull(obj) {
|
|
|
255
406
|
* Copyright (C) 2025 baxyz
|
|
256
407
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
257
408
|
*/
|
|
409
|
+
var UNSAFE_KEYS = new Set([
|
|
410
|
+
"__proto__",
|
|
411
|
+
"constructor",
|
|
412
|
+
"prototype"
|
|
413
|
+
]);
|
|
258
414
|
/**
|
|
259
415
|
* Sets a value in an object using a dot-notated path
|
|
260
416
|
* @param obj - The object to set value in
|
|
@@ -265,6 +421,7 @@ function removeUndefinedNull(obj) {
|
|
|
265
421
|
*/
|
|
266
422
|
function set(obj, path, value) {
|
|
267
423
|
const keys = path.split(".");
|
|
424
|
+
if (keys.some((k) => UNSAFE_KEYS.has(k))) return obj;
|
|
268
425
|
let current = obj;
|
|
269
426
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
270
427
|
const key = keys[i];
|
|
@@ -275,6 +432,31 @@ function set(obj, path, value) {
|
|
|
275
432
|
return obj;
|
|
276
433
|
}
|
|
277
434
|
//#endregion
|
|
278
|
-
|
|
435
|
+
//#region helpers/object/shallowEquals.ts
|
|
436
|
+
/**
|
|
437
|
+
* This file is part of helpers4.
|
|
438
|
+
* Copyright (C) 2025 baxyz
|
|
439
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
440
|
+
*/
|
|
441
|
+
/**
|
|
442
|
+
* Quick comparison of two objects using JSON.stringify.
|
|
443
|
+
* This is a fast but simple comparison that may not work for all edge cases.
|
|
444
|
+
*
|
|
445
|
+
* @param objA - First object to compare
|
|
446
|
+
* @param objB - Second object to compare
|
|
447
|
+
* @returns `true` if objects are identical according to JSON.stringify, `false` otherwise
|
|
448
|
+
* @since 2.0.0
|
|
449
|
+
*/
|
|
450
|
+
function shallowEquals(objA, objB) {
|
|
451
|
+
if (objA === objB) return true;
|
|
452
|
+
if (typeof objA === "function" || typeof objB === "function") return objA === objB;
|
|
453
|
+
try {
|
|
454
|
+
return JSON.stringify(objA) === JSON.stringify(objB);
|
|
455
|
+
} catch {
|
|
456
|
+
return objA === objB;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
//#endregion
|
|
460
|
+
export { compact, deepClone, deepCompare, deepMerge, get, omit, pick, removeUndefinedNull, safeJsonParse, set, shallowEquals };
|
|
279
461
|
|
|
280
462
|
//# sourceMappingURL=index.js.map
|