/*! * https://github.com/Starcounter-Jack/JSON-Patch * (c) 2017-2021 Joachim Wester * MIT license */ import { _deepClone, _objectKeys, escapePathComponent, hasOwnProperty } from './helpers.mjs'; import { applyPatch } from './core.mjs'; var beforeDict = new WeakMap(); var Mirror = /** @class */ (function () { function Mirror(obj) { this.observers = new Map(); this.obj = obj; } return Mirror; }()); var ObserverInfo = /** @class */ (function () { function ObserverInfo(callback, observer) { this.callback = callback; this.observer = observer; } return ObserverInfo; }()); function getMirror(obj) { return beforeDict.get(obj); } function getObserverFromMirror(mirror, callback) { return mirror.observers.get(callback); } function removeObserverFromMirror(mirror, observer) { mirror.observers.delete(observer.callback); } /** * Detach an observer from an object */ export function unobserve(root, observer) { observer.unobserve(); } /** * Observes changes made to an object, which can then be retrieved using generate */ export function observe(obj, callback) { var patches = []; var observer; var mirror = getMirror(obj); if (!mirror) { mirror = new Mirror(obj); beforeDict.set(obj, mirror); } else { var observerInfo = getObserverFromMirror(mirror, callback); observer = observerInfo && observerInfo.observer; } if (observer) { return observer; } observer = {}; mirror.value = _deepClone(obj); if (callback) { observer.callback = callback; observer.next = null; var dirtyCheck = function () { generate(observer); }; var fastCheck = function () { clearTimeout(observer.next); observer.next = setTimeout(dirtyCheck); }; if (typeof window !== 'undefined') { //not Node window.addEventListener('mouseup', fastCheck); window.addEventListener('keyup', fastCheck); window.addEventListener('mousedown', fastCheck); window.addEventListener('keydown', fastCheck); window.addEventListener('change', fastCheck); } } observer.patches = patches; observer.object = obj; observer.unobserve = function () { generate(observer); clearTimeout(observer.next); removeObserverFromMirror(mirror, observer); if (typeof window !== 'undefined') { window.removeEventListener('mouseup', fastCheck); window.removeEventListener('keyup', fastCheck); window.removeEventListener('mousedown', fastCheck); window.removeEventListener('keydown', fastCheck); window.removeEventListener('change', fastCheck); } }; mirror.observers.set(callback, new ObserverInfo(callback, observer)); return observer; } /** * Generate an array of patches from an observer */ export function generate(observer, invertible) { if (invertible === void 0) { invertible = false; } var mirror = beforeDict.get(observer.object); _generate(mirror.value, observer.object, observer.patches, "", invertible); if (observer.patches.length) { applyPatch(mirror.value, observer.patches); } var temp = observer.patches; if (temp.length > 0) { observer.patches = []; if (observer.callback) { observer.callback(temp); } } return temp; } // Dirty check if obj is different from mirror, generate patches and update mirror function _generate(mirror, obj, patches, path, invertible) { if (obj === mirror) { return; } if (typeof obj.toJSON === "function") { obj = obj.toJSON(); } var newKeys = _objectKeys(obj); var oldKeys = _objectKeys(mirror); var changed = false; var deleted = false; //if ever "move" operation is implemented here, make sure this test runs OK: "should not generate the same patch twice (move)" for (var t = oldKeys.length - 1; t >= 0; t--) { var key = oldKeys[t]; var oldVal = mirror[key]; if (hasOwnProperty(obj, key) && !(obj[key] === undefined && oldVal !== undefined && Array.isArray(obj) === false)) { var newVal = obj[key]; if (typeof oldVal == "object" && oldVal != null && typeof newVal == "object" && newVal != null && Array.isArray(oldVal) === Array.isArray(newVal)) { _generate(oldVal, newVal, patches, path + "/" + escapePathComponent(key), invertible); } else { if (oldVal !== newVal) { changed = true; if (invertible) { patches.push({ op: "test", path: path + "/" + escapePathComponent(key), value: _deepClone(oldVal) }); } patches.push({ op: "replace", path: path + "/" + escapePathComponent(key), value: _deepClone(newVal) }); } } } else if (Array.isArray(mirror) === Array.isArray(obj)) { if (invertible) { patches.push({ op: "test", path: path + "/" + escapePathComponent(key), value: _deepClone(oldVal) }); } patches.push({ op: "remove", path: path + "/" + escapePathComponent(key) }); deleted = true; // property has been deleted } else { if (invertible) { patches.push({ op: "test", path: path, value: mirror }); } patches.push({ op: "replace", path: path, value: obj }); changed = true; } } if (!deleted && newKeys.length == oldKeys.length) { return; } for (var t = 0; t < newKeys.length; t++) { var key = newKeys[t]; if (!hasOwnProperty(mirror, key) && obj[key] !== undefined) { patches.push({ op: "add", path: path + "/" + escapePathComponent(key), value: _deepClone(obj[key]) }); } } } /** * Create an array of patches from the differences in two objects */ export function compare(tree1, tree2, invertible) { if (invertible === void 0) { invertible = false; } var patches = []; _generate(tree1, tree2, patches, '', invertible); return patches; }