@helpers4/object 2.0.0-alpha.10 → 2.0.0-alpha.14

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 CHANGED
@@ -12,10 +12,13 @@ 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
20
+ - omit
21
+ - pick
19
22
  - removeUndefinedNull
20
23
  - set
21
24
  - shallowEquals
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, any>>(target: T, ...sources: Record<string, any>[]): T;
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,45 @@ 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 = any>(obj: any, path: string, defaultValue?: T): T | undefined;
83
+ declare function get<T = unknown>(obj: unknown, path: string, defaultValue?: T): T | undefined;
84
+
85
+ /**
86
+ * This file is part of helpers4.
87
+ * Copyright (C) 2025 baxyz
88
+ * SPDX-License-Identifier: LGPL-3.0-or-later
89
+ */
90
+ /**
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 }
117
+ * @since 2.0.0
118
+ */
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;
64
122
 
65
123
  /**
66
124
  * This file is part of helpers4.
@@ -105,7 +163,7 @@ declare function removeUndefinedNull(obj: undefined): undefined;
105
163
  * @returns The modified object
106
164
  * @since 1.9.0
107
165
  */
108
- declare function set(obj: Record<string, any>, path: string, value: any): Record<string, any>;
166
+ declare function set(obj: Record<string, unknown>, path: string, value: unknown): Record<string, unknown>;
109
167
 
110
168
  /**
111
169
  * This file is part of helpers4.
@@ -123,5 +181,5 @@ declare function set(obj: Record<string, any>, path: string, value: any): Record
123
181
  */
124
182
  declare function shallowEquals(objA: unknown, objB: unknown): boolean;
125
183
 
126
- export { deepClone, deepCompare, deepMerge, get, removeUndefinedNull, set, shallowEquals };
184
+ export { compact, deepClone, deepCompare, deepMerge, get, omit, pick, removeUndefinedNull, set, shallowEquals };
127
185
  export type { DeepCompareResult };
package/lib/index.js CHANGED
@@ -1,4 +1,11 @@
1
- import { isNullish } from "radashi";
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.
@@ -151,34 +158,59 @@ function deepCompare(objA, objB) {
151
158
  return Object.keys(differences).length > 0 ? differences : true;
152
159
  }
153
160
  //#endregion
154
- //#region helpers/object/deepMerge.ts
161
+ //#region helpers/type/isPlainObject.ts
155
162
  /**
156
163
  * This file is part of helpers4.
157
164
  * Copyright (C) 2025 baxyz
158
165
  * SPDX-License-Identifier: LGPL-3.0-or-later
159
166
  */
160
167
  /**
161
- * Merges two or more objects deeply
162
- * @param target - The target object
163
- * @param sources - The source objects to merge
164
- * @returns The merged object
165
- * @since 1.9.0
168
+ * Checks if a value is a plain object.
169
+ *
170
+ * A plain object is created by `{}`, `new Object()`, or `Object.create(null)`.
171
+ * Returns `false` for arrays, Date, Map, Set, RegExp, class instances, etc.
172
+ *
173
+ * @param value - The value to check
174
+ * @returns True if value is a plain object
175
+ * @example
176
+ * isPlainObject({}) // => true
177
+ * isPlainObject({ a: 1 }) // => true
178
+ * isPlainObject(new Date()) // => false
179
+ * isPlainObject([]) // => false
180
+ * isPlainObject(null) // => false
181
+ * @since 2.0.0
166
182
  */
183
+ function isPlainObject(value) {
184
+ if (value === null || typeof value !== "object") return false;
185
+ const proto = Object.getPrototypeOf(value);
186
+ return proto === Object.prototype || proto === null;
187
+ }
188
+ //#endregion
189
+ //#region helpers/object/deepMerge.ts
190
+ /**
191
+ * This file is part of helpers4.
192
+ * Copyright (C) 2025 baxyz
193
+ * SPDX-License-Identifier: LGPL-3.0-or-later
194
+ */
195
+ var UNSAFE_KEYS$1 = new Set([
196
+ "__proto__",
197
+ "constructor",
198
+ "prototype"
199
+ ]);
167
200
  function deepMerge(target, ...sources) {
201
+ if (target === void 0 || target === null) return target;
168
202
  if (!sources.length) return target;
169
203
  const source = sources.shift();
170
204
  if (!source) return deepMerge(target, ...sources);
171
205
  for (const key in source) {
206
+ if (!Object.hasOwn(source, key) || UNSAFE_KEYS$1.has(key)) continue;
172
207
  const targetValue = target[key];
173
208
  const sourceValue = source[key];
174
- if (isObject(targetValue) && isObject(sourceValue)) target[key] = deepMerge(targetValue, sourceValue);
209
+ if (isPlainObject(targetValue) && isPlainObject(sourceValue)) target[key] = deepMerge(targetValue, sourceValue);
175
210
  else if (sourceValue !== void 0) target[key] = sourceValue;
176
211
  }
177
212
  return deepMerge(target, ...sources);
178
213
  }
179
- function isObject(item) {
180
- return item && typeof item === "object" && !Array.isArray(item);
181
- }
182
214
  //#endregion
183
215
  //#region helpers/object/get.ts
184
216
  /**
@@ -204,6 +236,46 @@ function get(obj, path, defaultValue) {
204
236
  return result !== void 0 ? result : defaultValue;
205
237
  }
206
238
  //#endregion
239
+ //#region helpers/object/omit.ts
240
+ function omit(obj, keys) {
241
+ if (obj === void 0 || obj === null) return obj;
242
+ const result = { ...obj };
243
+ for (const key of keys) delete result[key];
244
+ return result;
245
+ }
246
+ //#endregion
247
+ //#region helpers/object/pick.ts
248
+ function pick(obj, keys) {
249
+ if (obj === void 0 || obj === null) return obj;
250
+ const result = {};
251
+ for (const key of keys) if (Object.prototype.hasOwnProperty.call(obj, key)) result[key] = obj[key];
252
+ return result;
253
+ }
254
+ //#endregion
255
+ //#region helpers/type/isNullish.ts
256
+ /**
257
+ * This file is part of helpers4.
258
+ * Copyright (C) 2025 baxyz
259
+ * SPDX-License-Identifier: LGPL-3.0-or-later
260
+ */
261
+ /**
262
+ * Checks if a value is null or undefined (nullish).
263
+ * @param value - The value to check
264
+ * @returns True if value is null or undefined
265
+ * @example
266
+ * isNullish(null) // => true
267
+ * isNullish(undefined) // => true
268
+ * isNullish(0) // => false
269
+ * isNullish('') // => false
270
+ *
271
+ * @see {@link isDefined} for the inverse check (`!isNullish`)
272
+ * @see {@link isFalsy} to also catch `0`, `""`, `false`, and `NaN`
273
+ * @since 2.0.0
274
+ */
275
+ function isNullish(value) {
276
+ return value === null || value === void 0;
277
+ }
278
+ //#endregion
207
279
  //#region helpers/object/removeUndefinedNull.ts
