prostgles-types
Version:
Shared TypeScript object definitions for prostgles-client and prostgles-server
102 lines (87 loc) • 2.67 kB
text/typescript
/**
* Compare two objects for equality
* Returns false if any circular references are detected
*/
export const isEqual = (x: any, y: any, circularMode: CircularMode = "trace"): boolean => {
return isEqualWithSeen(x, y, undefined, circularMode);
};
type CircularMode = "quiet" | "trace" | "error";
const isEqualWithSeen = (
x: any,
y: any,
seen = new WeakSet(),
circularMode: CircularMode
): boolean => {
if (x === y) {
return true;
}
if (typeof x !== typeof y) {
return false;
}
if (typeof x === "object" && x !== null && typeof y === "object" && y !== null) {
if (x instanceof Date && y instanceof Date) {
return x.getTime() === y.getTime();
}
if (x instanceof RegExp && y instanceof RegExp) {
return x.source === y.source && x.flags === y.flags;
}
const xIsArray = Array.isArray(x);
const yIsArray = Array.isArray(y);
if (xIsArray && yIsArray) {
if (x.length !== y.length) return false;
for (let i = 0; i < x.length; i++) {
if (!isEqualWithSeen(x[i], y[i], seen, circularMode)) return false;
}
return true;
}
if (xIsArray !== yIsArray) {
return false;
}
if (ArrayBuffer.isView(x) && ArrayBuffer.isView(y)) {
if (x.byteLength !== y.byteLength) return false;
const xView = new Uint8Array(x.buffer, x.byteOffset, x.byteLength);
const yView = new Uint8Array(y.buffer, y.byteOffset, y.byteLength);
for (let i = 0; i < xView.length; i++) {
if (xView[i] !== yView[i]) return false;
}
return true;
}
if (x instanceof ArrayBuffer && y instanceof ArrayBuffer) {
if (x.byteLength !== y.byteLength) return false;
const xView = new Uint8Array(x);
const yView = new Uint8Array(y);
for (let i = 0; i < xView.length; i++) {
if (xView[i] !== yView[i]) return false;
}
return true;
}
const xKeys = Object.keys(x);
if (xKeys.length !== Object.keys(y).length) {
return false;
}
if (seen.has(x) || seen.has(y)) {
if (circularMode !== "quiet") {
console.trace("Circular reference detected in isEqual", x, y, seen);
}
if (circularMode === "error") {
throw new Error("Circular reference detected in isEqual");
}
return false;
}
seen.add(x);
seen.add(y);
for (const key of xKeys) {
if (key in y) {
const xProp = x[key];
const yProp = y[key];
if (!isEqualWithSeen(xProp, yProp, seen, circularMode)) {
return false;
}
} else {
return false;
}
}
return true;
}
return x !== x && y !== y;
};