import { isRecord } from "@commontools/utils/types"; export function setValueAtPath( obj: any, path: PropertyKey[], value: any, ): boolean { let parent = obj; for (let i = 0; i < path.length - 1; i++) { const key = path[i]; if (typeof parent[key] !== "object") { parent[key] = typeof path[i + 1] === "number" ? [] : {}; } parent = parent[key]; } if (deepEqual(parent[path[path.length - 1]], value)) return false; if (value === undefined) { delete parent[path[path.length - 1]]; // Truncate array from the end for undefined values if (Array.isArray(parent)) { while (parent.length > 0 && parent[parent.length - 1] === undefined) { parent.pop(); } } } else parent[path[path.length - 1]] = value; return true; } export function getValueAtPath(obj: any, path: readonly PropertyKey[]): any { let current = obj; for (const key of path) { if (current === undefined || current === null) return undefined; current = current[key]; } return current; } export function hasValueAtPath(obj: any, path: PropertyKey[]): boolean { let current = obj; for (const key of path) { if (!isRecord(current) || !(key in current)) { return false; } current = current[key]; } return current !== undefined; } export const deepEqual = (a: any, b: any): boolean => { if (a === b) return true; if (isRecord(a) && isRecord(b)) { if (a.constructor !== b.constructor) return false; const keysA = Object.keys(a); const keysB = Object.keys(b); if (keysA.length !== keysB.length) return false; for (const key of keysA) { if (!keysB.includes(key)) return false; if (!deepEqual(a[key], b[key])) return false; } return true; } return a !== a && b !== b; // NaN check }; export function arrayEqual( a?: readonly PropertyKey[], b?: readonly PropertyKey[], ): boolean { if (!a || !b) return a === b; if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; return true; }