Files
client/node_modules/ohash/dist/utils/index.mjs
T
2026-10-07 18:56:27 +02:00

110 lines
3.3 KiB
JavaScript

import { isEqual, serialize } from "../_chunks/is-equal.mjs";
const PROTO_KEY = "__proto__";
function diff(obj1, obj2) {
const diffs = [];
_diff(obj1, obj2, "", diffs);
return diffs;
}
function _diff(v1, v2, key, out) {
if (v1 === v2) return;
const leaf1 = v1 === null || typeof v1 !== "object";
const leaf2 = v2 === null || typeof v2 !== "object";
if (!leaf1 && !leaf2) {
const entries1 = _entries(v1);
const entries2 = _entries(v2);
for (const [k, child1] of entries1) {
const childKey = key ? `${key}.${k}` : k;
if (entries2.has(k)) _diff(child1, entries2.get(k), childKey, out);
else out.push(new DiffEntry(childKey, "removed", void 0, _toHashedObject(child1, childKey)));
}
for (const [k, child2] of entries2) {
if (entries1.has(k)) continue;
const childKey = key ? `${key}.${k}` : k;
out.push(new DiffEntry(childKey, "added", _toHashedObject(child2, childKey)));
}
return;
}
if (_hasKeys(v1, leaf1) || _hasKeys(v2, leaf2)) return;
const node1 = _emptyOrLeafNode(v1, key, leaf1);
const node2 = _emptyOrLeafNode(v2, key, leaf2);
if (node1.hash !== node2.hash) out.push(new DiffEntry(key, "changed", node2, node1));
}
function _entries(value) {
const entries = /* @__PURE__ */ new Map();
for (const key in value) if (key !== PROTO_KEY) entries.set(key, value[key]);
return entries;
}
function _hasKeys(value, isLeaf) {
if (isLeaf) return false;
for (const key in value) if (key !== PROTO_KEY) return true;
return false;
}
function _emptyOrLeafNode(value, key, isLeaf) {
return isLeaf ? new DiffHashedObject(key, value, _leafHash(value)) : new DiffHashedObject(key, value, "{}", Object.create(null));
}
function _toHashedObject(obj, key = "") {
if (obj === null || typeof obj !== "object") return new DiffHashedObject(key, obj, _leafHash(obj));
const props = Object.create(null);
const hashes = [];
for (const _key in obj) {
if (_key === PROTO_KEY) continue;
const child = _toHashedObject(obj[_key], key ? `${key}.${_key}` : _key);
props[_key] = child;
hashes.push(child.hash);
}
return new DiffHashedObject(key, obj, `{${hashes.join(":")}}`, props);
}
function _leafHash(value) {
switch (typeof value) {
case "string": return `'${value}'`;
case "number":
case "boolean": return "" + value;
case "bigint": return `${value}n`;
default: return serialize(value);
}
}
var DiffEntry = class {
key;
type;
newValue;
oldValue;
constructor(key, type, newValue, oldValue) {
this.key = key;
this.type = type;
this.newValue = newValue;
this.oldValue = oldValue;
}
toString() {
return this.toJSON();
}
toJSON() {
switch (this.type) {
case "added": return `Added \`${this.key}\``;
case "removed": return `Removed \`${this.key}\``;
case "changed": return `Changed \`${this.key}\` from \`${this.oldValue?.toString() || "-"}\` to \`${this.newValue?.toString()}\``;
}
}
};
var DiffHashedObject = class {
key;
value;
hash;
props;
constructor(key, value, hash, props) {
this.key = key;
this.value = value;
this.hash = hash;
this.props = props;
}
toString() {
if (this.props) return `{${Object.keys(this.props).join(",")}}`;
else return JSON.stringify(this.value);
}
toJSON() {
const k = this.key || ".";
if (this.props) return `${k}({${Object.keys(this.props).join(",")}})`;
return `${k}(${this.value})`;
}
};
export { diff, isEqual };