@tanstack/db
Version:
A reactive client store for building super fast apps on sync
230 lines (229 loc) • 7.53 kB
JavaScript
import { UndefinedKeyError, InvalidKeyError } from "./errors.js";
const durationFields = [
`years`,
`months`,
`weeks`,
`days`,
`hours`,
`minutes`,
`seconds`,
`milliseconds`,
`microseconds`,
`nanoseconds`
];
function deepEquals(a, b) {
return deepEqualsInternal(a, b);
}
function sameRowValue(a, b) {
return deepEqualsInternal(a, b, ROW);
}
function samePublishedValue(a, b) {
return deepEqualsInternal(a, b, ROW | PUBLICATION);
}
function equalPersistedSnapshotValues(a, b) {
return deepEqualsInternal(a, b, DRAFT | PERSISTED);
}
function isPlainPrototype(prototype) {
return prototype === null || Object.getPrototypeOf(prototype) === null;
}
function enumerableOwnKeys(value) {
const keys = Object.keys(value);
for (const key of Object.getOwnPropertySymbols(value)) {
if (Object.prototype.propertyIsEnumerable.call(value, key)) keys.push(key);
}
return keys;
}
const DRAFT = 1;
const PERSISTED = 2;
const ROW = 4;
const PUBLICATION = 8;
function deepEqualsInternal(a, b, mode = 0, visited) {
const draft = mode & DRAFT;
if (!(mode & ROW) && a === b || Object.is(a, b)) return true;
if (a == null || b == null) return false;
if (typeof a !== typeof b) return false;
if (mode & PERSISTED && typeof a === `object` && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) {
return false;
}
if (visited?.has(a)) return visited.get(a) === b;
if (a instanceof Date) {
if (!(b instanceof Date)) return false;
return Object.is(a.getTime(), b.getTime());
}
if (b instanceof Date) return false;
if (a instanceof RegExp) {
if (!(b instanceof RegExp)) return false;
return a.source === b.source && a.flags === b.flags && (!draft || a.lastIndex === b.lastIndex);
}
if (b instanceof RegExp) return false;
if (a instanceof Map) {
if (!(b instanceof Map)) return false;
if (a.size !== b.size) return false;
(visited ??= /* @__PURE__ */ new Map()).set(a, b);
const bEntries = draft && Array.from(b.entries());
const result = Array.from(a.entries()).every(
([key, val], index) => bEntries ? Object.is(key, bEntries[index][0]) && deepEqualsInternal(val, bEntries[index][1], mode, visited) : b.has(key) && deepEqualsInternal(val, b.get(key), mode, visited)
);
visited.delete(a);
return result;
}
if (b instanceof Map) return false;
if (a instanceof Set) {
if (!(b instanceof Set)) return false;
if (a.size !== b.size) return false;
(visited ??= /* @__PURE__ */ new Map()).set(a, b);
const aValues = Array.from(a);
const bValues = Array.from(b);
if (draft) {
const result2 = deepEqualsInternal(aValues, bValues, mode, visited);
visited.delete(a);
return result2;
}
if (aValues.every((val) => typeof val !== `object`)) {
visited.delete(a);
return aValues.every((val) => b.has(val));
}
if (aValues.every(
(value, index) => deepEqualsInternal(value, bValues[index], mode, visited)
)) {
visited.delete(a);
return true;
}
const candidates = aValues.map(
(value) => bValues.flatMap(
(candidate, index) => deepEqualsInternal(value, candidate, mode, visited) ? [index] : []
)
);
const assigned = /* @__PURE__ */ new Map();
const match = (index, seen) => {
const options = candidates[index];
const free = options.find((candidate) => !assigned.has(candidate));
if (free !== void 0) {
assigned.set(free, index);
return true;
}
return options.some((candidate) => {
if (seen.has(candidate)) return false;
seen.add(candidate);
const previous = assigned.get(candidate);
if (previous !== void 0 && !match(previous, seen)) return false;
assigned.set(candidate, index);
return true;
});
};
const result = aValues.every((_, index) => match(index, /* @__PURE__ */ new Set()));
visited.delete(a);
return result;
}
if (b instanceof Set) return false;
const typed = ArrayBuffer.isView(a) && !(a instanceof DataView);
if (typed !== (ArrayBuffer.isView(b) && !(b instanceof DataView))) {
return false;
}
if (typed) {
const typedA = a;
const typedB = b;
return (!draft || Object.getPrototypeOf(a) === Object.getPrototypeOf(b)) && typedA.length === typedB.length && typedA.every((x, i) => deepEqualsInternal(x, typedB[i], mode));
}
if (isTemporal(a) && isTemporal(b)) {
const aTag = a[Symbol.toStringTag];
const bTag = b[Symbol.toStringTag];
if (aTag !== bTag) return false;
if (aTag === `Temporal.Duration`) {
const durationA = a;
const durationB = b;
return durationFields.every(
(field) => durationA[field] === durationB[field]
);
}
if (typeof a.equals === `function`) {
return a.equals(b);
}
return a.toString() === b.toString();
}
if (isTemporal(b)) return false;
if (Array.isArray(a) !== Array.isArray(b)) return false;
if (Array.isArray(a) && a.length !== b.length) return false;
if (Array.isArray(a) && !draft) {
(visited ??= /* @__PURE__ */ new Map()).set(a, b);
let result = true;
for (let index = 0; index < a.length; index++) {
if (!deepEqualsInternal(a[index], b[index], mode, visited)) {
result = false;
break;
}
}
visited.delete(a);
return result;
}
if (typeof a === `object`) {
const prototype = Object.getPrototypeOf(a);
const prototypeB = Object.getPrototypeOf(b);
const plain = isPlainPrototype(prototype);
const plainB = isPlainPrototype(prototypeB);
if (prototype !== prototypeB && (mode & ROW && !draft || !plain && !plainB))
return false;
if (mode & PUBLICATION && !(plain && plainB)) return false;
const keysA = enumerableOwnKeys(a);
const keysB = enumerableOwnKeys(b);
if (keysA.length !== keysB.length) return false;
if (keysA.length === 0 && !Array.isArray(a) && !(plain && plainB)) {
return a instanceof URL && a.href === b.href;
}
let registered = false;
let result = true;
for (const key of keysA) {
const value = a[key];
if (!registered && value !== null && typeof value === `object`) {
(visited ??= /* @__PURE__ */ new Map()).set(a, b);
registered = true;
}
if (!Object.prototype.propertyIsEnumerable.call(b, key) || !deepEqualsInternal(value, b[key], mode, visited)) {
result = false;
break;
}
}
if (registered) visited.delete(a);
return result;
}
return false;
}
const temporalTypes = /* @__PURE__ */ new Set([
`Temporal.Duration`,
`Temporal.Instant`,
`Temporal.PlainDate`,
`Temporal.PlainDateTime`,
`Temporal.PlainMonthDay`,
`Temporal.PlainTime`,
`Temporal.PlainYearMonth`,
`Temporal.ZonedDateTime`
]);
function isTemporal(a) {
if (a == null || typeof a !== `object`) return false;
const tag = a[Symbol.toStringTag];
return typeof tag === `string` && temporalTypes.has(tag);
}
const DEFAULT_COMPARE_OPTIONS = {
direction: `asc`,
nulls: `first`,
stringSort: `locale`
};
function assertCollectionKey(key, item) {
if (typeof key !== `string` && typeof key !== `number`) {
throw key === void 0 ? new UndefinedKeyError(item) : new InvalidKeyError(key, item);
}
}
export {
DEFAULT_COMPARE_OPTIONS,
DRAFT,
ROW,
assertCollectionKey,
deepEquals,
deepEqualsInternal,
durationFields,
equalPersistedSnapshotValues,
isTemporal,
samePublishedValue,
sameRowValue
};
//# sourceMappingURL=utils.js.map