UNPKG

@tldraw/sync-core

Version:

tldraw infinite canvas SDK (multiplayer sync).

217 lines (216 loc) • 6.6 kB
"use strict"; 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