1 | /*!
|
---|
2 | * https://github.com/Starcounter-Jack/JSON-Patch
|
---|
3 | * (c) 2017-2021 Joachim Wester
|
---|
4 | * MIT license
|
---|
5 | */
|
---|
6 | import { _deepClone, _objectKeys, escapePathComponent, hasOwnProperty } from './helpers.mjs';
|
---|
7 | import { applyPatch } from './core.mjs';
|
---|
8 | var beforeDict = new WeakMap();
|
---|
9 | var Mirror = /** @class */ (function () {
|
---|
10 | function Mirror(obj) {
|
---|
11 | this.observers = new Map();
|
---|
12 | this.obj = obj;
|
---|
13 | }
|
---|
14 | return Mirror;
|
---|
15 | }());
|
---|
16 | var ObserverInfo = /** @class */ (function () {
|
---|
17 | function ObserverInfo(callback, observer) {
|
---|
18 | this.callback = callback;
|
---|
19 | this.observer = observer;
|
---|
20 | }
|
---|
21 | return ObserverInfo;
|
---|
22 | }());
|
---|
23 | function getMirror(obj) {
|
---|
24 | return beforeDict.get(obj);
|
---|
25 | }
|
---|
26 | function getObserverFromMirror(mirror, callback) {
|
---|
27 | return mirror.observers.get(callback);
|
---|
28 | }
|
---|
29 | function removeObserverFromMirror(mirror, observer) {
|
---|
30 | mirror.observers.delete(observer.callback);
|
---|
31 | }
|
---|
32 | /**
|
---|
33 | * Detach an observer from an object
|
---|
34 | */
|
---|
35 | export function unobserve(root, observer) {
|
---|
36 | observer.unobserve();
|
---|
37 | }
|
---|
38 | /**
|
---|
39 | * Observes changes made to an object, which can then be retrieved using generate
|
---|
40 | */
|
---|
41 | export function observe(obj, callback) {
|
---|
42 | var patches = [];
|
---|
43 | var observer;
|
---|
44 | var mirror = getMirror(obj);
|
---|
45 | if (!mirror) {
|
---|
46 | mirror = new Mirror(obj);
|
---|
47 | beforeDict.set(obj, mirror);
|
---|
48 | }
|
---|
49 | else {
|
---|
50 | var observerInfo = getObserverFromMirror(mirror, callback);
|
---|
51 | observer = observerInfo && observerInfo.observer;
|
---|
52 | }
|
---|
53 | if (observer) {
|
---|
54 | return observer;
|
---|
55 | }
|
---|
56 | observer = {};
|
---|
57 | mirror.value = _deepClone(obj);
|
---|
58 | if (callback) {
|
---|
59 | observer.callback = callback;
|
---|
60 | observer.next = null;
|
---|
61 | var dirtyCheck = function () {
|
---|
62 | generate(observer);
|
---|
63 | };
|
---|
64 | var fastCheck = function () {
|
---|
65 | clearTimeout(observer.next);
|
---|
66 | observer.next = setTimeout(dirtyCheck);
|
---|
67 | };
|
---|
68 | if (typeof window !== 'undefined') { //not Node
|
---|
69 | window.addEventListener('mouseup', fastCheck);
|
---|
70 | window.addEventListener('keyup', fastCheck);
|
---|
71 | window.addEventListener('mousedown', fastCheck);
|
---|
72 | window.addEventListener('keydown', fastCheck);
|
---|
73 | window.addEventListener('change', fastCheck);
|
---|
74 | }
|
---|
75 | }
|
---|
76 | observer.patches = patches;
|
---|
77 | observer.object = obj;
|
---|
78 | observer.unobserve = function () {
|
---|
79 | generate(observer);
|
---|
80 | clearTimeout(observer.next);
|
---|
81 | removeObserverFromMirror(mirror, observer);
|
---|
82 | if (typeof window !== 'undefined') {
|
---|
83 | window.removeEventListener('mouseup', fastCheck);
|
---|
84 | window.removeEventListener('keyup', fastCheck);
|
---|
85 | window.removeEventListener('mousedown', fastCheck);
|
---|
86 | window.removeEventListener('keydown', fastCheck);
|
---|
87 | window.removeEventListener('change', fastCheck);
|
---|
88 | }
|
---|
89 | };
|
---|
90 | mirror.observers.set(callback, new ObserverInfo(callback, observer));
|
---|
91 | return observer;
|
---|
92 | }
|
---|
93 | /**
|
---|
94 | * Generate an array of patches from an observer
|
---|
95 | */
|
---|
96 | export function generate(observer, invertible) {
|
---|
97 | if (invertible === void 0) { invertible = false; }
|
---|
98 | var mirror = beforeDict.get(observer.object);
|
---|
99 | _generate(mirror.value, observer.object, observer.patches, "", invertible);
|
---|
100 | if (observer.patches.length) {
|
---|
101 | applyPatch(mirror.value, observer.patches);
|
---|
102 | }
|
---|
103 | var temp = observer.patches;
|
---|
104 | if (temp.length > 0) {
|
---|
105 | observer.patches = [];
|
---|
106 | if (observer.callback) {
|
---|
107 | observer.callback(temp);
|
---|
108 | }
|
---|
109 | }
|
---|
110 | return temp;
|
---|
111 | }
|
---|
112 | // Dirty check if obj is different from mirror, generate patches and update mirror
|
---|
113 | function _generate(mirror, obj, patches, path, invertible) {
|
---|
114 | if (obj === mirror) {
|
---|
115 | return;
|
---|
116 | }
|
---|
117 | if (typeof obj.toJSON === "function") {
|
---|
118 | obj = obj.toJSON();
|
---|
119 | }
|
---|
120 | var newKeys = _objectKeys(obj);
|
---|
121 | var oldKeys = _objectKeys(mirror);
|
---|
122 | var changed = false;
|
---|
123 | var deleted = false;
|
---|
124 | //if ever "move" operation is implemented here, make sure this test runs OK: "should not generate the same patch twice (move)"
|
---|
125 | for (var t = oldKeys.length - 1; t >= 0; t--) {
|
---|
126 | var key = oldKeys[t];
|
---|
127 | var oldVal = mirror[key];
|
---|
128 | if (hasOwnProperty(obj, key) && !(obj[key] === undefined && oldVal !== undefined && Array.isArray(obj) === false)) {
|
---|
129 | var newVal = obj[key];
|
---|
130 | if (typeof oldVal == "object" && oldVal != null && typeof newVal == "object" && newVal != null && Array.isArray(oldVal) === Array.isArray(newVal)) {
|
---|
131 | _generate(oldVal, newVal, patches, path + "/" + escapePathComponent(key), invertible);
|
---|
132 | }
|
---|
133 | else {
|
---|
134 | if (oldVal !== newVal) {
|
---|
135 | changed = true;
|
---|
136 | if (invertible) {
|
---|
137 | patches.push({ op: "test", path: path + "/" + escapePathComponent(key), value: _deepClone(oldVal) });
|
---|
138 | }
|
---|
139 | patches.push({ op: "replace", path: path + "/" + escapePathComponent(key), value: _deepClone(newVal) });
|
---|
140 | }
|
---|
141 | }
|
---|
142 | }
|
---|
143 | else if (Array.isArray(mirror) === Array.isArray(obj)) {
|
---|
144 | if (invertible) {
|
---|
145 | patches.push({ op: "test", path: path + "/" + escapePathComponent(key), value: _deepClone(oldVal) });
|
---|
146 | }
|
---|
147 | patches.push({ op: "remove", path: path + "/" + escapePathComponent(key) });
|
---|
148 | deleted = true; // property has been deleted
|
---|
149 | }
|
---|
150 | else {
|
---|
151 | if (invertible) {
|
---|
152 | patches.push({ op: "test", path: path, value: mirror });
|
---|
153 | }
|
---|
154 | patches.push({ op: "replace", path: path, value: obj });
|
---|
155 | changed = true;
|
---|
156 | }
|
---|
157 | }
|
---|
158 | if (!deleted && newKeys.length == oldKeys.length) {
|
---|
159 | return;
|
---|
160 | }
|
---|
161 | for (var t = 0; t < newKeys.length; t++) {
|
---|
162 | var key = newKeys[t];
|
---|
163 | if (!hasOwnProperty(mirror, key) && obj[key] !== undefined) {
|
---|
164 | patches.push({ op: "add", path: path + "/" + escapePathComponent(key), value: _deepClone(obj[key]) });
|
---|
165 | }
|
---|
166 | }
|
---|
167 | }
|
---|
168 | /**
|
---|
169 | * Create an array of patches from the differences in two objects
|
---|
170 | */
|
---|
171 | export function compare(tree1, tree2, invertible) {
|
---|
172 | if (invertible === void 0) { invertible = false; }
|
---|
173 | var patches = [];
|
---|
174 | _generate(tree1, tree2, patches, '', invertible);
|
---|
175 | return patches;
|
---|
176 | }
|
---|