@tldraw/sync-core
Version:
tldraw infinite canvas SDK (multiplayer sync).
217 lines (216 loc) • 6.6 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var diff_exports = {};
__export(diff_exports, {
RecordOpType: () => RecordOpType,
ValueOpType: () => ValueOpType,
applyObjectDiff: () => applyObjectDiff,
diffRecord: () => diffRecord,
getNetworkDiff: () => getNetworkDiff
});
module.exports = __toCommonJS(diff_exports);
var import_utils = require("@tldraw/utils");
const RecordOpType = {
Put: "put",
Patch: "patch",
Remove: "remove"
};
function getNetworkDiff(diff) {
let res = null;
for (const [k, v] of (0, import_utils.objectMapEntries)(diff.added)) {
if (!res) res = {};
res[k] = [RecordOpType.Put, v];
}
for (const [from, to] of (0, import_utils.objectMapValues)(diff.updated)) {
const diff2 = diffRecord(from, to);
if (diff2) {
if (!res) res = {};
res[to.id] = [RecordOpType.Patch, diff2];
}
}
for (const removed of Object.keys(diff.removed)) {
if (!res) res = {};
res[removed] = [RecordOpType.Remove];
}
return res;
}
const ValueOpType = {
Put: "put",
Delete: "delete",
Append: "append",
Patch: "patch"
};
function diffRecord(prev, next) {
return diffObject(prev, next, /* @__PURE__ */ new Set(["props"]));
}
function diffObject(prev, next, nestedKeys) {
if (prev === next) {
return null;
}
let result = null;
for (const key of Object.keys(prev)) {
if (!(key in next)) {
if (!result) result = {};
result[key] = [ValueOpType.Delete];
continue;
}
const prevVal = prev[key];
const nextVal = next[key];
if (!(0, import_utils.isEqual)(prevVal, nextVal)) {
if (nestedKeys?.has(key) && prevVal && nextVal) {
const diff = diffObject(prevVal, nextVal);
if (diff) {
if (!result) result = {};
result[key] = [ValueOpType.Patch, diff];
}
} else if (Array.isArray(nextVal) && Array.isArray(prevVal)) {
const op = diffArray(prevVal, nextVal);
if (op) {
if (!result) result = {};
result[key] = op;
}
} else {
if (!result) result = {};
result[key] = [ValueOpType.Put, nextVal];
}
}
}
for (const key of Object.keys(next)) {
if (!(key in prev)) {
if (!result) result = {};
result[key] = [ValueOpType.Put, next[key]];
}
}
return result;
}
function diffValue(valueA, valueB) {
if (Object.is(valueA, valueB)) return null;
if (Array.isArray(valueA) && Array.isArray(valueB)) {
return diffArray(valueA, valueB);
} else if (!valueA || !valueB || typeof valueA !== "object" || typeof valueB !== "object") {
return (0, import_utils.isEqual)(valueA, valueB) ? null : [ValueOpType.Put, valueB];
} else {
const diff = diffObject(valueA, valueB);
return diff ? [ValueOpType.Patch, diff] : null;
}
}
function diffArray(prevArray, nextArray) {
if (Object.is(prevArray, nextArray)) return null;
if (prevArray.length === nextArray.length) {
const maxPatchIndexes = Math.max(prevArray.length / 5, 1);
const toPatchIndexes = [];
for (let i = 0; i < prevArray.length; i++) {
if (!(0, import_utils.isEqual)(prevArray[i], nextArray[i])) {
toPatchIndexes.push(i);
if (toPatchIndexes.length > maxPatchIndexes) {
return [ValueOpType.Put, nextArray];
}
}
}
if (toPatchIndexes.length === 0) {
return null;
}
const diff = {};
for (const i of toPatchIndexes) {
const prevItem = prevArray[i];
const nextItem = nextArray[i];
if (!prevItem || !nextItem) {
diff[i] = [ValueOpType.Put, nextItem];
} else if (typeof prevItem === "object" && typeof nextItem === "object") {
const op = diffValue(prevItem, nextItem);
if (op) {
diff[i] = op;
}
} else {
diff[i] = [ValueOpType.Put, nextItem];
}
}
return [ValueOpType.Patch, diff];
}
for (let i = 0; i < prevArray.length; i++) {
if (!(0, import_utils.isEqual)(prevArray[i], nextArray[i])) {
return [ValueOpType.Put, nextArray];
}
}
return [ValueOpType.Append, nextArray.slice(prevArray.length), prevArray.length];
}
function applyObjectDiff(object, objectDiff) {
if (!object || typeof object !== "object") return object;
const isArray = Array.isArray(object);
let newObject = void 0;
const set = (k, v) => {
if (!newObject) {
if (isArray) {
newObject = [...object];
} else {
newObject = { ...object };
}
}
if (isArray) {
newObject[Number(k)] = v;
} else {
newObject[k] = v;
}
};
for (const [key, op] of Object.entries(objectDiff)) {
switch (op[0]) {
case ValueOpType.Put: {
const value = op[1];
if (!(0, import_utils.isEqual)(object[key], value)) {
set(key, value);
}
break;
}
case ValueOpType.Append: {
const values = op[1];
const offset = op[2];
const arr = object[key];
if (Array.isArray(arr) && arr.length === offset) {
set(key, [...arr, ...values]);
}
break;
}
case ValueOpType.Patch: {
if (object[key] && typeof object[key] === "object") {
const diff = op[1];
const patched = applyObjectDiff(object[key], diff);
if (patched !== object[key]) {
set(key, patched);
}
}
break;
}
case ValueOpType.Delete: {
if (key in object) {
if (!newObject) {
if (isArray) {
console.error("Can't delete array item yet (this should never happen)");
newObject = [...object];
} else {
newObject = { ...object };
}
}
delete newObject[key];
}
}
}
}
return newObject ?? object;
}
//# sourceMappingURL=diff.js.map