@helpers4/object 2.0.0-alpha.21 → 2.0.0-alpha.22
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 +1 -0
- package/lib/index.d.ts +24 -1
- package/lib/index.js +10 -1
- package/lib/index.js.map +1 -1
- package/llms.txt +65 -1
- package/meta/api.json +72 -1
- package/meta/examples.json +20 -0
- package/package.json +2 -1
package/README.md
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -150,6 +150,29 @@ declare function removeUndefinedNull(obj: null): null;
|
|
|
150
150
|
*/
|
|
151
151
|
declare function removeUndefinedNull(obj: undefined): undefined;
|
|
152
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
|
+
|
|
153
176
|
/**
|
|
154
177
|
* This file is part of helpers4.
|
|
155
178
|
* Copyright (C) 2025 baxyz
|
|
@@ -181,5 +204,5 @@ declare function set(obj: Record<string, unknown>, path: string, value: unknown)
|
|
|
181
204
|
*/
|
|
182
205
|
declare function shallowEquals(objA: unknown, objB: unknown): boolean;
|
|
183
206
|
|
|
184
|
-
export { compact, deepClone, deepCompare, deepMerge, get, omit, pick, removeUndefinedNull, set, shallowEquals };
|
|
207
|
+
export { compact, deepClone, deepCompare, deepMerge, get, omit, pick, removeUndefinedNull, safeJsonParse, set, shallowEquals };
|
|
185
208
|
export type { DeepCompareResult };
|
package/lib/index.js
CHANGED
|
@@ -391,6 +391,15 @@ function removeUndefinedNull(obj) {
|
|
|
391
391
|
return result;
|
|
392
392
|
}
|
|
393
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
|
+
}
|
|
401
|
+
}
|
|
402
|
+
//#endregion
|
|
394
403
|
//#region helpers/object/set.ts
|
|
395
404
|
/**
|
|
396
405
|
* This file is part of helpers4.
|
|
@@ -448,6 +457,6 @@ function shallowEquals(objA, objB) {
|
|
|
448
457
|
}
|
|
449
458
|
}
|
|
450
459
|
//#endregion
|
|
451
|
-
export { compact, deepClone, deepCompare, deepMerge, get, omit, pick, removeUndefinedNull, set, shallowEquals };
|
|
460
|
+
export { compact, deepClone, deepCompare, deepMerge, get, omit, pick, removeUndefinedNull, safeJsonParse, set, shallowEquals };
|
|
452
461
|
|
|
453
462
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../helpers/object/compact.ts","../../../helpers/object/deepClone.ts","../../../helpers/array/shallowEquals.ts","../../../helpers/date/timestamp.ts","../../../helpers/date/ensureDate.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\nconst UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\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 (Object.prototype.hasOwnProperty.call(obj, key) && !UNSAFE_KEYS.has(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\nimport { ensureDate } from './ensureDate';\nimport type { DateLike } from './types.model';\n\n/**\n * Checks if a timestamp is likely in seconds (Java/Unix style) vs milliseconds (JavaScript style)\n * @param timestamp - The timestamp to check\n * @returns True if timestamp appears to be in seconds\n * @since 1.9.0\n */\nexport function isTimestampInSeconds(timestamp: number): boolean {\n // Timestamps before year 2001 in milliseconds are less than 10^10\n // Use Math.abs to correctly handle negative timestamps (before epoch)\n return Math.abs(timestamp) < 10000000000;\n}\n\n/**\n * Converts a timestamp to JavaScript milliseconds format\n * @param timestamp - The timestamp (in seconds or milliseconds)\n * @returns Timestamp in milliseconds\n * @since 1.9.0\n */\nexport function normalizeTimestamp(timestamp: number): number {\n return isTimestampInSeconds(timestamp) ? timestamp * 1000 : timestamp;\n}\n\n/**\n * Converts a date to a timestamp in **seconds** (epoch seconds).\n *\n * Use this when sending a date to a backend API that expects seconds\n * (e.g. Java `Instant.getEpochSecond()`, Python `time.time()`).\n *\n * @param date - The date to convert\n * @returns Seconds since the Unix epoch, or `null` for invalid input\n * @example\n * toSeconds('2025-01-19T12:00:00Z') // => 1737288000\n * toSeconds(null) // => null\n *\n * @since 2.0.0\n */\nexport function toSeconds(date: DateLike): number | null {\n const d = ensureDate(date);\n if (!d) return null;\n return Math.floor(d.getTime() / 1000);\n}\n\n/**\n * Converts a date to a timestamp in **milliseconds** (epoch millis).\n *\n * Use this when you need a plain number from a `DateLike` value\n * (e.g. for `Date.now()` comparisons, localStorage, or JS-native APIs).\n *\n * @param date - The date to convert\n * @returns Milliseconds since the Unix epoch, or `null` for invalid input\n * @example\n * toMillis('2025-01-19T12:00:00Z') // => 1737288000000\n * toMillis(null) // => null\n *\n * @since 2.0.0\n */\nexport function toMillis(date: DateLike): number | null {\n const d = ensureDate(date);\n if (!d) return null;\n return d.getTime();\n}\n\n/**\n * Creates a `Date` from a timestamp in **seconds**.\n *\n * Use this when receiving a timestamp from a backend that sends seconds\n * (e.g. Java `Instant.getEpochSecond()`). No heuristic — the input is\n * always treated as seconds.\n *\n * @param seconds - Seconds since the Unix epoch\n * @returns A valid `Date`, or `null` for `NaN` / non-finite input\n * @example\n * fromSeconds(1737288000) // => Date('2025-01-19T12:00:00Z')\n * fromSeconds(0) // => Date('1970-01-01T00:00:00Z')\n * fromSeconds(NaN) // => null\n *\n * @since 2.0.0\n */\nexport function fromSeconds(seconds: number): Date | null {\n if (!Number.isFinite(seconds)) return null;\n return new Date(seconds * 1000);\n}\n\n/**\n * Creates a `Date` from a timestamp in **milliseconds**.\n *\n * Use this when receiving a timestamp from a JS-native source\n * (e.g. `Date.now()`, `performance.timeOrigin`). No heuristic — the\n * input is always treated as milliseconds.\n *\n * @param ms - Milliseconds since the Unix epoch\n * @returns A valid `Date`, or `null` for `NaN` / non-finite input\n * @example\n * fromMillis(1737288000000) // => Date('2025-01-19T12:00:00Z')\n * fromMillis(0) // => Date('1970-01-01T00:00:00Z')\n * fromMillis(NaN) // => null\n *\n * @since 2.0.0\n */\nexport function fromMillis(ms: number): Date | null {\n if (!Number.isFinite(ms)) return null;\n return new Date(ms);\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 { normalizeTimestamp } from './timestamp';\nimport type { DateLike, EpochMilliseconds } from './types.model';\n\n/**\n * Type guard that checks whether a value satisfies the {@link EpochMilliseconds}\n * structural interface (e.g. `Temporal.Instant`, `Temporal.ZonedDateTime`).\n */\nfunction hasEpochMilliseconds(value: unknown): value is EpochMilliseconds {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'epochMilliseconds' in value &&\n typeof (value as EpochMilliseconds).epochMilliseconds === 'number'\n );\n}\n\n/**\n * Safely converts a date-like value to a valid `Date` object, or returns `null`.\n *\n * Accepts `Date`, timestamps (seconds or milliseconds, auto-detected), date strings,\n * and objects with an `epochMilliseconds` property (e.g. `Temporal.Instant`,\n * `Temporal.ZonedDateTime`).\n * Returns `null` for `null`, `undefined`, empty strings, `0`, and any value that\n * produces an invalid `Date`.\n *\n * This is the date equivalent of {@link ensureArray} — it normalizes flexible\n * input into a guaranteed type (or a safe fallback).\n *\n * @param input - A date-like value to convert\n * @returns A valid `Date` object, or `null` if the input is invalid\n * @example\n * ensureDate('2025-01-19T12:00:00Z') // => Date\n * ensureDate(1737290400) // => Date (from Unix seconds)\n * ensureDate(1737290400000) // => Date (from milliseconds)\n * ensureDate(new Date()) // => Date (same reference)\n * ensureDate(null) // => null\n * ensureDate('invalid') // => null\n *\n * @see {@link DateLike} for the accepted input types\n * @since 2.0.0\n */\nexport function ensureDate(input: DateLike | null | undefined): Date | null {\n if (input === null || input === undefined || input === '' || input === 0) {\n return null;\n }\n\n if (input instanceof Date) {\n return isNaN(input.getTime()) ? null : input;\n }\n\n if (hasEpochMilliseconds(input)) {\n const date = new Date(input.epochMilliseconds);\n return isNaN(date.getTime()) ? null : date;\n }\n\n if (typeof input === 'number') {\n const normalized = normalizeTimestamp(input);\n const date = new Date(normalized);\n return isNaN(date.getTime()) ? null : date;\n }\n\n if (typeof input === 'string') {\n const date = new Date(input);\n return isNaN(date.getTime()) ? null : date;\n }\n\n return 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 { ensureDate } from './ensureDate';\nimport type { DateLike } from './types.model';\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 * - 'months': Compare only year and month\n * - 'years': Compare only year\n */\n precision?: 'milliseconds' | 'seconds' | 'minutes' | 'hours' | 'days' | 'months' | 'years';\n}\n\n/**\n * Comparison of two dates.\n *\n * Accepts any {@link DateLike} input (Date, timestamp, or date string).\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 * @example\n * compare('2025-01-19', new Date('2025-01-19T08:00:00Z'), { precision: 'days' })\n * // => true\n * @since 2.0.0\n */\nexport function compare(dateA: DateLike, dateB: DateLike, options: DateCompareOptions = {}): boolean {\n const { precision = 'milliseconds' } = options;\n\n const a = ensureDate(dateA);\n const b = ensureDate(dateB);\n\n // Any invalid → not equal\n if (a === null || b === null) {\n return false;\n }\n\n switch (precision) {\n case 'years':\n return a.getFullYear() === b.getFullYear();\n\n case 'months':\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();\n\n case 'days':\n return a.toDateString() === b.toDateString();\n\n case 'hours': {\n const hoursA = Math.floor(a.getTime() / (1000 * 60 * 60));\n const hoursB = Math.floor(b.getTime() / (1000 * 60 * 60));\n return hoursA === hoursB;\n }\n\n case 'minutes': {\n const minutesA = Math.floor(a.getTime() / (1000 * 60));\n const minutesB = Math.floor(b.getTime() / (1000 * 60));\n return minutesA === minutesB;\n }\n\n case 'seconds': {\n const secondsA = Math.floor(a.getTime() / 1000);\n const secondsB = Math.floor(b.getTime() / 1000);\n return secondsA === secondsB;\n }\n\n case 'milliseconds':\n default:\n return a.getTime() === b.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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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../helpers/object/compact.ts","../../../helpers/object/deepClone.ts","../../../helpers/array/shallowEquals.ts","../../../helpers/date/timestamp.ts","../../../helpers/date/ensureDate.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/safeJsonParse.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\nconst UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\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 (Object.prototype.hasOwnProperty.call(obj, key) && !UNSAFE_KEYS.has(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\nimport { ensureDate } from './ensureDate';\nimport type { DateLike } from './types.model';\n\n/**\n * Checks if a timestamp is likely in seconds (Java/Unix style) vs milliseconds (JavaScript style)\n * @param timestamp - The timestamp to check\n * @returns True if timestamp appears to be in seconds\n * @since 1.9.0\n */\nexport function isTimestampInSeconds(timestamp: number): boolean {\n // Timestamps before year 2001 in milliseconds are less than 10^10\n // Use Math.abs to correctly handle negative timestamps (before epoch)\n return Math.abs(timestamp) < 10000000000;\n}\n\n/**\n * Converts a timestamp to JavaScript milliseconds format\n * @param timestamp - The timestamp (in seconds or milliseconds)\n * @returns Timestamp in milliseconds\n * @since 1.9.0\n */\nexport function normalizeTimestamp(timestamp: number): number {\n return isTimestampInSeconds(timestamp) ? timestamp * 1000 : timestamp;\n}\n\n/**\n * Converts a date to a timestamp in **seconds** (epoch seconds).\n *\n * Use this when sending a date to a backend API that expects seconds\n * (e.g. Java `Instant.getEpochSecond()`, Python `time.time()`).\n *\n * @param date - The date to convert\n * @returns Seconds since the Unix epoch, or `null` for invalid input\n * @example\n * toSeconds('2025-01-19T12:00:00Z') // => 1737288000\n * toSeconds(null) // => null\n *\n * @since 2.0.0\n */\nexport function toSeconds(date: DateLike): number | null {\n const d = ensureDate(date);\n if (!d) return null;\n return Math.floor(d.getTime() / 1000);\n}\n\n/**\n * Converts a date to a timestamp in **milliseconds** (epoch millis).\n *\n * Use this when you need a plain number from a `DateLike` value\n * (e.g. for `Date.now()` comparisons, localStorage, or JS-native APIs).\n *\n * @param date - The date to convert\n * @returns Milliseconds since the Unix epoch, or `null` for invalid input\n * @example\n * toMillis('2025-01-19T12:00:00Z') // => 1737288000000\n * toMillis(null) // => null\n *\n * @since 2.0.0\n */\nexport function toMillis(date: DateLike): number | null {\n const d = ensureDate(date);\n if (!d) return null;\n return d.getTime();\n}\n\n/**\n * Creates a `Date` from a timestamp in **seconds**.\n *\n * Use this when receiving a timestamp from a backend that sends seconds\n * (e.g. Java `Instant.getEpochSecond()`). No heuristic — the input is\n * always treated as seconds.\n *\n * @param seconds - Seconds since the Unix epoch\n * @returns A valid `Date`, or `null` for `NaN` / non-finite input\n * @example\n * fromSeconds(1737288000) // => Date('2025-01-19T12:00:00Z')\n * fromSeconds(0) // => Date('1970-01-01T00:00:00Z')\n * fromSeconds(NaN) // => null\n *\n * @since 2.0.0\n */\nexport function fromSeconds(seconds: number): Date | null {\n if (!Number.isFinite(seconds)) return null;\n return new Date(seconds * 1000);\n}\n\n/**\n * Creates a `Date` from a timestamp in **milliseconds**.\n *\n * Use this when receiving a timestamp from a JS-native source\n * (e.g. `Date.now()`, `performance.timeOrigin`). No heuristic — the\n * input is always treated as milliseconds.\n *\n * @param ms - Milliseconds since the Unix epoch\n * @returns A valid `Date`, or `null` for `NaN` / non-finite input\n * @example\n * fromMillis(1737288000000) // => Date('2025-01-19T12:00:00Z')\n * fromMillis(0) // => Date('1970-01-01T00:00:00Z')\n * fromMillis(NaN) // => null\n *\n * @since 2.0.0\n */\nexport function fromMillis(ms: number): Date | null {\n if (!Number.isFinite(ms)) return null;\n return new Date(ms);\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 { normalizeTimestamp } from './timestamp';\nimport type { DateLike, EpochMilliseconds } from './types.model';\n\n/**\n * Type guard that checks whether a value satisfies the {@link EpochMilliseconds}\n * structural interface (e.g. `Temporal.Instant`, `Temporal.ZonedDateTime`).\n */\nfunction hasEpochMilliseconds(value: unknown): value is EpochMilliseconds {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'epochMilliseconds' in value &&\n typeof (value as EpochMilliseconds).epochMilliseconds === 'number'\n );\n}\n\n/**\n * Safely converts a date-like value to a valid `Date` object, or returns `null`.\n *\n * Accepts `Date`, timestamps (seconds or milliseconds, auto-detected), date strings,\n * and objects with an `epochMilliseconds` property (e.g. `Temporal.Instant`,\n * `Temporal.ZonedDateTime`).\n * Returns `null` for `null`, `undefined`, empty strings, `0`, and any value that\n * produces an invalid `Date`.\n *\n * This is the date equivalent of {@link ensureArray} — it normalizes flexible\n * input into a guaranteed type (or a safe fallback).\n *\n * @param input - A date-like value to convert\n * @returns A valid `Date` object, or `null` if the input is invalid\n * @example\n * ensureDate('2025-01-19T12:00:00Z') // => Date\n * ensureDate(1737290400) // => Date (from Unix seconds)\n * ensureDate(1737290400000) // => Date (from milliseconds)\n * ensureDate(new Date()) // => Date (same reference)\n * ensureDate(null) // => null\n * ensureDate('invalid') // => null\n *\n * @see {@link DateLike} for the accepted input types\n * @since 2.0.0\n */\nexport function ensureDate(input: DateLike | null | undefined): Date | null {\n if (input === null || input === undefined || input === '' || input === 0) {\n return null;\n }\n\n if (input instanceof Date) {\n return isNaN(input.getTime()) ? null : input;\n }\n\n if (hasEpochMilliseconds(input)) {\n const date = new Date(input.epochMilliseconds);\n return isNaN(date.getTime()) ? null : date;\n }\n\n if (typeof input === 'number') {\n const normalized = normalizeTimestamp(input);\n const date = new Date(normalized);\n return isNaN(date.getTime()) ? null : date;\n }\n\n if (typeof input === 'string') {\n const date = new Date(input);\n return isNaN(date.getTime()) ? null : date;\n }\n\n return 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 { ensureDate } from './ensureDate';\nimport type { DateLike } from './types.model';\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 * - 'months': Compare only year and month\n * - 'years': Compare only year\n */\n precision?: 'milliseconds' | 'seconds' | 'minutes' | 'hours' | 'days' | 'months' | 'years';\n}\n\n/**\n * Comparison of two dates.\n *\n * Accepts any {@link DateLike} input (Date, timestamp, or date string).\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 * @example\n * compare('2025-01-19', new Date('2025-01-19T08:00:00Z'), { precision: 'days' })\n * // => true\n * @since 2.0.0\n */\nexport function compare(dateA: DateLike, dateB: DateLike, options: DateCompareOptions = {}): boolean {\n const { precision = 'milliseconds' } = options;\n\n const a = ensureDate(dateA);\n const b = ensureDate(dateB);\n\n // Any invalid → not equal\n if (a === null || b === null) {\n return false;\n }\n\n switch (precision) {\n case 'years':\n return a.getFullYear() === b.getFullYear();\n\n case 'months':\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();\n\n case 'days':\n return a.toDateString() === b.toDateString();\n\n case 'hours': {\n const hoursA = Math.floor(a.getTime() / (1000 * 60 * 60));\n const hoursB = Math.floor(b.getTime() / (1000 * 60 * 60));\n return hoursA === hoursB;\n }\n\n case 'minutes': {\n const minutesA = Math.floor(a.getTime() / (1000 * 60));\n const minutesB = Math.floor(b.getTime() / (1000 * 60));\n return minutesA === minutesB;\n }\n\n case 'seconds': {\n const secondsA = Math.floor(a.getTime() / 1000);\n const secondsB = Math.floor(b.getTime() / 1000);\n return secondsA === secondsB;\n }\n\n case 'milliseconds':\n default:\n return a.getTime() === b.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\n/**\n * Parses a JSON string, returning `null` (or a fallback) on any parse failure.\n *\n * Unlike `JSON.parse`, this never throws. Invalid JSON strings and other\n * parsing edge-cases resolve to `null` or the provided `fallback`.\n *\n * @param input - The JSON string to parse.\n * @param fallback - Value returned on failure. Defaults to `null` when omitted.\n * @returns The parsed value typed as `T`, or `fallback` on failure.\n * @example\n * safeJsonParse<{ a: number }>('{\"a\":1}') // => { a: 1 }\n * safeJsonParse('invalid') // => null\n * safeJsonParse('invalid', []) // => []\n * @since next\n */\nexport function safeJsonParse<T>(input: string): T | null;\nexport function safeJsonParse<T>(input: string, fallback: T): T;\nexport function safeJsonParse<T>(input: string, fallback?: T): T | null {\n try {\n return JSON.parse(input) as T;\n } catch {\n // Distinguish \"no fallback provided\" (→ null) from \"fallback is undefined\"\n // (→ undefined as T) to keep runtime behaviour aligned with the overloads.\n return arguments.length < 2 ? null : (fallback as 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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"}
|
package/llms.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @helpers4/object
|
|
2
2
|
|
|
3
3
|
> Tree-shakable TypeScript utility functions for the `object` domain.
|
|
4
|
-
> Package: `@helpers4/object` — Version: 2.0.0-alpha.
|
|
4
|
+
> Package: `@helpers4/object` — Version: 2.0.0-alpha.22
|
|
5
5
|
> License: LGPL-3.0-or-later
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
@@ -30,6 +30,7 @@ import { compact, deepClone, deepCompare, ... } from '@helpers4/object';
|
|
|
30
30
|
| `omit` | Creates a new object without the specified keys. |
|
|
31
31
|
| `pick` | Creates a new object with only the specified keys. |
|
|
32
32
|
| `removeUndefinedNull` | Remove null and undefined values from an object. |
|
|
33
|
+
| `safeJsonParse` | Parses a JSON string, returning `null` (or a fallback) on any parse failure. Unlike `JSON.parse`, t |
|
|
33
34
|
| `set` | Sets a value in an object using a dot-notated path |
|
|
34
35
|
| `shallowEquals` | Quick comparison of two objects using JSON.stringify. This is a fast but simple comparison that may |
|
|
35
36
|
|
|
@@ -450,6 +451,69 @@ removeUndefinedNull({ a: 1, b: null, c: undefined, d: 'ok' })
|
|
|
450
451
|
|
|
451
452
|
---
|
|
452
453
|
|
|
454
|
+
### `safeJsonParse`
|
|
455
|
+
|
|
456
|
+
Parses a JSON string, returning `null` (or a fallback) on any parse failure.
|
|
457
|
+
|
|
458
|
+
Unlike `JSON.parse`, this never throws. Invalid JSON strings and other
|
|
459
|
+
parsing edge-cases resolve to `null` or the provided `fallback`.
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
import { safeJsonParse } from '@helpers4/object';
|
|
463
|
+
|
|
464
|
+
safeJsonParse<T>(input: string): T | null
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Parameters:**
|
|
468
|
+
|
|
469
|
+
- `input: string` — The JSON string to parse.
|
|
470
|
+
|
|
471
|
+
**Returns:** `T | null` — The parsed value typed as `T`, or `fallback` on failure.
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
import { safeJsonParse } from '@helpers4/object';
|
|
475
|
+
|
|
476
|
+
safeJsonParse<T>(input: string, fallback: T): T
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
**Parameters:**
|
|
480
|
+
|
|
481
|
+
- `input: string` — The JSON string to parse.
|
|
482
|
+
- `fallback: T` — Value returned on failure. Defaults to `null` when omitted.
|
|
483
|
+
|
|
484
|
+
**Returns:** `T` — The parsed value typed as `T`, or `fallback` on failure.
|
|
485
|
+
|
|
486
|
+
**Examples:**
|
|
487
|
+
|
|
488
|
+
*Parse valid JSON*
|
|
489
|
+
|
|
490
|
+
Returns the parsed value when the input is valid JSON.
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
safeJsonParse<{ a: number }>('{"a":1}')
|
|
494
|
+
// => { a: 1 }
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
*Return null on invalid input*
|
|
498
|
+
|
|
499
|
+
Returns null instead of throwing when JSON is malformed.
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
safeJsonParse('invalid')
|
|
503
|
+
// => null
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
*Use a fallback value*
|
|
507
|
+
|
|
508
|
+
Returns the provided fallback when parsing fails.
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
safeJsonParse('invalid', [])
|
|
512
|
+
// => []
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
453
517
|
### `set`
|
|
454
518
|
|
|
455
519
|
Sets a value in an object using a dot-notated path
|
package/meta/api.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"category": "object",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.22",
|
|
4
4
|
"functions": [
|
|
5
5
|
{
|
|
6
6
|
"name": "compact",
|
|
@@ -542,6 +542,77 @@
|
|
|
542
542
|
],
|
|
543
543
|
"sourceFile": "removeUndefinedNull.ts"
|
|
544
544
|
},
|
|
545
|
+
{
|
|
546
|
+
"name": "safeJsonParse",
|
|
547
|
+
"kind": "function",
|
|
548
|
+
"description": "Parses a JSON string, returning `null` (or a fallback) on any parse failure.\n\nUnlike `JSON.parse`, this never throws. Invalid JSON strings and other\nparsing edge-cases resolve to `null` or the provided `fallback`.",
|
|
549
|
+
"since": "next",
|
|
550
|
+
"signatures": [
|
|
551
|
+
{
|
|
552
|
+
"signature": "safeJsonParse<T>(input: string): T | null",
|
|
553
|
+
"description": "Parses a JSON string, returning `null` (or a fallback) on any parse failure.\n\nUnlike `JSON.parse`, this never throws. Invalid JSON strings and other\nparsing edge-cases resolve to `null` or the provided `fallback`.",
|
|
554
|
+
"params": [
|
|
555
|
+
{
|
|
556
|
+
"name": "input",
|
|
557
|
+
"type": "string",
|
|
558
|
+
"description": "The JSON string to parse."
|
|
559
|
+
}
|
|
560
|
+
],
|
|
561
|
+
"returns": {
|
|
562
|
+
"type": "T | null",
|
|
563
|
+
"description": "The parsed value typed as `T`, or `fallback` on failure."
|
|
564
|
+
},
|
|
565
|
+
"typeParameters": [
|
|
566
|
+
{
|
|
567
|
+
"name": "T"
|
|
568
|
+
}
|
|
569
|
+
]
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
"signature": "safeJsonParse<T>(input: string, fallback: T): T",
|
|
573
|
+
"description": "Parses a JSON string, returning `null` (or a fallback) on any parse failure.\n\nUnlike `JSON.parse`, this never throws. Invalid JSON strings and other\nparsing edge-cases resolve to `null` or the provided `fallback`.",
|
|
574
|
+
"params": [
|
|
575
|
+
{
|
|
576
|
+
"name": "input",
|
|
577
|
+
"type": "string",
|
|
578
|
+
"description": "The JSON string to parse."
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
"name": "fallback",
|
|
582
|
+
"type": "T",
|
|
583
|
+
"description": "Value returned on failure. Defaults to `null` when omitted."
|
|
584
|
+
}
|
|
585
|
+
],
|
|
586
|
+
"returns": {
|
|
587
|
+
"type": "T",
|
|
588
|
+
"description": "The parsed value typed as `T`, or `fallback` on failure."
|
|
589
|
+
},
|
|
590
|
+
"typeParameters": [
|
|
591
|
+
{
|
|
592
|
+
"name": "T"
|
|
593
|
+
}
|
|
594
|
+
]
|
|
595
|
+
}
|
|
596
|
+
],
|
|
597
|
+
"examples": [
|
|
598
|
+
{
|
|
599
|
+
"title": "Parse valid JSON",
|
|
600
|
+
"description": "Returns the parsed value when the input is valid JSON.",
|
|
601
|
+
"code": "safeJsonParse<{ a: number }>('{\"a\":1}')\n// => { a: 1 }"
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
"title": "Return null on invalid input",
|
|
605
|
+
"description": "Returns null instead of throwing when JSON is malformed.",
|
|
606
|
+
"code": "safeJsonParse('invalid')\n// => null"
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
"title": "Use a fallback value",
|
|
610
|
+
"description": "Returns the provided fallback when parsing fails.",
|
|
611
|
+
"code": "safeJsonParse('invalid', [])\n// => []"
|
|
612
|
+
}
|
|
613
|
+
],
|
|
614
|
+
"sourceFile": "safeJsonParse.ts"
|
|
615
|
+
},
|
|
545
616
|
{
|
|
546
617
|
"name": "set",
|
|
547
618
|
"kind": "function",
|
package/meta/examples.json
CHANGED
|
@@ -106,6 +106,26 @@
|
|
|
106
106
|
}
|
|
107
107
|
]
|
|
108
108
|
},
|
|
109
|
+
{
|
|
110
|
+
"name": "safeJsonParse",
|
|
111
|
+
"examples": [
|
|
112
|
+
{
|
|
113
|
+
"title": "Parse valid JSON",
|
|
114
|
+
"description": "Returns the parsed value when the input is valid JSON.",
|
|
115
|
+
"code": "safeJsonParse<{ a: number }>('{\"a\":1}')\n// => { a: 1 }"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"title": "Return null on invalid input",
|
|
119
|
+
"description": "Returns null instead of throwing when JSON is malformed.",
|
|
120
|
+
"code": "safeJsonParse('invalid')\n// => null"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"title": "Use a fallback value",
|
|
124
|
+
"description": "Returns the provided fallback when parsing fails.",
|
|
125
|
+
"code": "safeJsonParse('invalid', [])\n// => []"
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
},
|
|
109
129
|
{
|
|
110
130
|
"name": "set",
|
|
111
131
|
"examples": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helpers4/object",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.22",
|
|
4
4
|
"description": "A set of helpers in TS/JS, compatible with tree-shaking, for object.",
|
|
5
5
|
"author": "baxyz <baxy@etik.com>",
|
|
6
6
|
"license": "LGPL-3.0",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"omit",
|
|
36
36
|
"pick",
|
|
37
37
|
"removeUndefinedNull",
|
|
38
|
+
"safeJsonParse",
|
|
38
39
|
"set",
|
|
39
40
|
"shallowEquals"
|
|
40
41
|
],
|