value-semantics
Version:
Mimic value semantics for JavaScript objects with deep cloning and equality functions
467 lines (466 loc) • 11.3 kB
JavaScript
Symbol.metadata ?? (Symbol.metadata = Symbol("Symbol.metadata"));
const d = Symbol.for("meta-not-found");
function p(e, t) {
if ("constructor" in e && Symbol.metadata in e.constructor) {
const n = e.constructor[Symbol.metadata];
if (typeof n == "object" && n && t in n)
return n[t];
}
return d;
}
function O(e, t, n) {
var r;
e[r = Symbol.metadata] ?? (e[r] = {}), e[Symbol.metadata][t] = n;
}
const U = [
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array,
BigInt64Array,
BigUint64Array
], S = Symbol.for("clone-method"), P = Symbol.for("equals-method"), m = Symbol.for("clone-include-props"), g = Symbol.for("clone-exclude-props"), M = Symbol.for("equals-include-props"), j = Symbol.for("equals-exclude-props"), h = Symbol.for("constructor-props");
function q(e) {
return /* @__PURE__ */ new Set([...Object.getOwnPropertyNames(e), ...Object.getOwnPropertySymbols(e)]);
}
function F(e, t, n, r = !1) {
if (t === "include") {
let a = q(e);
const u = p(e, n === "clone" ? g : j);
if (u !== d && (a = a.difference(u)), n === "clone") {
const c = p(e, h);
if (c !== d && r)
for (const _ of c)
a.delete(_);
const k = p(e, m);
k !== d && (a = a.union(k));
}
return a;
}
const f = p(e, n === "clone" ? m : M);
return f !== d ? f : /* @__PURE__ */ new Set();
}
const v = {
ErrorOnClone: "Instances of class % cannot be cloned",
IncludeAndExclude: "A field cannot be decorated with both `@include` and `@exclude`"
};
class K extends Error {
constructor(t, n = "") {
const r = v[t].replace("%", n);
super(r), this.type = t;
}
}
const B = Symbol.for("error-on-clone");
function G(e) {
const t = e.constructor;
let n = p(e, h);
n === d && (n = []);
const r = n.map((o) => e[o]);
return new t(...r);
}
function Q(e) {
return Object.create(Object.getPrototypeOf(e));
}
function H(e, t, n, r) {
for (const o of e) {
const f = Object.getOwnPropertyDescriptor(n, o);
f.get || f.set ? Object.defineProperty(t, o, f) : Object.defineProperty(
t,
o,
{
value: w(n[o], r),
writable: f.writable,
configurable: f.configurable,
enumerable: f.enumerable
}
);
}
}
const s = (e, t) => {
O(e, S, t);
};
s(Array, function(e) {
const t = [];
return e.set(this, t), this.forEach((n, r) => t[r] = w(n, e)), t;
});
s(Set, function(e) {
const t = /* @__PURE__ */ new Set();
return e.set(this, t), this.forEach((n) => t.add(w(n, e))), t;
});
s(Map, function(e) {
const t = /* @__PURE__ */ new Map();
return e.set(this, t), this.forEach((n, r) => t.set(w(r, e), w(n, e))), t;
});
s(RegExp, function(e) {
const t = new RegExp(this);
return e.set(this, t), t;
});
s(Date, function(e) {
const t = new Date(this);
return e.set(this, t), t;
});
s(ArrayBuffer, function(e) {
const t = this.slice(0);
return e.set(this, t), t;
});
s(SharedArrayBuffer, function(e) {
const t = this.slice(0);
return e.set(this, t), t;
});
s(DataView, function(e) {
const t = new DataView(A(this.buffer), this.byteOffset, this.byteLength);
return e.set(this, t), t;
});
for (const e of U)
s(
e,
function(t) {
const n = new e(A(this.buffer), this.byteOffset, this.length);
return t.set(this, n), n;
}
);
function C(e) {
s(e, function(t) {
return t.set(this, this), this;
});
}
C(Boolean);
C(Number);
C(String);
O(BigInt, S, function(e) {
return e.set(this, this), this;
});
O(Symbol, S, function(e) {
return e.set(this, this), this;
});
function R(e) {
O(e, B, e.name);
}
R(WeakSet);
R(WeakMap);
R(WeakRef);
function A(e) {
return w(e, /* @__PURE__ */ new Map());
}
function w(e, t) {
if (typeof e == "object") {
if (e === null)
return null;
const n = p(e, B);
if (typeof n == "string")
throw new K("ErrorOnClone", n);
if (t.has(e))
return t.get(e);
const r = p(e, S);
if (r !== d)
return r.call(e, t);
const o = Object.getPrototypeOf(e), f = Object.create(o);
return t.set(e, f), H(q(e), f, e, t), f;
}
return e;
}
const I = ["deep", "returnOriginal", "errorOnClone"];
function J(e, t) {
const n = typeof e == "string" ? e : "deep";
t || (typeof e == "object" ? t = e : t = {});
const r = {
runConstructor: !1,
propDefault: "include",
...t
}, o = function(a) {
const i = r.runConstructor ? G(this) : Q(this);
a.set(this, i);
const u = F(this, r.propDefault, "clone", r.runConstructor);
return H(u, i, this, a), i;
}, f = function(a) {
return a.set(this, this), this;
};
return function(a, i) {
if (n === "returnOriginal")
i.metadata[S] = f;
else if (n === "errorOnClone")
i.metadata[B] = i.name ?? "(Anonymous class)";
else {
const u = i.metadata[m] ?? /* @__PURE__ */ new Set(), c = i.metadata[g] ?? /* @__PURE__ */ new Set();
if (!u.isDisjointFrom(c))
throw new K("IncludeAndExclude");
i.metadata[S] = o;
}
};
}
((e) => {
function t(o, f) {
var a;
(a = f.metadata)[m] ?? (a[m] = /* @__PURE__ */ new Set()), f.metadata[m].add(f.name);
}
e.include = t;
function n(o, f) {
var a;
(a = f.metadata)[g] ?? (a[g] = /* @__PURE__ */ new Set()), f.metadata[g].add(f.name);
}
e.exclude = n;
function r(o, f) {
var a;
(a = f.metadata)[h] ?? (a[h] = []), f.metadata[h].push(f.name);
}
e.constructorParam = r;
})(A || (A = {}));
const V = Symbol.for("ref-equals"), Z = [Boolean, Number, BigInt, String, Symbol];
function $(e, t) {
return e.map((n, r) => [n, t[r]]);
}
function L(e, t, n) {
const r = p(e, P);
if (r !== d)
throw r.call(e, t, n);
}
function N(e, t) {
if (p(e, V) !== d)
throw e === t;
}
function X(e, t, n) {
const r = n.get(e);
if (r !== void 0) {
const o = r.get(t);
if (o !== void 0)
return o;
}
return null;
}
function E(e, t, n, r) {
let o = n.get(e);
o === void 0 && (o = /* @__PURE__ */ new Map(), n.set(e, o)), o.set(t, r);
let f = n.get(t);
f === void 0 && (f = /* @__PURE__ */ new Map(), n.set(t, f)), f.set(e, r);
}
const l = (e, t) => {
O(e, P, t);
}, Y = (e) => {
O(e, V, !0);
};
function y(e, t) {
return Object.getPrototypeOf(e) === t.prototype;
}
function W(e, t) {
for (const n of Z)
if (e instanceof n)
return e.valueOf() === t;
return !1;
}
function ee(e, t, n) {
e: for (const r of e) {
for (const o of t)
if (b(r, o, n))
continue e;
return !1;
}
return !0;
}
function te(e, t, n) {
e: for (const r of e.keys()) {
for (const o of t.keys())
if (b(r, o, n)) {
if (!b(e.get(r), t.get(o), n))
return !1;
continue e;
}
return !1;
}
return !0;
}
l(
Array,
function(e, t) {
return !y(e, Array) || this.length !== e.length ? !1 : $(this, e).every(([n, r]) => b(n, r, t));
}
);
l(
Set,
function(e, t) {
return !y(e, Set) || this.size !== e.size ? !1 : ee(this, e, t);
}
);
l(
Map,
function(e, t) {
return !y(e, Map) || this.size !== e.size ? !1 : te(this, e, t);
}
);
l(
RegExp,
function(e, t) {
return y(e, RegExp) ? this.toString() === e.toString() : !1;
}
);
l(
Date,
function(e, t) {
return y(e, Date) ? +this == +e : !1;
}
);
l(
ArrayBuffer,
function(e, t) {
if (!y(e, ArrayBuffer) || this.byteLength !== e.byteLength)
return !1;
const n = new Uint8Array(this), r = new Uint8Array(e);
for (let o = 0; o < this.byteLength; o++)
if (n[o] !== r[o])
return !1;
return !0;
}
);
l(
SharedArrayBuffer,
function(e, t) {
if (!y(e, SharedArrayBuffer) || this.byteLength !== e.byteLength)
return !1;
const n = new Uint8Array(this), r = new Uint8Array(e);
for (let o = 0; o < this.byteLength; o++)
if (n[o] !== r[o])
return !1;
return !0;
}
);
l(
DataView,
function(e, t) {
if (!y(e, DataView) || this.byteLength !== e.byteLength)
return !1;
for (let n = 0; n < this.byteLength; n++)
if (this.getInt8(n) !== e.getInt8(n))
return !1;
return !0;
}
);
for (const e of U)
l(
e,
function(t, n) {
if (!y(t, e) || this.byteLength !== t.byteLength)
return !1;
for (let r = 0; r < this.byteLength; r++)
if (this[r] !== t[r])
return !1;
return !0;
}
);
l(
WeakRef,
function(e, t) {
return y(e, WeakRef) ? b(this.deref(), e.deref(), t) : !1;
}
);
Y(WeakSet);
Y(WeakMap);
function D(e, t) {
return b(e, t, /* @__PURE__ */ new Map());
}
function b(e, t, n) {
if (e === t)
return !0;
if (e === null || t === null)
return !1;
if (typeof e != "object")
return typeof e == "number" && isNaN(e) ? typeof t == "number" && isNaN(t) : typeof t == "object" ? W(t, e) : !1;
if (typeof t != "object")
return W(e, t);
const r = X(e, t, n);
if (r !== null)
return r;
E(e, t, n, !0);
try {
L(e, t, n), L(t, e, n), N(e, t), N(t, e);
} catch (a) {
if (typeof a == "boolean")
return E(e, t, n, a), a;
throw a;
}
if (Object.getPrototypeOf(e) !== Object.getPrototypeOf(t))
return E(e, t, n, !1), !1;
const o = q(e), f = q(t);
if (o.size !== f.size || o.intersection(f).size !== o.size)
return !1;
for (const a of o)
if (!b(e[a], t[a], n))
return E(e, t, n, !1), !1;
return E(e, t, n, !0), !0;
}
const x = ["value", "ref"];
function ne(e, t) {
return Object.getPrototypeOf(e) === Object.getPrototypeOf(t);
}
function re(e, t) {
const n = typeof e == "string" ? e : "value";
t || (typeof e == "object" ? t = e : t = {});
const r = { propDefault: "include", ...t };
return function(o, f) {
if (n === "ref") {
f.metadata[V] = !0;
return;
}
f.metadata[P] = function(a, i) {
if (!ne(this, a))
return !1;
const u = F(this, r.propDefault, "equals");
for (const c of u)
if (!b(this[c], a[c], i))
return !1;
return !0;
};
};
}
((e) => {
function t(r, o) {
var f;
(f = o.metadata)[M] ?? (f[M] = /* @__PURE__ */ new Set()), o.metadata[M].add(o.name);
}
e.include = t;
function n(r, o) {
var f;
(f = o.metadata)[j] ?? (f[j] = /* @__PURE__ */ new Set()), o.metadata[j].add(o.name);
}
e.exclude = n;
})(D || (D = {}));
var T;
((e) => {
e.clone = J, e.equals = re;
function t(n, r, o) {
return function(f, a) {
let i = "deep", u = "value", c = {};
typeof n == "string" ? (I.includes(n) && (i = n), x.includes(n) && (u = n)) : n && (c = n), typeof r == "string" ? (I.includes(r) && (i = r), x.includes(r) && (u = r)) : r && (c = r), o && (c = o);
const k = {
runConstructor: !1,
propDefault: "include",
...c
}, _ = {
propDefault: "include",
...c
};
i === "deep" ? e.clone("deep", k)(f, a) : e.clone(i)(f, a), u === "value" ? e.equals("value", _)(f, a) : e.equals(u)(f, a);
};
}
e.value = t;
})(T || (T = {}));
var z;
((e) => {
function t(r, o) {
A.include(r, o), D.include(r, o);
}
e.include = t;
function n(r, o) {
A.exclude(r, o), D.exclude(r, o);
}
e.exclude = n;
})(z || (z = {}));
export {
A as clone,
T as customize,
D as equals,
z as value
};