208
280
  /**
209
281
  * This file is part of helpers4.
@@ -218,10 +290,10 @@ function get(obj, path, defaultValue) {
218
290
  * @since 1.0.0
219
291
  */
220
292
  function removeUndefinedNull(obj) {
221
- return obj ? Object.entries(obj).filter(([_, v]) => !isNullish(v)).reduce((acc, [k, v]) => ({
222
- ...acc,
223
- [k]: v
224
- }), {}) : obj;
293
+ if (!obj) return obj;
294
+ const result = {};
295
+ for (const [k, v] of Object.entries(obj)) if (!isNullish(v)) result[k] = v;
296
+ return result;
225
297
  }
226
298
  //#endregion
227
299
  //#region helpers/object/set.ts
@@ -230,6 +302,11 @@ function removeUndefinedNull(obj) {
230
302
  * Copyright (C) 2025 baxyz
231
303
  * SPDX-License-Identifier: LGPL-3.0-or-later
232
304
  */
305
+ var UNSAFE_KEYS = new Set([
306
+ "__proto__",
307
+ "constructor",
308
+ "prototype"
309
+ ]);
233
310
  /**
234
311
  * Sets a value in an object using a dot-notated path
235
312
  * @param obj - The object to set value in
@@ -240,6 +317,7 @@ function removeUndefinedNull(obj) {
240
317
  */
241
318
  function set(obj, path, value) {
242
319
  const keys = path.split(".");
320
+ if (keys.some((k) => UNSAFE_KEYS.has(k))) return obj;
243
321
  let current = obj;
244
322
  for (let i = 0; i < keys.length - 1; i++) {
245
323
  const key = keys[i];
@@ -275,6 +353,6 @@ function shallowEquals(objA, objB) {
275
353
  }
276
354
  }
277
355
  //#endregion
278
- export { deepClone, deepCompare, deepMerge, get, removeUndefinedNull, set, shallowEquals };
356
+ export { compact, deepClone, deepCompare, deepMerge, get, omit, pick, removeUndefinedNull, set, shallowEquals };
279
357
 
280
358
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../helpers/object/deepClone.ts","../../../helpers/array/shallowEquals.ts","../../../helpers/date/compare.ts","../../../helpers/type/isSpecialObject.ts","../../../helpers/object/deepCompare.ts","../../../helpers/object/deepMerge.ts","../../../helpers/object/get.ts","../../../helpers/object/removeUndefinedNull.ts","../../../helpers/object/set.ts","../../../helpers/object/shallowEquals.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Creates a deep copy of an object or array\n * @param obj - The object to clone\n * @returns Deep cloned object\n * @since 1.9.0\n */\nexport function deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as T;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(item => deepClone(item)) as T;\n }\n\n const cloned = {} as T;\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n cloned[key] = deepClone(obj[key]);\n }\n }\n\n return cloned;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Quick comparison of two arrays using JSON.stringify.\n * This is a fast but simple comparison that may not work for all edge cases.\n *\n * @param arrA - First array to compare\n * @param arrB - Second array to compare\n * @returns `true` if arrays are identical according to JSON.stringify, `false` otherwise\n * @since 2.0.0\n */\nexport function shallowEquals<T>(arrA: T[], arrB: T[]): boolean {\n try {\n return JSON.stringify(arrA) === JSON.stringify(arrB);\n } catch {\n // If JSON.stringify fails (e.g., circular references), fall back to reference equality\n return arrA === arrB;\n }\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Options for date comparison\n * @since 2.0.0\n */\nexport interface DateCompareOptions {\n /** \n * Comparison precision\n * - 'milliseconds': Compare all including milliseconds (default)\n * - 'seconds': Ignore milliseconds, compare to the second\n * - 'minutes': Ignore seconds and milliseconds, compare to the minute\n * - 'hours': Ignore minutes, seconds and milliseconds, compare to the hour\n * - 'days': Compare only the date part (ignore time completely)\n */\n precision?: 'milliseconds' | 'seconds' | 'minutes' | 'hours' | 'days';\n}\n\n/**\n * Comparison of two dates.\n * \n * @param dateA - First date to compare\n * @param dateB - Second date to compare\n * @param options - Comparison options\n * @returns `true` if dates are identical according to the specified precision, `false` otherwise\n * @since 2.0.0\n */\nexport function compare(dateA: Date, dateB: Date, options: DateCompareOptions = {}): boolean {\n const { precision = 'milliseconds' } = options;\n\n if (!(dateA instanceof Date) || !(dateB instanceof Date)) {\n return dateA === dateB;\n }\n\n // Handle invalid dates\n if (isNaN(dateA.getTime()) || isNaN(dateB.getTime())) {\n return isNaN(dateA.getTime()) && isNaN(dateB.getTime());\n }\n\n switch (precision) {\n case 'days':\n return dateA.toDateString() === dateB.toDateString();\n\n case 'hours':\n const hoursA = Math.floor(dateA.getTime() / (1000 * 60 * 60));\n const hoursB = Math.floor(dateB.getTime() / (1000 * 60 * 60));\n return hoursA === hoursB;\n\n case 'minutes':\n const minutesA = Math.floor(dateA.getTime() / (1000 * 60));\n const minutesB = Math.floor(dateB.getTime() / (1000 * 60));\n return minutesA === minutesB;\n\n case 'seconds':\n const secondsA = Math.floor(dateA.getTime() / 1000);\n const secondsB = Math.floor(dateB.getTime() / 1000);\n return secondsA === secondsB;\n\n case 'milliseconds':\n default:\n return dateA.getTime() === dateB.getTime();\n }\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Determines if a value is a special object that should not have its properties compared deeply.\n * Special objects include: Date, Function, Promise, Observable, RegExp, Error, Map, Set, WeakMap, WeakSet, etc.\n * \n * @param value - The value to check\n * @returns `true` if the value is a special object, `false` otherwise\n * @since 2.0.0\n */\nexport function isSpecialObject(value: unknown): boolean {\n if (value === null || value === undefined) {\n return false;\n }\n\n // Functions are special objects\n if (typeof value === 'function') {\n return true;\n }\n\n // Non-objects are not special\n if (typeof value !== 'object') {\n return false;\n }\n\n // Arrays are not considered special objects for our purposes\n if (Array.isArray(value)) {\n return false;\n }\n\n // Check for built-in special objects\n if (\n value instanceof Date ||\n value instanceof Promise ||\n value instanceof RegExp ||\n value instanceof Error ||\n value instanceof Map ||\n value instanceof Set ||\n value instanceof WeakMap ||\n value instanceof WeakSet ||\n value instanceof ArrayBuffer ||\n value instanceof DataView\n ) {\n return true;\n }\n\n // Check for Observable (RxJS or similar)\n if (value.constructor?.name === 'Observable') {\n return true;\n }\n\n // Check for DOM elements (if in browser environment)\n if (typeof HTMLElement !== 'undefined' && value instanceof HTMLElement) {\n return true;\n }\n\n // Check for other built-in types by constructor name\n const constructorName = value.constructor?.name;\n if (constructorName && [\n 'Buffer', // Node.js Buffer\n 'URL', // Web URL API\n 'URLSearchParams',\n 'File', // Web File API\n 'Blob', // Web Blob API\n 'FormData', // Web FormData API\n 'Headers', // Web Headers API\n 'Request', // Web Request API\n 'Response', // Web Response API\n 'EventTarget', // Web EventTarget API\n 'Symbol' // Symbol objects\n ].includes(constructorName)) {\n return true;\n }\n\n return false;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { shallowEquals as shallowEqualsArray } from '../array/shallowEquals';\nimport { compare as compareDate } from '../date/compare';\nimport { isSpecialObject } from '../type/isSpecialObject';\n\n/**\n * Result type for deep comparison when objects are not identical\n * @since 2.0.0\n */\nexport interface DeepCompareResult {\n [key: string]: \"onlyA\" | \"onlyB\" | false | DeepCompareResult;\n}\n\n/**\n * Deep comparison of two objects that returns detailed information about differences.\n *\n * @param objA - First object to compare (can be object, undefined, or null)\n * @param objB - Second object to compare (can be object, undefined, or null)\n * @returns `true` if objects are identical, `false` if incompatible types, or a `DeepCompareResult` object detailing differences\n * @since 2.0.0\n */\nexport function deepCompare(objA: object | undefined | null, objB: object | undefined | null): true | false | DeepCompareResult {\n // Quick reference equality check\n if (objA === objB) {\n return true;\n }\n\n // Handle null/undefined cases\n if (objA == null || objB == null) {\n return false;\n }\n\n // Handle Date objects\n if (objA instanceof Date && objB instanceof Date) {\n return compareDate(objA, objB);\n }\n\n // Handle Arrays at root level - use shallowEquals\n if (Array.isArray(objA) && Array.isArray(objB)) {\n return shallowEqualsArray(objA, objB);\n }\n\n // If one is array and other is not\n if (Array.isArray(objA) || Array.isArray(objB)) {\n return false;\n }\n\n // Handle special objects - compare by reference only\n if (isSpecialObject(objA) || isSpecialObject(objB)) {\n return objA === objB;\n }\n\n // Handle objects - detailed comparison\n const keys = Array.from(new Set([...Object.keys(objA), ...Object.keys(objB)]));\n\n const differences: DeepCompareResult = {};\n\n for (const key of keys) {\n const hasA = key in objA;\n const hasB = key in objB;\n\n if (!hasA && hasB) {\n differences[key] = \"onlyB\";\n } else if (hasA && !hasB) {\n differences[key] = \"onlyA\";\n } else {\n // Both objects have this key - compare values\n const valueA = (objA as any)[key];\n const valueB = (objB as any)[key];\n\n // For arrays, use shallowEquals\n if (Array.isArray(valueA) && Array.isArray(valueB)) {\n if (!shallowEqualsArray(valueA, valueB)) {\n differences[key] = false;\n }\n }\n // For dates, use compareDate\n else if (valueA instanceof Date && valueB instanceof Date) {\n if (!compareDate(valueA, valueB)) {\n differences[key] = false;\n }\n }\n // For special objects, compare by reference only\n else if (isSpecialObject(valueA) || isSpecialObject(valueB)) {\n if (valueA !== valueB) {\n differences[key] = false;\n }\n }\n // For objects, recurse (only plain objects now)\n else if (\n valueA !== null && valueB !== null &&\n valueA !== undefined && valueB !== undefined &&\n typeof valueA === 'object' && typeof valueB === 'object' &&\n !isSpecialObject(valueA) && !isSpecialObject(valueB)\n ) {\n const nestedResult = deepCompare(valueA, valueB);\n if (nestedResult !== true) {\n differences[key] = nestedResult === false ? false : nestedResult;\n }\n }\n // For primitives and other types, check equality\n else if (valueA !== valueB) {\n differences[key] = false;\n }\n }\n }\n\n return Object.keys(differences).length > 0 ? differences : true;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Merges two or more objects deeply\n * @param target - The target object\n * @param sources - The source objects to merge\n * @returns The merged object\n * @since 1.9.0\n */\nexport function deepMerge<T extends Record<string, any>>(target: T, ...sources: Record<string, any>[]): T {\n if (!sources.length) return target;\n const source = sources.shift();\n\n if (!source) return deepMerge(target, ...sources);\n\n for (const key in source) {\n const targetValue = target[key];\n const sourceValue = source[key];\n\n if (isObject(targetValue) && isObject(sourceValue)) {\n (target as any)[key] = deepMerge(targetValue, sourceValue);\n } else if (sourceValue !== undefined) {\n (target as any)[key] = sourceValue;\n }\n }\n\n return deepMerge(target, ...sources);\n}\n\nfunction isObject(item: any): item is object {\n return item && typeof item === 'object' && !Array.isArray(item);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Gets a value from an object using a dot-notated path\n * @param obj - The object to get value from\n * @param path - The dot-notated path (e.g., 'a.b.c')\n * @param defaultValue - Default value if path doesn't exist\n * @returns The value at the path or default value\n * @since 1.9.0\n */\nexport function get<T = any>(obj: any, path: string, defaultValue?: T): T | undefined {\n const keys = path.split('.');\n let result = obj;\n\n for (const key of keys) {\n if (result == null || typeof result !== 'object') {\n return defaultValue;\n }\n result = result[key];\n }\n\n return result !== undefined ? result : defaultValue;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { isNullish } from \"radashi\";\n\n/**\n * Remove null and undefined values from an object.\n *\n * @param obj an object\n * @returns A shallow copy of the object without null or undefined values\n * @since 1.0.0\n */\nexport function removeUndefinedNull<T extends Record<string, string | number | boolean | null | undefined>>(obj: T): Partial<T>;\n\n/**\n * Remove null and undefined values from an object.\n *\n * @param obj a null object\n * @returns null\n * @since 1.0.0\n */\nexport function removeUndefinedNull(obj: null): null;\n\n/**\n * Remove null and undefined values from an object.\n *\n * @param obj an undefined object\n * @returns undefined\n * @since 1.0.0\n */\nexport function removeUndefinedNull(obj: undefined): undefined;\n\n/**\n * Remove null and undefined values from an object.\n *\n * @param obj an object\n * @returns A shallow copy of the object without null or undefined values\n * @since 1.0.0\n */\nexport function removeUndefinedNull<T extends Record<string, string | number | boolean | null | undefined>>(obj: T | null | undefined): Partial<T> | null | undefined {\n return obj ? Object.entries(obj)\n .filter(([_, v]) => !isNullish(v))\n .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {} as Partial<T>) : obj;\n}\n\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Sets a value in an object using a dot-notated path\n * @param obj - The object to set value in\n * @param path - The dot-notated path (e.g., 'a.b.c')\n * @param value - The value to set\n * @returns The modified object\n * @since 1.9.0\n */\nexport function set(obj: Record<string, any>, path: string, value: any): Record<string, any> {\n const keys = path.split('.');\n let current = obj;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n\n if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {\n current[key] = {};\n }\n\n current = current[key];\n }\n\n current[keys[keys.length - 1]] = value;\n return obj;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Quick comparison of two objects using JSON.stringify.\n * This is a fast but simple comparison that may not work for all edge cases.\n *\n * @param objA - First object to compare\n * @param objB - Second object to compare\n * @returns `true` if objects are identical according to JSON.stringify, `false` otherwise\n * @since 2.0.0\n */\nexport function shallowEquals(objA: unknown, objB: unknown): boolean {\n // Quick reference equality check\n if (objA === objB) {\n return true;\n }\n\n // Handle functions - they can't be JSON.stringified reliably\n if (typeof objA === 'function' || typeof objB === 'function') {\n return objA === objB;\n }\n\n try {\n return JSON.stringify(objA) === JSON.stringify(objB);\n } catch {\n // If JSON.stringify fails (e.g., circular references), fall back to basic comparison\n return objA === objB;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,SAAgB,UAAa,KAAW;AACtC,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,QAAO;AAGT,KAAI,eAAe,KACjB,QAAO,IAAI,KAAK,IAAI,SAAS,CAAC;AAGhC,KAAI,MAAM,QAAQ,IAAI,CACpB,QAAO,IAAI,KAAI,SAAQ,UAAU,KAAK,CAAC;CAGzC,MAAM,SAAS,EAAE;AACjB,MAAK,MAAM,OAAO,IAChB,KAAI,IAAI,eAAe,IAAI,CACzB,QAAO,OAAO,UAAU,IAAI,KAAK;AAIrC,QAAO;;;;;;;;;;;;;;;;;;ACjBT,SAAgB,gBAAiB,MAAW,MAAoB;AAC9D,KAAI;AACF,SAAO,KAAK,UAAU,KAAK,KAAK,KAAK,UAAU,KAAK;SAC9C;AAEN,SAAO,SAAS;;;;;;;;;;;;;;ACWpB,SAAgB,QAAQ,OAAa,OAAa,UAA8B,EAAE,EAAW;CAC3F,MAAM,EAAE,YAAY,mBAAmB;AAEvC,KAAI,EAAE,iBAAiB,SAAS,EAAE,iBAAiB,MACjD,QAAO,UAAU;AAInB,KAAI,MAAM,MAAM,SAAS,CAAC,IAAI,MAAM,MAAM,SAAS,CAAC,CAClD,QAAO,MAAM,MAAM,SAAS,CAAC,IAAI,MAAM,MAAM,SAAS,CAAC;AAGzD,SAAQ,WAAR;EACE,KAAK,OACH,QAAO,MAAM,cAAc,KAAK,MAAM,cAAc;EAEtD,KAAK,QAGH,QAFe,KAAK,MAAM,MAAM,SAAS,IAAI,MAAO,KAAK,IAAI,KAC9C,KAAK,MAAM,MAAM,SAAS,IAAI,MAAO,KAAK,IAAI;EAG/D,KAAK,UAGH,QAFiB,KAAK,MAAM,MAAM,SAAS,IAAI,MAAO,IAAI,KACzC,KAAK,MAAM,MAAM,SAAS,IAAI,MAAO,IAAI;EAG5D,KAAK,UAGH,QAFiB,KAAK,MAAM,MAAM,SAAS,GAAG,IAAK,KAClC,KAAK,MAAM,MAAM,SAAS,GAAG,IAAK;EAIrD,QACE,QAAO,MAAM,SAAS,KAAK,MAAM,SAAS;;;;;;;;;;;;;;;;;;AClDhD,SAAgB,gBAAgB,OAAyB;AACvD,KAAI,UAAU,QAAQ,UAAU,KAAA,EAC9B,QAAO;AAIT,KAAI,OAAO,UAAU,WACnB,QAAO;AAIT,KAAI,OAAO,UAAU,SACnB,QAAO;AAIT,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO;AAIT,KACE,iBAAiB,QACjB,iBAAiB,WACjB,iBAAiB,UACjB,iBAAiB,SACjB,iBAAiB,OACjB,iBAAiB,OACjB,iBAAiB,WACjB,iBAAiB,WACjB,iBAAiB,eACjB,iBAAiB,SAEjB,QAAO;AAIT,KAAI,MAAM,aAAa,SAAS,aAC9B,QAAO;AAIT,KAAI,OAAO,gBAAgB,eAAe,iBAAiB,YACzD,QAAO;CAIT,MAAM,kBAAkB,MAAM,aAAa;AAC3C,KAAI,mBAAmB;EACrB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,SAAS,gBAAgB,CACzB,QAAO;AAGT,QAAO;;;;;;;;;;;;;;;;;ACpDT,SAAgB,YAAY,MAAiC,MAAmE;AAE9H,KAAI,SAAS,KACX,QAAO;AAIT,KAAI,QAAQ,QAAQ,QAAQ,KAC1B,QAAO;AAIT,KAAI,gBAAgB,QAAQ,gBAAgB,KAC1C,QAAO,QAAY,MAAM,KAAK;AAIhC,KAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,KAAK,CAC5C,QAAO,gBAAmB,MAAM,KAAK;AAIvC,KAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,KAAK,CAC5C,QAAO;AAIT,KAAI,gBAAgB,KAAK,IAAI,gBAAgB,KAAK,CAChD,QAAO,SAAS;CAIlB,MAAM,OAAO,MAAM,KAAK,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,KAAK,EAAE,GAAG,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC;CAE9E,MAAM,cAAiC,EAAE;AAEzC,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,OAAO,OAAO;EACpB,MAAM,OAAO,OAAO;AAEpB,MAAI,CAAC,QAAQ,KACX,aAAY,OAAO;WACV,QAAQ,CAAC,KAClB,aAAY,OAAO;OACd;GAEL,MAAM,SAAU,KAAa;GAC7B,MAAM,SAAU,KAAa;AAG7B,OAAI,MAAM,QAAQ,OAAO,IAAI,MAAM,QAAQ,OAAO;QAC5C,CAAC,gBAAmB,QAAQ,OAAO,CACrC,aAAY,OAAO;cAId,kBAAkB,QAAQ,kBAAkB;QAC/C,CAAC,QAAY,QAAQ,OAAO,CAC9B,aAAY,OAAO;cAId,gBAAgB,OAAO,IAAI,gBAAgB,OAAO;QACrD,WAAW,OACb,aAAY,OAAO;cAKrB,WAAW,QAAQ,WAAW,QAC9B,WAAW,KAAA,KAAa,WAAW,KAAA,KACnC,OAAO,WAAW,YAAY,OAAO,WAAW,YAChD,CAAC,gBAAgB,OAAO,IAAI,CAAC,gBAAgB,OAAO,EACpD;IACA,MAAM,eAAe,YAAY,QAAQ,OAAO;AAChD,QAAI,iBAAiB,KACnB,aAAY,OAAO,iBAAiB,QAAQ,QAAQ;cAI/C,WAAW,OAClB,aAAY,OAAO;;;AAKzB,QAAO,OAAO,KAAK,YAAY,CAAC,SAAS,IAAI,cAAc;;;;;;;;;;;;;;;;ACnG7D,SAAgB,UAAyC,QAAW,GAAG,SAAmC;AACxG,KAAI,CAAC,QAAQ,OAAQ,QAAO;CAC5B,MAAM,SAAS,QAAQ,OAAO;AAE9B,KAAI,CAAC,OAAQ,QAAO,UAAU,QAAQ,GAAG,QAAQ;AAEjD,MAAK,MAAM,OAAO,QAAQ;EACxB,MAAM,cAAc,OAAO;EAC3B,MAAM,cAAc,OAAO;AAE3B,MAAI,SAAS,YAAY,IAAI,SAAS,YAAY,CAC/C,QAAe,OAAO,UAAU,aAAa,YAAY;WACjD,gBAAgB,KAAA,EACxB,QAAe,OAAO;;AAI3B,QAAO,UAAU,QAAQ,GAAG,QAAQ;;AAGtC,SAAS,SAAS,MAA2B;AAC3C,QAAO,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,KAAK;;;;;;;;;;;;;;;;;ACpBjE,SAAgB,IAAa,KAAU,MAAc,cAAiC;CACpF,MAAM,OAAO,KAAK,MAAM,IAAI;CAC5B,IAAI,SAAS;AAEb,MAAK,MAAM,OAAO,MAAM;AACtB,MAAI,UAAU,QAAQ,OAAO,WAAW,SACtC,QAAO;AAET,WAAS,OAAO;;AAGlB,QAAO,WAAW,KAAA,IAAY,SAAS;;;;;;;;;;;;;;;;ACiBzC,SAAgB,oBAA4F,KAA0D;AACpK,QAAO,MAAM,OAAO,QAAQ,IAAI,CAC7B,QAAQ,CAAC,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CACjC,QAAQ,KAAK,CAAC,GAAG,QAAQ;EAAE,GAAG;GAAM,IAAI;EAAG,GAAG,EAAE,CAAe,GAAG;;;;;;;;;;;;;;;;;AC/BvE,SAAgB,IAAI,KAA0B,MAAc,OAAiC;CAC3F,MAAM,OAAO,KAAK,MAAM,IAAI;CAC5B,IAAI,UAAU;AAEd,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;EACxC,MAAM,MAAM,KAAK;AAEjB,MAAI,EAAE,OAAO,YAAY,OAAO,QAAQ,SAAS,YAAY,QAAQ,SAAS,KAC5E,SAAQ,OAAO,EAAE;AAGnB,YAAU,QAAQ;;AAGpB,SAAQ,KAAK,KAAK,SAAS,MAAM;AACjC,QAAO;;;;;;;;;;;;;;;;;;ACdT,SAAgB,cAAc,MAAe,MAAwB;AAEnE,KAAI,SAAS,KACX,QAAO;AAIT,KAAI,OAAO,SAAS,cAAc,OAAO,SAAS,WAChD,QAAO,SAAS;AAGlB,KAAI;AACF,SAAO,KAAK,UAAU,KAAK,KAAK,KAAK,UAAU,KAAK;SAC9C;AAEN,SAAO,SAAS"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../helpers/object/compact.ts","../../../helpers/object/deepClone.ts","../../../helpers/array/shallowEquals.ts","../../../helpers/date/compare.ts","../../../helpers/type/isSpecialObject.ts","../../../helpers/object/deepCompare.ts","../../../helpers/type/isPlainObject.ts","../../../helpers/object/deepMerge.ts","../../../helpers/object/get.ts","../../../helpers/object/omit.ts","../../../helpers/object/pick.ts","../../../helpers/type/isNullish.ts","../../../helpers/object/removeUndefinedNull.ts","../../../helpers/object/set.ts","../../../helpers/object/shallowEquals.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Removes all entries with falsy values (`false`, `null`, `undefined`, `0`, `\"\"`, `NaN`) from an object.\n * @param obj - The source object\n * @returns A new object containing only entries with truthy values\n * @example\n * compact({ a: 1, b: null, c: '', d: 0, e: 'hello' })\n * // => { a: 1, e: 'hello' }\n * @since 2.0.0\n */\nexport function compact<T extends Record<string, unknown>>(obj: T): Partial<T>;\nexport function compact(obj: undefined): undefined;\nexport function compact(obj: null): null;\nexport function compact<T extends Record<string, unknown>>(obj: T | undefined | null): Partial<T> | undefined | null {\n if (obj === undefined || obj === null) return obj;\n const result: Partial<T> = {};\n for (const key of Object.keys(obj) as (keyof T)[]) {\n if (obj[key]) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Creates a deep copy of an object or array\n * @param obj - The object to clone\n * @returns Deep cloned object\n * @since 1.9.0\n */\nexport function deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as T;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(item => deepClone(item)) as T;\n }\n\n const cloned = {} as T;\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n cloned[key] = deepClone(obj[key]);\n }\n }\n\n return cloned;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Quick comparison of two arrays using JSON.stringify.\n * This is a fast but simple comparison that may not work for all edge cases.\n *\n * @param arrA - First array to compare\n * @param arrB - Second array to compare\n * @returns `true` if arrays are identical according to JSON.stringify, `false` otherwise\n * @since 2.0.0\n */\nexport function shallowEquals<T>(arrA: T[], arrB: T[]): boolean {\n try {\n return JSON.stringify(arrA) === JSON.stringify(arrB);\n } catch {\n // If JSON.stringify fails (e.g., circular references), fall back to reference equality\n return arrA === arrB;\n }\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Options for date comparison\n * @since 2.0.0\n */\nexport interface DateCompareOptions {\n /** \n * Comparison precision\n * - 'milliseconds': Compare all including milliseconds (default)\n * - 'seconds': Ignore milliseconds, compare to the second\n * - 'minutes': Ignore seconds and milliseconds, compare to the minute\n * - 'hours': Ignore minutes, seconds and milliseconds, compare to the hour\n * - 'days': Compare only the date part (ignore time completely)\n */\n precision?: 'milliseconds' | 'seconds' | 'minutes' | 'hours' | 'days';\n}\n\n/**\n * Comparison of two dates.\n * \n * @param dateA - First date to compare\n * @param dateB - Second date to compare\n * @param options - Comparison options\n * @returns `true` if dates are identical according to the specified precision, `false` otherwise\n * @since 2.0.0\n */\nexport function compare(dateA: Date, dateB: Date, options: DateCompareOptions = {}): boolean {\n const { precision = 'milliseconds' } = options;\n\n if (!(dateA instanceof Date) || !(dateB instanceof Date)) {\n return dateA === dateB;\n }\n\n // Handle invalid dates\n if (isNaN(dateA.getTime()) || isNaN(dateB.getTime())) {\n return isNaN(dateA.getTime()) && isNaN(dateB.getTime());\n }\n\n switch (precision) {\n case 'days':\n return dateA.toDateString() === dateB.toDateString();\n\n case 'hours':\n const hoursA = Math.floor(dateA.getTime() / (1000 * 60 * 60));\n const hoursB = Math.floor(dateB.getTime() / (1000 * 60 * 60));\n return hoursA === hoursB;\n\n case 'minutes':\n const minutesA = Math.floor(dateA.getTime() / (1000 * 60));\n const minutesB = Math.floor(dateB.getTime() / (1000 * 60));\n return minutesA === minutesB;\n\n case 'seconds':\n const secondsA = Math.floor(dateA.getTime() / 1000);\n const secondsB = Math.floor(dateB.getTime() / 1000);\n return secondsA === secondsB;\n\n case 'milliseconds':\n default:\n return dateA.getTime() === dateB.getTime();\n }\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Determines if a value is a special object that should not have its properties compared deeply.\n * Special objects include: Date, Function, Promise, Observable, RegExp, Error, Map, Set, WeakMap, WeakSet, etc.\n * \n * @param value - The value to check\n * @returns `true` if the value is a special object, `false` otherwise\n * @since 2.0.0\n */\nexport function isSpecialObject(value: unknown): boolean {\n if (value === null || value === undefined) {\n return false;\n }\n\n // Functions are special objects\n if (typeof value === 'function') {\n return true;\n }\n\n // Non-objects are not special\n if (typeof value !== 'object') {\n return false;\n }\n\n // Arrays are not considered special objects for our purposes\n if (Array.isArray(value)) {\n return false;\n }\n\n // Check for built-in special objects\n if (\n value instanceof Date ||\n value instanceof Promise ||\n value instanceof RegExp ||\n value instanceof Error ||\n value instanceof Map ||\n value instanceof Set ||\n value instanceof WeakMap ||\n value instanceof WeakSet ||\n value instanceof ArrayBuffer ||\n value instanceof DataView\n ) {\n return true;\n }\n\n // Check for Observable (RxJS or similar)\n if (value.constructor?.name === 'Observable') {\n return true;\n }\n\n // Check for DOM elements (if in browser environment)\n if (typeof HTMLElement !== 'undefined' && value instanceof HTMLElement) {\n return true;\n }\n\n // Check for other built-in types by constructor name\n const constructorName = value.constructor?.name;\n if (constructorName && [\n 'Buffer', // Node.js Buffer\n 'URL', // Web URL API\n 'URLSearchParams',\n 'File', // Web File API\n 'Blob', // Web Blob API\n 'FormData', // Web FormData API\n 'Headers', // Web Headers API\n 'Request', // Web Request API\n 'Response', // Web Response API\n 'EventTarget', // Web EventTarget API\n 'Symbol' // Symbol objects\n ].includes(constructorName)) {\n return true;\n }\n\n return false;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { shallowEquals as shallowEqualsArray } from '../array/shallowEquals';\nimport { compare as compareDate } from '../date/compare';\nimport { isSpecialObject } from '../type/isSpecialObject';\n\n/**\n * Result type for deep comparison when objects are not identical\n * @since 2.0.0\n */\nexport interface DeepCompareResult {\n [key: string]: \"onlyA\" | \"onlyB\" | false | DeepCompareResult;\n}\n\n/**\n * Deep comparison of two objects that returns detailed information about differences.\n *\n * @param objA - First object to compare (can be object, undefined, or null)\n * @param objB - Second object to compare (can be object, undefined, or null)\n * @returns `true` if objects are identical, `false` if incompatible types, or a `DeepCompareResult` object detailing differences\n * @since 2.0.0\n */\nexport function deepCompare(objA: object | undefined | null, objB: object | undefined | null): true | false | DeepCompareResult {\n // Quick reference equality check\n if (objA === objB) {\n return true;\n }\n\n // Handle null/undefined cases\n if (objA == null || objB == null) {\n return false;\n }\n\n // Handle Date objects\n if (objA instanceof Date && objB instanceof Date) {\n return compareDate(objA, objB);\n }\n\n // Handle Arrays at root level - use shallowEquals\n if (Array.isArray(objA) && Array.isArray(objB)) {\n return shallowEqualsArray(objA, objB);\n }\n\n // If one is array and other is not\n if (Array.isArray(objA) || Array.isArray(objB)) {\n return false;\n }\n\n // Handle special objects - compare by reference only\n if (isSpecialObject(objA) || isSpecialObject(objB)) {\n return objA === objB;\n }\n\n // Handle objects - detailed comparison\n const keys = Array.from(new Set([...Object.keys(objA), ...Object.keys(objB)]));\n\n const differences: DeepCompareResult = {};\n\n for (const key of keys) {\n const hasA = key in objA;\n const hasB = key in objB;\n\n if (!hasA && hasB) {\n differences[key] = \"onlyB\";\n } else if (hasA && !hasB) {\n differences[key] = \"onlyA\";\n } else {\n // Both objects have this key - compare values\n const valueA = (objA as Record<string, unknown>)[key];\n const valueB = (objB as Record<string, unknown>)[key];\n\n // For arrays, use shallowEquals\n if (Array.isArray(valueA) && Array.isArray(valueB)) {\n if (!shallowEqualsArray(valueA, valueB)) {\n differences[key] = false;\n }\n }\n // For dates, use compareDate\n else if (valueA instanceof Date && valueB instanceof Date) {\n if (!compareDate(valueA, valueB)) {\n differences[key] = false;\n }\n }\n // For special objects, compare by reference only\n else if (isSpecialObject(valueA) || isSpecialObject(valueB)) {\n if (valueA !== valueB) {\n differences[key] = false;\n }\n }\n // For objects, recurse (only plain objects now)\n else if (\n valueA !== null && valueB !== null &&\n valueA !== undefined && valueB !== undefined &&\n typeof valueA === 'object' && typeof valueB === 'object' &&\n !isSpecialObject(valueA) && !isSpecialObject(valueB)\n ) {\n const nestedResult = deepCompare(valueA, valueB);\n if (nestedResult !== true) {\n differences[key] = nestedResult === false ? false : nestedResult;\n }\n }\n // For primitives and other types, check equality\n else if (valueA !== valueB) {\n differences[key] = false;\n }\n }\n }\n\n return Object.keys(differences).length > 0 ? differences : true;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a plain object.\n *\n * A plain object is created by `{}`, `new Object()`, or `Object.create(null)`.\n * Returns `false` for arrays, Date, Map, Set, RegExp, class instances, etc.\n *\n * @param value - The value to check\n * @returns True if value is a plain object\n * @example\n * isPlainObject({}) // => true\n * isPlainObject({ a: 1 }) // => true\n * isPlainObject(new Date()) // => false\n * isPlainObject([]) // => false\n * isPlainObject(null) // => false\n * @since 2.0.0\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { isPlainObject } from '../type/isPlainObject';\n\nconst UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Merges two or more objects deeply\n * @param target - The target object\n * @param sources - The source objects to merge\n * @returns The merged object\n * @since 1.9.0\n */\nexport function deepMerge<T extends Record<string, unknown>>(target: T, ...sources: Record<string, unknown>[]): T;\nexport function deepMerge(target: undefined, ...sources: Record<string, unknown>[]): undefined;\nexport function deepMerge(target: null, ...sources: Record<string, unknown>[]): null;\nexport function deepMerge<T extends Record<string, unknown>>(target: T | undefined | null, ...sources: Record<string, unknown>[]): T | undefined | null {\n if (target === undefined || target === null) return target;\n if (!sources.length) return target;\n const source = sources.shift();\n\n if (!source) return deepMerge(target, ...sources);\n\n for (const key in source) {\n if (!Object.hasOwn(source, key) || UNSAFE_KEYS.has(key)) continue;\n\n const targetValue = target[key];\n const sourceValue = source[key];\n\n if (isPlainObject(targetValue) && isPlainObject(sourceValue)) {\n (target as Record<string, unknown>)[key] = deepMerge(targetValue as Record<string, unknown>, sourceValue as Record<string, unknown>);\n } else if (sourceValue !== undefined) {\n (target as Record<string, unknown>)[key] = sourceValue;\n }\n }\n\n return deepMerge(target, ...sources);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Gets a value from an object using a dot-notated path\n * @param obj - The object to get value from\n * @param path - The dot-notated path (e.g., 'a.b.c')\n * @param defaultValue - Default value if path doesn't exist\n * @returns The value at the path or default value\n * @since 1.9.0\n */\nexport function get<T = unknown>(obj: unknown, path: string, defaultValue?: T): T | undefined {\n const keys = path.split('.');\n let result: unknown = obj;\n\n for (const key of keys) {\n if (result == null || typeof result !== 'object') {\n return defaultValue;\n }\n result = (result as Record<string, unknown>)[key];\n }\n\n return result !== undefined ? result as T | undefined : defaultValue;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Creates a new object without the specified keys.\n * @param obj - The source object\n * @param keys - The keys to omit\n * @returns A new object without the omitted keys\n * @example\n * omit({ a: 1, b: 2, c: 3 }, ['b'])\n * // => { a: 1, c: 3 }\n * @since 2.0.0\n */\nexport function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: readonly K[]): Omit<T, K>;\nexport function omit(obj: undefined, keys: readonly string[]): undefined;\nexport function omit(obj: null, keys: readonly string[]): null;\nexport function omit<T extends Record<string, unknown>, K extends keyof T>(\n obj: T | undefined | null,\n keys: readonly K[]\n): Omit<T, K> | undefined | null {\n if (obj === undefined || obj === null) return obj;\n const result = { ...obj };\n for (const key of keys) {\n delete result[key];\n }\n return result as Omit<T, K>;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Creates a new object with only the specified keys.\n * @param obj - The source object\n * @param keys - The keys to pick\n * @returns A new object with only the picked keys\n * @example\n * pick({ a: 1, b: 2, c: 3 }, ['a', 'c'])\n * // => { a: 1, c: 3 }\n * @since 2.0.0\n */\nexport function pick<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: readonly K[]): Pick<T, K>;\nexport function pick(obj: undefined, keys: readonly string[]): undefined;\nexport function pick(obj: null, keys: readonly string[]): null;\nexport function pick<T extends Record<string, unknown>, K extends keyof T>(\n obj: T | undefined | null,\n keys: readonly K[]\n): Pick<T, K> | undefined | null {\n if (obj === undefined || obj === null) return obj;\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is null or undefined (nullish).\n * @param value - The value to check\n * @returns True if value is null or undefined\n * @example\n * isNullish(null) // => true\n * isNullish(undefined) // => true\n * isNullish(0) // => false\n * isNullish('') // => false\n *\n * @see {@link isDefined} for the inverse check (`!isNullish`)\n * @see {@link isFalsy} to also catch `0`, `\"\"`, `false`, and `NaN`\n * @since 2.0.0\n */\nexport function isNullish(value: unknown): value is null | undefined {\n return value === null || value === undefined;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { isNullish } from '../type/isNullish';\n\n/**\n * Remove null and undefined values from an object.\n *\n * @param obj an object\n * @returns A shallow copy of the object without null or undefined values\n * @since 1.0.0\n */\nexport function removeUndefinedNull<T extends Record<string, string | number | boolean | null | undefined>>(obj: T): Partial<T>;\n\n/**\n * Remove null and undefined values from an object.\n *\n * @param obj a null object\n * @returns null\n * @since 1.0.0\n */\nexport function removeUndefinedNull(obj: null): null;\n\n/**\n * Remove null and undefined values from an object.\n *\n * @param obj an undefined object\n * @returns undefined\n * @since 1.0.0\n */\nexport function removeUndefinedNull(obj: undefined): undefined;\n\n/**\n * Remove null and undefined values from an object.\n *\n * @param obj an object\n * @returns A shallow copy of the object without null or undefined values\n * @since 1.0.0\n */\nexport function removeUndefinedNull<T extends Record<string, string | number | boolean | null | undefined>>(obj: T | null | undefined): Partial<T> | null | undefined {\n if (!obj) return obj;\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (!isNullish(v)) { result[k] = v; }\n }\n return result as Partial<T>;\n}\n\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nconst UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Sets a value in an object using a dot-notated path\n * @param obj - The object to set value in\n * @param path - The dot-notated path (e.g., 'a.b.c')\n * @param value - The value to set\n * @returns The modified object\n * @since 1.9.0\n */\nexport function set(obj: Record<string, unknown>, path: string, value: unknown): Record<string, unknown> {\n const keys = path.split('.');\n\n if (keys.some((k) => UNSAFE_KEYS.has(k))) return obj;\n\n let current: Record<string, unknown> = obj;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n\n if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {\n current[key] = {};\n }\n\n current = current[key] as Record<string, unknown>;\n }\n\n current[keys[keys.length - 1]] = value;\n return obj;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Quick comparison of two objects using JSON.stringify.\n * This is a fast but simple comparison that may not work for all edge cases.\n *\n * @param objA - First object to compare\n * @param objB - Second object to compare\n * @returns `true` if objects are identical according to JSON.stringify, `false` otherwise\n * @since 2.0.0\n */\nexport function shallowEquals(objA: unknown, objB: unknown): boolean {\n // Quick reference equality check\n if (objA === objB) {\n return true;\n }\n\n // Handle functions - they can't be JSON.stringified reliably\n if (typeof objA === 'function' || typeof objB === 'function') {\n return objA === objB;\n }\n\n try {\n return JSON.stringify(objA) === JSON.stringify(objB);\n } catch {\n // If JSON.stringify fails (e.g., circular references), fall back to basic comparison\n return objA === objB;\n }\n}\n"],"mappings":"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"}