@helpers4/object 2.0.2 → 2.0.4
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 +2 -0
- package/lib/index.d.ts +152 -18
- package/lib/index.js +136 -38
- package/lib/index.js.map +1 -1
- package/llms.txt +216 -29
- package/meta/api.json +198 -36
- package/meta/examples.json +48 -3
- package/package.json +3 -1
package/README.md
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -35,15 +35,21 @@ declare function deepClone<T>(obj: T): T;
|
|
|
35
35
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
36
36
|
*/
|
|
37
37
|
/**
|
|
38
|
-
* Merges two or more objects deeply
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* Merges two or more objects deeply.
|
|
39
|
+
*
|
|
40
|
+
* Recursively merges own enumerable properties — both string and symbol keys.
|
|
41
|
+
* Plain objects are merged recursively; all other values (arrays, class instances,
|
|
42
|
+
* primitives, etc.) are replaced by the source value.
|
|
43
|
+
* `undefined` source values do not overwrite existing target values.
|
|
44
|
+
*
|
|
45
|
+
* @param target - The target object (mutated in place)
|
|
46
|
+
* @param sources - One or more source objects to merge into the target
|
|
47
|
+
* @returns The mutated target
|
|
42
48
|
* @since 1.9.0
|
|
43
49
|
*/
|
|
44
|
-
declare function deepMerge<T extends Record<
|
|
45
|
-
declare function deepMerge(target: undefined, ...sources: Record<
|
|
46
|
-
declare function deepMerge(target: null, ...sources: Record<
|
|
50
|
+
declare function deepMerge<T extends Record<PropertyKey, unknown>>(target: T, ...sources: Record<PropertyKey, unknown>[]): T;
|
|
51
|
+
declare function deepMerge(target: undefined, ...sources: Record<PropertyKey, unknown>[]): undefined;
|
|
52
|
+
declare function deepMerge(target: null, ...sources: Record<PropertyKey, unknown>[]): null;
|
|
47
53
|
|
|
48
54
|
/**
|
|
49
55
|
* This file is part of helpers4.
|
|
@@ -163,14 +169,65 @@ declare function equalsShallow(objA: unknown, objB: unknown): boolean;
|
|
|
163
169
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
164
170
|
*/
|
|
165
171
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
* @
|
|
170
|
-
* @
|
|
172
|
+
* Resolves the value type at a given path within T.
|
|
173
|
+
* Returns `unknown` when the path doesn't match the shape of T.
|
|
174
|
+
*
|
|
175
|
+
* @internal
|
|
176
|
+
* @example
|
|
177
|
+
* DeepGet<{ a: { b: number } }, ['a', 'b']> // => number
|
|
178
|
+
* DeepGet<{ a: { b: number } }, ['a', 'x']> // => unknown
|
|
179
|
+
* DeepGet<{ a: number }, []> // => { a: number }
|
|
180
|
+
*/
|
|
181
|
+
type DeepGet<T, Path extends readonly PropertyKey[]> = Path extends readonly [] ? T : Path extends readonly [infer K, ...infer Rest extends readonly PropertyKey[]] ? K extends keyof T ? DeepGet<T[K], Rest> : unknown : unknown;
|
|
182
|
+
/**
|
|
183
|
+
* Produces the type of T after setting the value at Path to V.
|
|
184
|
+
* Resolves to `never` when a key in Path is not present in the corresponding level of T,
|
|
185
|
+
* surfacing invalid paths at the call site rather than silently returning T unchanged.
|
|
186
|
+
*
|
|
187
|
+
* @internal
|
|
188
|
+
* @example
|
|
189
|
+
* DeepSet<{ a: { b: number } }, ['a', 'b'], string>
|
|
190
|
+
* // => { a: { b: string } }
|
|
191
|
+
*/
|
|
192
|
+
type DeepSet<T, Path extends readonly PropertyKey[], V> = Path extends readonly [] ? T : Path extends readonly [infer K, ...infer Rest extends readonly PropertyKey[]] ? K extends keyof T ? {
|
|
193
|
+
[P in keyof T]: P extends K ? DeepSet<T[K], Rest, V> : T[P];
|
|
194
|
+
} : never : never;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* This file is part of helpers4.
|
|
198
|
+
* Copyright (C) 2025 baxyz
|
|
199
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
200
|
+
*/
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Gets a value from an object using a dot/bracket-notated path or explicit key array.
|
|
204
|
+
*
|
|
205
|
+
* **Two path forms are supported:**
|
|
206
|
+
*
|
|
207
|
+
* 1. **String path** — dot notation (`'a.b.c'`) and bracket notation (`'layers[1].name'`)
|
|
208
|
+
* are both accepted and mixed freely. Segments are traversed as string keys; `[n]`
|
|
209
|
+
* indices become numeric keys.
|
|
210
|
+
*
|
|
211
|
+
* 2. **Key array** (`PropertyKey[]`) — explicit array of `string | number | symbol` keys,
|
|
212
|
+
* no parsing performed. Enables symbol-keyed traversal and compile-time type inference:
|
|
213
|
+
* `get(obj, ['a', 'b'] as const)` infers the return type from the path.
|
|
214
|
+
*
|
|
215
|
+
* @param obj - The object to read from
|
|
216
|
+
* @param path - Dot/bracket-notation string or explicit `PropertyKey[]`
|
|
217
|
+
* @param defaultValue - Returned when the path is absent or resolves to `undefined`
|
|
218
|
+
* @returns The value at the path, or `defaultValue`
|
|
219
|
+
* @example
|
|
220
|
+
* get({ a: { b: { c: 42 } } }, 'a.b.c') // => 42
|
|
221
|
+
* get({ a: [1, 2, 3] }, 'a[1]') // => 2
|
|
222
|
+
* get({ a: { b: 0 } }, 'a.b', 99) // => 0 (falsy values are returned as-is)
|
|
223
|
+
* get({ a: 1 }, 'b.c', 'default') // => 'default'
|
|
224
|
+
*
|
|
225
|
+
* const id = Symbol('id')
|
|
226
|
+
* get({ [id]: 'alice' }, [id]) // => 'alice' (symbol key)
|
|
171
227
|
* @since 1.9.0
|
|
172
228
|
*/
|
|
173
229
|
declare function get<T = unknown>(obj: unknown, path: string, defaultValue?: T): T | undefined;
|
|
230
|
+
declare function get<T extends object, const Path extends readonly PropertyKey[]>(obj: T, path: Path, defaultValue?: DeepGet<T, Path>): DeepGet<T, Path> | undefined;
|
|
174
231
|
|
|
175
232
|
/**
|
|
176
233
|
* This file is part of helpers4.
|
|
@@ -214,6 +271,47 @@ declare function groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: (
|
|
|
214
271
|
*/
|
|
215
272
|
declare function invert<K extends string, V extends PropertyKey>(obj: Record<K, V>): Record<V, K>;
|
|
216
273
|
|
|
274
|
+
/**
|
|
275
|
+
* This file is part of helpers4.
|
|
276
|
+
* Copyright (C) 2025 baxyz
|
|
277
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
278
|
+
*/
|
|
279
|
+
/**
|
|
280
|
+
* Checks if a plain object has no own enumerable string-keyed properties.
|
|
281
|
+
*
|
|
282
|
+
* Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
283
|
+
* separately if symbol keys matter for your use case.
|
|
284
|
+
*
|
|
285
|
+
* @param value - The object to check
|
|
286
|
+
* @returns `true` if the object has no own enumerable string-keyed properties
|
|
287
|
+
* @example
|
|
288
|
+
* isEmpty({}) // => true
|
|
289
|
+
* isEmpty({ a: 1 }) // => false
|
|
290
|
+
* isEmpty({ a: undefined }) // => false (key exists)
|
|
291
|
+
* @since 2.0.3
|
|
292
|
+
*/
|
|
293
|
+
declare function isEmpty(value: Record<PropertyKey, unknown>): boolean;
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* This file is part of helpers4.
|
|
297
|
+
* Copyright (C) 2025 baxyz
|
|
298
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
299
|
+
*/
|
|
300
|
+
/**
|
|
301
|
+
* Checks if a plain object has at least one own enumerable string-keyed property.
|
|
302
|
+
*
|
|
303
|
+
* Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
304
|
+
* separately if symbol keys matter for your use case.
|
|
305
|
+
*
|
|
306
|
+
* @param value - The object to check
|
|
307
|
+
* @returns `true` if the object has at least one own enumerable string-keyed property
|
|
308
|
+
* @example
|
|
309
|
+
* isNonEmpty({ a: 1 }) // => true
|
|
310
|
+
* isNonEmpty({}) // => false
|
|
311
|
+
* @since 2.0.3
|
|
312
|
+
*/
|
|
313
|
+
declare function isNonEmpty(value: Record<PropertyKey, unknown>): boolean;
|
|
314
|
+
|
|
217
315
|
/**
|
|
218
316
|
* This file is part of helpers4.
|
|
219
317
|
* Copyright (C) 2025 baxyz
|
|
@@ -344,15 +442,51 @@ declare function safeJsonParse<T>(input: string, fallback: T): T;
|
|
|
344
442
|
* Copyright (C) 2025 baxyz
|
|
345
443
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
346
444
|
*/
|
|
445
|
+
|
|
347
446
|
/**
|
|
348
|
-
* Sets a value in an object
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
447
|
+
* Sets a value in an object at the given path, creating intermediate objects as needed.
|
|
448
|
+
*
|
|
449
|
+
* **Three path forms are supported:**
|
|
450
|
+
*
|
|
451
|
+
* 1. **Dot notation** (`string`) — segments split on `.` are kept as-is string keys.
|
|
452
|
+
* `"layers.1.name"` → keys `["layers", "1", "name"]` (all strings, including `"1"`).
|
|
453
|
+
*
|
|
454
|
+
* 2. **Bracket notation** (`string`) — `[n]` segments are parsed as numeric keys.
|
|
455
|
+
* `"layers[1].name"` → keys `["layers", 1, "name"]` (index `1` becomes a number).
|
|
456
|
+
* Dot and bracket can be mixed freely: `"a[0].b[2].c"`.
|
|
457
|
+
*
|
|
458
|
+
* 3. **Key array** (`PropertyKey[]`) — explicit array of `string | number | symbol` keys,
|
|
459
|
+
* no parsing performed. Enables full key-type control, including symbols:
|
|
460
|
+
* `["layers", 1, Symbol('id')]`.
|
|
461
|
+
*
|
|
462
|
+
* Intermediate nodes that are absent, `null`, or not an object are replaced with `{}`.
|
|
463
|
+
* Any path containing a string segment equal to `__proto__`, `constructor`, or `prototype`
|
|
464
|
+
* is rejected and the original object is returned unchanged (prototype-pollution guard).
|
|
465
|
+
*
|
|
466
|
+
* @param obj - The object to mutate
|
|
467
|
+
* @param path - Dot/bracket-notation string or explicit `PropertyKey[]`
|
|
468
|
+
* @param value - Value to assign at the path
|
|
469
|
+
* @returns The mutated object (same reference)
|
|
470
|
+
* @example
|
|
471
|
+
* // 1. Dot notation — "1" stays a string key
|
|
472
|
+
* set({}, 'a.b.c', 42)
|
|
473
|
+
* // => { a: { b: { c: 42 } } }
|
|
474
|
+
*
|
|
475
|
+
* set({}, 'layers.1.name', 'bg')
|
|
476
|
+
* // => { layers: { '1': { name: 'bg' } } }
|
|
477
|
+
*
|
|
478
|
+
* // 2. Bracket notation — [1] becomes a number key
|
|
479
|
+
* set({ layers: [{}, { name: 'old' }] }, 'layers[1].name', 'bg')
|
|
480
|
+
* // => { layers: [{}, { name: 'bg' }] }
|
|
481
|
+
*
|
|
482
|
+
* // 3. Key array — supports symbols
|
|
483
|
+
* const id = Symbol('id')
|
|
484
|
+
* set({}, ['user', id], 'alice')
|
|
485
|
+
* // => { user: { [id]: 'alice' } }
|
|
353
486
|
* @since 1.9.0
|
|
354
487
|
*/
|
|
355
488
|
declare function set(obj: Record<string, unknown>, path: string, value: unknown): Record<string, unknown>;
|
|
489
|
+
declare function set<T extends object, const Path extends readonly PropertyKey[], V extends DeepGet<T, Path>>(obj: T, path: Path, value: V): DeepSet<T, Path, V>;
|
|
356
490
|
|
|
357
|
-
export { compact, deepClone, deepMerge, diff, equalsDeep, equalsShallow, get, groupBy, invert, map, omit, pick, removeUndefinedNull, safeJsonParse, set };
|
|
491
|
+
export { compact, deepClone, deepMerge, diff, equalsDeep, equalsShallow, get, groupBy, invert, isEmpty, isNonEmpty, map, omit, pick, removeUndefinedNull, safeJsonParse, set };
|
|
358
492
|
export type { DiffOptions, DiffResult };
|
package/lib/index.js
CHANGED
|
@@ -6,17 +6,28 @@ function compact(obj) {
|
|
|
6
6
|
return result;
|
|
7
7
|
}
|
|
8
8
|
//#endregion
|
|
9
|
-
//#region helpers/object/
|
|
9
|
+
//#region helpers/object/_unsafeKeys.ts
|
|
10
10
|
/**
|
|
11
11
|
* This file is part of helpers4.
|
|
12
12
|
* Copyright (C) 2025 baxyz
|
|
13
13
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Keys that must never be set on a plain object (prototype-pollution guard).
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
var UNSAFE_KEYS = new Set([
|
|
16
20
|
"__proto__",
|
|
17
21
|
"constructor",
|
|
18
22
|
"prototype"
|
|
19
23
|
]);
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region helpers/object/deepClone.ts
|
|
26
|
+
/**
|
|
27
|
+
* This file is part of helpers4.
|
|
28
|
+
* Copyright (C) 2025 baxyz
|
|
29
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
30
|
+
*/
|
|
20
31
|
/**
|
|
21
32
|
* Creates a deep copy of an object or array
|
|
22
33
|
* @param obj - The object to clone
|
|
@@ -28,7 +39,7 @@ function deepClone(obj) {
|
|
|
28
39
|
if (obj instanceof Date) return new Date(obj.getTime());
|
|
29
40
|
if (Array.isArray(obj)) return obj.map((item) => deepClone(item));
|
|
30
41
|
const cloned = {};
|
|
31
|
-
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key) && !UNSAFE_KEYS
|
|
42
|
+
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key) && !UNSAFE_KEYS.has(key)) cloned[key] = deepClone(obj[key]);
|
|
32
43
|
return cloned;
|
|
33
44
|
}
|
|
34
45
|
//#endregion
|
|
@@ -66,22 +77,26 @@ function isPlainObject(value) {
|
|
|
66
77
|
* Copyright (C) 2025 baxyz
|
|
67
78
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
68
79
|
*/
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
]
|
|
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
|
+
}
|
|
74
86
|
function deepMerge(target, ...sources) {
|
|
75
87
|
if (target === void 0 || target === null) return target;
|
|
76
88
|
if (!sources.length) return target;
|
|
77
89
|
const source = sources.shift();
|
|
78
90
|
if (!source) return deepMerge(target, ...sources);
|
|
91
|
+
const t = target;
|
|
92
|
+
const s = source;
|
|
79
93
|
for (const key in source) {
|
|
80
|
-
if (!Object.hasOwn(source, key) || UNSAFE_KEYS
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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);
|
|
85
100
|
}
|
|
86
101
|
return deepMerge(target, ...sources);
|
|
87
102
|
}
|
|
@@ -443,22 +458,70 @@ function equalsShallow(objA, objB) {
|
|
|
443
458
|
return true;
|
|
444
459
|
}
|
|
445
460
|
//#endregion
|
|
446
|
-
//#region helpers/object/
|
|
461
|
+
//#region helpers/object/_parsePath.ts
|
|
447
462
|
/**
|
|
448
463
|
* This file is part of helpers4.
|
|
449
464
|
* Copyright (C) 2025 baxyz
|
|
450
465
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
451
466
|
*/
|
|
452
467
|
/**
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
468
|
+
* Parses a string path into an array of string/number segments.
|
|
469
|
+
*
|
|
470
|
+
* - Dot separators (`.`) split segments; each segment becomes a string key.
|
|
471
|
+
* - Bracket indices (`[n]`) become number keys.
|
|
472
|
+
* - A leading `.` is treated as "current level" and stripped before parsing,
|
|
473
|
+
* so `.[0]` ≡ `[0]` and `.a.b` ≡ `a.b`.
|
|
474
|
+
* - Empty string (or bare `.`) returns `['']` (addresses the `''` key on the root object).
|
|
475
|
+
* - Consecutive dots (`a..b`) produce an empty-string segment: `['a', '', 'b']`.
|
|
476
|
+
* - Non-bracket text trailing a `]` within the same dot-segment throws a RangeError;
|
|
477
|
+
* use a dot separator instead (`'a[0].b'` not `'a[0]b'`).
|
|
478
|
+
*
|
|
479
|
+
* @internal
|
|
480
|
+
* @example
|
|
481
|
+
* parsePath('a.b.c') // => ['a', 'b', 'c'] (all strings)
|
|
482
|
+
* parsePath('layers.1.x') // => ['layers', '1', 'x'] (all strings)
|
|
483
|
+
* parsePath('layers[1].x') // => ['layers', 1, 'x'] (bracket → number)
|
|
484
|
+
* parsePath('.[0]') // => [0] (leading dot stripped)
|
|
485
|
+
* parsePath('a..b') // => ['a', '', 'b'] (empty segment preserved)
|
|
486
|
+
* parsePath('.') // => [''] (leading dot → empty-string key)
|
|
487
|
+
*/
|
|
488
|
+
var BRACKET = /\[(\d+)\]/g;
|
|
489
|
+
var parsePathCache = /* @__PURE__ */ new Map();
|
|
490
|
+
function parsePath(path) {
|
|
491
|
+
const cached = parsePathCache.get(path);
|
|
492
|
+
if (cached !== void 0) return cached;
|
|
493
|
+
const result = Object.freeze(parsePathUncached(path));
|
|
494
|
+
parsePathCache.set(path, result);
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
497
|
+
function parsePathUncached(path) {
|
|
498
|
+
if (path === "") return [""];
|
|
499
|
+
if (path[0] === ".") path = path.slice(1);
|
|
500
|
+
if (path === "") return [""];
|
|
501
|
+
const result = [];
|
|
502
|
+
for (const dotSegment of path.split(".")) {
|
|
503
|
+
BRACKET.lastIndex = 0;
|
|
504
|
+
let last = 0;
|
|
505
|
+
let match;
|
|
506
|
+
while ((match = BRACKET.exec(dotSegment)) !== null) {
|
|
507
|
+
if (match.index > last) result.push(dotSegment.slice(last, match.index));
|
|
508
|
+
result.push(parseInt(match[1], 10));
|
|
509
|
+
last = BRACKET.lastIndex;
|
|
510
|
+
}
|
|
511
|
+
if (last === 0) result.push(dotSegment);
|
|
512
|
+
else if (last < dotSegment.length) throw new RangeError(`parsePath: non-bracket text '${dotSegment.slice(last)}' after ']' in segment '${dotSegment}' — use 'a[0].b' instead of 'a[0]b'`);
|
|
513
|
+
}
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
//#endregion
|
|
517
|
+
//#region helpers/object/get.ts
|
|
518
|
+
/**
|
|
519
|
+
* This file is part of helpers4.
|
|
520
|
+
* Copyright (C) 2025 baxyz
|
|
521
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
459
522
|
*/
|
|
460
523
|
function get(obj, path, defaultValue) {
|
|
461
|
-
const keys = path
|
|
524
|
+
const keys = typeof path === "string" ? parsePath(path) : path;
|
|
462
525
|
let result = obj;
|
|
463
526
|
for (const key of keys) {
|
|
464
527
|
if (result == null || typeof result !== "object") return defaultValue;
|
|
@@ -523,6 +586,53 @@ function invert(obj) {
|
|
|
523
586
|
return result;
|
|
524
587
|
}
|
|
525
588
|
//#endregion
|
|
589
|
+
//#region helpers/object/isEmpty.ts
|
|
590
|
+
/**
|
|
591
|
+
* This file is part of helpers4.
|
|
592
|
+
* Copyright (C) 2025 baxyz
|
|
593
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
594
|
+
*/
|
|
595
|
+
/**
|
|
596
|
+
* Checks if a plain object has no own enumerable string-keyed properties.
|
|
597
|
+
*
|
|
598
|
+
* Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
599
|
+
* separately if symbol keys matter for your use case.
|
|
600
|
+
*
|
|
601
|
+
* @param value - The object to check
|
|
602
|
+
* @returns `true` if the object has no own enumerable string-keyed properties
|
|
603
|
+
* @example
|
|
604
|
+
* isEmpty({}) // => true
|
|
605
|
+
* isEmpty({ a: 1 }) // => false
|
|
606
|
+
* isEmpty({ a: undefined }) // => false (key exists)
|
|
607
|
+
* @since 2.0.3
|
|
608
|
+
*/
|
|
609
|
+
function isEmpty(value) {
|
|
610
|
+
return Object.keys(value).length === 0;
|
|
611
|
+
}
|
|
612
|
+
//#endregion
|
|
613
|
+
//#region helpers/object/isNonEmpty.ts
|
|
614
|
+
/**
|
|
615
|
+
* This file is part of helpers4.
|
|
616
|
+
* Copyright (C) 2025 baxyz
|
|
617
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
618
|
+
*/
|
|
619
|
+
/**
|
|
620
|
+
* Checks if a plain object has at least one own enumerable string-keyed property.
|
|
621
|
+
*
|
|
622
|
+
* Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
623
|
+
* separately if symbol keys matter for your use case.
|
|
624
|
+
*
|
|
625
|
+
* @param value - The object to check
|
|
626
|
+
* @returns `true` if the object has at least one own enumerable string-keyed property
|
|
627
|
+
* @example
|
|
628
|
+
* isNonEmpty({ a: 1 }) // => true
|
|
629
|
+
* isNonEmpty({}) // => false
|
|
630
|
+
* @since 2.0.3
|
|
631
|
+
*/
|
|
632
|
+
function isNonEmpty(value) {
|
|
633
|
+
return Object.keys(value).length > 0;
|
|
634
|
+
}
|
|
635
|
+
//#endregion
|
|
526
636
|
//#region helpers/object/map.ts
|
|
527
637
|
/**
|
|
528
638
|
* This file is part of helpers4.
|
|
@@ -641,32 +751,20 @@ function safeJsonParse(input, fallback) {
|
|
|
641
751
|
* Copyright (C) 2025 baxyz
|
|
642
752
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
643
753
|
*/
|
|
644
|
-
var UNSAFE_KEYS = new Set([
|
|
645
|
-
"__proto__",
|
|
646
|
-
"constructor",
|
|
647
|
-
"prototype"
|
|
648
|
-
]);
|
|
649
|
-
/**
|
|
650
|
-
* Sets a value in an object using a dot-notated path
|
|
651
|
-
* @param obj - The object to set value in
|
|
652
|
-
* @param path - The dot-notated path (e.g., 'a.b.c')
|
|
653
|
-
* @param value - The value to set
|
|
654
|
-
* @returns The modified object
|
|
655
|
-
* @since 1.9.0
|
|
656
|
-
*/
|
|
657
754
|
function set(obj, path, value) {
|
|
658
|
-
const keys = path
|
|
659
|
-
if (keys.
|
|
755
|
+
const keys = typeof path === "string" ? parsePath(path) : path;
|
|
756
|
+
if (keys.length === 0) return obj;
|
|
757
|
+
if (keys.some((k) => typeof k === "string" && UNSAFE_KEYS.has(k))) return obj;
|
|
660
758
|
let current = obj;
|
|
661
759
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
662
760
|
const key = keys[i];
|
|
663
|
-
if (!(key
|
|
761
|
+
if (!Object.hasOwn(current, key) || typeof current[key] !== "object" || current[key] === null) current[key] = {};
|
|
664
762
|
current = current[key];
|
|
665
763
|
}
|
|
666
764
|
current[keys[keys.length - 1]] = value;
|
|
667
765
|
return obj;
|
|
668
766
|
}
|
|
669
767
|
//#endregion
|
|
670
|
-
export { compact, deepClone, deepMerge, diff, equalsDeep, equalsShallow, get, groupBy, invert, map, omit, pick, removeUndefinedNull, safeJsonParse, set };
|
|
768
|
+
export { compact, deepClone, deepMerge, diff, equalsDeep, equalsShallow, get, groupBy, invert, isEmpty, isNonEmpty, map, omit, pick, removeUndefinedNull, safeJsonParse, set };
|
|
671
769
|
|
|
672
770
|
//# sourceMappingURL=index.js.map
|