@mml-io/networked-dom-document 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/common.d.ts +1 -1
- package/build/diffing.d.ts +1 -1
- package/build/index.js +491 -15
- package/build/index.js.map +4 -4
- package/build/rfc6902/deepEqual.d.ts +1 -0
- package/build/rfc6902/diff.d.ts +167 -0
- package/build/rfc6902/index.d.ts +4 -0
- package/build/rfc6902/patch.d.ts +102 -0
- package/build/rfc6902/pointer.d.ts +33 -0
- package/build/rfc6902/util.d.ts +10 -0
- package/package.json +4 -5
package/build/common.d.ts
CHANGED
package/build/diffing.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Diff, NodeDescription } from "@mml-io/networked-dom-protocol";
|
|
2
2
|
import { StaticVirtualDOMElement } from "@mml-io/observable-dom-common";
|
|
3
|
-
import * as rfc6902 from "rfc6902";
|
|
4
3
|
import { StaticVirtualDOMMutationRecord, VirtualDOMDiffStruct } from "./common";
|
|
4
|
+
import * as rfc6902 from "./rfc6902";
|
|
5
5
|
export declare const visibleToAttrName = "visible-to";
|
|
6
6
|
export declare const hiddenFromAttrName = "hidden-from";
|
|
7
7
|
/**
|
package/build/index.js
CHANGED
|
@@ -1,8 +1,484 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
1
|
+
// src/rfc6902/pointer.ts
|
|
2
|
+
function unescape(token) {
|
|
3
|
+
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
4
|
+
}
|
|
5
|
+
function escape(token) {
|
|
6
|
+
return token.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
7
|
+
}
|
|
8
|
+
var Pointer = class _Pointer {
|
|
9
|
+
constructor(tokens = [""]) {
|
|
10
|
+
this.tokens = tokens;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
`path` *must* be a properly escaped string.
|
|
14
|
+
*/
|
|
15
|
+
static fromJSON(path) {
|
|
16
|
+
const tokens = path.split("/").map(unescape);
|
|
17
|
+
if (tokens[0] !== "")
|
|
18
|
+
throw new Error(`Invalid JSON Pointer: ${path}`);
|
|
19
|
+
return new _Pointer(tokens);
|
|
20
|
+
}
|
|
21
|
+
toString() {
|
|
22
|
+
return this.tokens.map(escape).join("/");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
Returns an object with 'parent', 'key', and 'value' properties.
|
|
26
|
+
In the special case that this Pointer's path == "",
|
|
27
|
+
this object will be {parent: null, key: '', value: object}.
|
|
28
|
+
Otherwise, parent and key will have the property such that parent[key] == value.
|
|
29
|
+
*/
|
|
30
|
+
evaluate(object) {
|
|
31
|
+
let parent = null;
|
|
32
|
+
let key = "";
|
|
33
|
+
let value = object;
|
|
34
|
+
for (let i = 1, l = this.tokens.length; i < l; i++) {
|
|
35
|
+
parent = value;
|
|
36
|
+
key = this.tokens[i];
|
|
37
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
value = (parent || {})[key];
|
|
41
|
+
}
|
|
42
|
+
return { parent, key, value };
|
|
43
|
+
}
|
|
44
|
+
get(object) {
|
|
45
|
+
return this.evaluate(object).value;
|
|
46
|
+
}
|
|
47
|
+
set(object, value) {
|
|
48
|
+
let cursor = object;
|
|
49
|
+
for (let i = 1, l = this.tokens.length - 1, token = this.tokens[i]; i < l; i++) {
|
|
50
|
+
cursor = (cursor || {})[token];
|
|
51
|
+
}
|
|
52
|
+
if (cursor) {
|
|
53
|
+
cursor[this.tokens[this.tokens.length - 1]] = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
push(token) {
|
|
57
|
+
this.tokens.push(token);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
`token` should be a String. It'll be coerced to one anyway.
|
|
61
|
+
|
|
62
|
+
immutable (shallowly)
|
|
63
|
+
*/
|
|
64
|
+
add(token) {
|
|
65
|
+
const tokens = this.tokens.concat(String(token));
|
|
66
|
+
return new _Pointer(tokens);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/rfc6902/deepEqual.ts
|
|
71
|
+
function deepEqual(a, b) {
|
|
72
|
+
if (a === b) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
76
|
+
if (a.length !== b.length) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return a.every((elem, index) => {
|
|
80
|
+
return deepEqual(elem, b[index]);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (typeof a === "object" && typeof b === "object" && a !== null && b !== null) {
|
|
84
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const keys1 = Object.keys(a);
|
|
88
|
+
const keys2 = Object.keys(b);
|
|
89
|
+
if (keys1.length !== keys2.length || !keys1.every((key) => keys2.includes(key))) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
for (const key in a) {
|
|
93
|
+
if (!deepEqual(a[key], b[key])) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (keys1.length === 0 && a instanceof Date && b instanceof Date) {
|
|
98
|
+
return a.getTime() === b.getTime();
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/rfc6902/util.ts
|
|
106
|
+
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
107
|
+
function objectType(object) {
|
|
108
|
+
if (object === void 0) {
|
|
109
|
+
return "undefined";
|
|
110
|
+
}
|
|
111
|
+
if (object === null) {
|
|
112
|
+
return "null";
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(object)) {
|
|
115
|
+
return "array";
|
|
116
|
+
}
|
|
117
|
+
return typeof object;
|
|
118
|
+
}
|
|
119
|
+
function isNonPrimitive(value) {
|
|
120
|
+
return value != null && typeof value === "object";
|
|
121
|
+
}
|
|
122
|
+
function clone(source) {
|
|
123
|
+
if (!isNonPrimitive(source)) {
|
|
124
|
+
return source;
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(source)) {
|
|
127
|
+
const length = source.length;
|
|
128
|
+
const arrayTarget = new Array(length);
|
|
129
|
+
for (let i = 0; i < length; i++) {
|
|
130
|
+
arrayTarget[i] = clone(source[i]);
|
|
131
|
+
}
|
|
132
|
+
return arrayTarget;
|
|
133
|
+
}
|
|
134
|
+
if (source.constructor === Date) {
|
|
135
|
+
const dateTarget = /* @__PURE__ */ new Date(+source);
|
|
136
|
+
return dateTarget;
|
|
137
|
+
}
|
|
138
|
+
const objectTarget = {};
|
|
139
|
+
for (const key in source) {
|
|
140
|
+
if (hasOwnProperty.call(source, key)) {
|
|
141
|
+
objectTarget[key] = clone(source[key]);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return objectTarget;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/rfc6902/diff.ts
|
|
148
|
+
function wrapVoidableDiff(diff) {
|
|
149
|
+
function wrappedDiff(input, output, ptr) {
|
|
150
|
+
const custom_patch = diff(input, output, ptr);
|
|
151
|
+
return Array.isArray(custom_patch) ? custom_patch : diffAny(input, output, ptr, wrappedDiff);
|
|
152
|
+
}
|
|
153
|
+
return wrappedDiff;
|
|
154
|
+
}
|
|
155
|
+
function createPatch(input, output, diff) {
|
|
156
|
+
const ptr = new Pointer();
|
|
157
|
+
return (diff ? wrapVoidableDiff(diff) : diffAny)(input, output, ptr);
|
|
158
|
+
}
|
|
159
|
+
function subtract(minuend, subtrahend) {
|
|
160
|
+
const obj = {};
|
|
161
|
+
for (const add_key in minuend) {
|
|
162
|
+
if (hasOwnProperty.call(minuend, add_key) && minuend[add_key] !== void 0) {
|
|
163
|
+
obj[add_key] = 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
for (const del_key in subtrahend) {
|
|
167
|
+
if (hasOwnProperty.call(subtrahend, del_key) && subtrahend[del_key] !== void 0) {
|
|
168
|
+
delete obj[del_key];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return Object.keys(obj);
|
|
172
|
+
}
|
|
173
|
+
function intersection(objects) {
|
|
174
|
+
const length = objects.length;
|
|
175
|
+
const counter = {};
|
|
176
|
+
for (let i = 0; i < length; i++) {
|
|
177
|
+
const object = objects[i];
|
|
178
|
+
for (const key in object) {
|
|
179
|
+
if (hasOwnProperty.call(object, key) && object[key] !== void 0) {
|
|
180
|
+
counter[key] = (counter[key] || 0) + 1;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (const key in counter) {
|
|
185
|
+
if (counter[key] < length) {
|
|
186
|
+
delete counter[key];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return Object.keys(counter);
|
|
190
|
+
}
|
|
191
|
+
function isArrayAdd(array_operation) {
|
|
192
|
+
return array_operation.op === "add";
|
|
193
|
+
}
|
|
194
|
+
function isArrayRemove(array_operation) {
|
|
195
|
+
return array_operation.op === "remove";
|
|
196
|
+
}
|
|
197
|
+
function buildOperations(memo, i, j) {
|
|
198
|
+
let memoized = memo[i][j];
|
|
199
|
+
if (!memoized) {
|
|
200
|
+
throw new Error("invalid memo");
|
|
201
|
+
}
|
|
202
|
+
const operations = [];
|
|
203
|
+
while (memoized && memoized.prev && memoized.operation) {
|
|
204
|
+
operations.push(memoized.operation);
|
|
205
|
+
const index = memoized.prev.split(",");
|
|
206
|
+
memoized = memo[Number(index[0])][Number(index[1])];
|
|
207
|
+
}
|
|
208
|
+
return operations.reverse();
|
|
209
|
+
}
|
|
210
|
+
function diffArrays(input, output, ptr, diff = diffAny) {
|
|
211
|
+
if (diff === void 0) {
|
|
212
|
+
diff = diffAny;
|
|
213
|
+
}
|
|
214
|
+
const input_length = isNaN(input.length) || input.length <= 0 ? 0 : input.length;
|
|
215
|
+
const output_length = isNaN(output.length) || output.length <= 0 ? 0 : output.length;
|
|
216
|
+
let input_end = input_length;
|
|
217
|
+
let output_end = output_length;
|
|
218
|
+
while (input_end > 0 && output_end > 0) {
|
|
219
|
+
if (deepEqual(input[input_end - 1], output[output_end - 1])) {
|
|
220
|
+
input_end--;
|
|
221
|
+
output_end--;
|
|
222
|
+
} else {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const memo = new Array(input_end + 1);
|
|
227
|
+
for (let i = 0; i <= input_end; i++) {
|
|
228
|
+
memo[i] = new Array(output_end + 1);
|
|
229
|
+
}
|
|
230
|
+
memo[0][0] = { prev: null, operation: null, cost: 0 };
|
|
231
|
+
for (let i = 0; i <= input_end; i++) {
|
|
232
|
+
for (let j = 0; j <= output_end; j++) {
|
|
233
|
+
let memoized = memo[i][j];
|
|
234
|
+
if (memoized)
|
|
235
|
+
continue;
|
|
236
|
+
const add_prev_key = `${i},${j - 1}`;
|
|
237
|
+
const remove_prev_key = `${i - 1},${j}`;
|
|
238
|
+
const replace_prev_key = `${i - 1},${j - 1}`;
|
|
239
|
+
const remove_operation = {
|
|
240
|
+
op: "remove",
|
|
241
|
+
index: i - 1
|
|
242
|
+
};
|
|
243
|
+
const add_operation = {
|
|
244
|
+
op: "add",
|
|
245
|
+
index: i - 1,
|
|
246
|
+
value: output[j - 1]
|
|
247
|
+
};
|
|
248
|
+
if (j === 0) {
|
|
249
|
+
memoized = {
|
|
250
|
+
prev: remove_prev_key,
|
|
251
|
+
operation: remove_operation,
|
|
252
|
+
cost: memo[i - 1][j].cost + 1
|
|
253
|
+
};
|
|
254
|
+
} else if (i === 0) {
|
|
255
|
+
memoized = { prev: add_prev_key, operation: add_operation, cost: memo[i][j - 1].cost + 1 };
|
|
256
|
+
} else {
|
|
257
|
+
if (deepEqual(input[i - 1], output[j - 1])) {
|
|
258
|
+
memoized = memo[i - 1][j - 1];
|
|
259
|
+
} else {
|
|
260
|
+
const remove_prev = memo[i - 1][j];
|
|
261
|
+
const add_prev = memo[i][j - 1];
|
|
262
|
+
const replace_prev = memo[i - 1][j - 1];
|
|
263
|
+
const min_cost = Math.min(replace_prev.cost, add_prev.cost, remove_prev.cost);
|
|
264
|
+
if (remove_prev.cost === min_cost) {
|
|
265
|
+
memoized = {
|
|
266
|
+
prev: remove_prev_key,
|
|
267
|
+
operation: remove_operation,
|
|
268
|
+
cost: memo[i - 1][j].cost + 1
|
|
269
|
+
};
|
|
270
|
+
} else if (add_prev.cost === min_cost) {
|
|
271
|
+
memoized = {
|
|
272
|
+
prev: add_prev_key,
|
|
273
|
+
operation: add_operation,
|
|
274
|
+
cost: memo[i][j - 1].cost + 1
|
|
275
|
+
};
|
|
276
|
+
} else {
|
|
277
|
+
const replace_operation = {
|
|
278
|
+
op: "replace",
|
|
279
|
+
index: i - 1,
|
|
280
|
+
original: input[i - 1],
|
|
281
|
+
value: output[j - 1]
|
|
282
|
+
};
|
|
283
|
+
memoized = {
|
|
284
|
+
prev: replace_prev_key,
|
|
285
|
+
operation: replace_operation,
|
|
286
|
+
cost: memo[i - 1][j - 1].cost + 1
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
memo[i][j] = memoized;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const array_operations = buildOperations(memo, input_end, output_end);
|
|
295
|
+
const [padded_operations] = array_operations.reduce(
|
|
296
|
+
([operations, padding], array_operation) => {
|
|
297
|
+
if (isArrayAdd(array_operation)) {
|
|
298
|
+
const padded_index = array_operation.index + 1 + padding;
|
|
299
|
+
const index_token = padded_index < input_length + padding ? String(padded_index) : "-";
|
|
300
|
+
const operation = {
|
|
301
|
+
op: array_operation.op,
|
|
302
|
+
path: ptr.add(index_token).toString(),
|
|
303
|
+
value: array_operation.value
|
|
304
|
+
};
|
|
305
|
+
return [operations.concat(operation), padding + 1];
|
|
306
|
+
} else if (isArrayRemove(array_operation)) {
|
|
307
|
+
const operation = {
|
|
308
|
+
op: array_operation.op,
|
|
309
|
+
path: ptr.add(String(array_operation.index + padding)).toString()
|
|
310
|
+
};
|
|
311
|
+
return [operations.concat(operation), padding - 1];
|
|
312
|
+
} else {
|
|
313
|
+
const replace_ptr = ptr.add(String(array_operation.index + padding));
|
|
314
|
+
const replace_operations = diff(
|
|
315
|
+
array_operation.original,
|
|
316
|
+
array_operation.value,
|
|
317
|
+
replace_ptr
|
|
318
|
+
);
|
|
319
|
+
return [operations.concat(...replace_operations), padding];
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
[[], 0]
|
|
323
|
+
);
|
|
324
|
+
return padded_operations;
|
|
325
|
+
}
|
|
326
|
+
function diffObjects(input, output, ptr, diff = diffAny) {
|
|
327
|
+
const operations = [];
|
|
328
|
+
subtract(input, output).forEach((key) => {
|
|
329
|
+
operations.push({ op: "remove", path: ptr.add(key).toString() });
|
|
330
|
+
});
|
|
331
|
+
subtract(output, input).forEach((key) => {
|
|
332
|
+
operations.push({ op: "add", path: ptr.add(key).toString(), value: output[key] });
|
|
333
|
+
});
|
|
334
|
+
intersection([input, output]).forEach((key) => {
|
|
335
|
+
operations.push(...diff(input[key], output[key], ptr.add(key)));
|
|
336
|
+
});
|
|
337
|
+
return operations;
|
|
338
|
+
}
|
|
339
|
+
function diffAny(input, output, ptr, diff = diffAny) {
|
|
340
|
+
if (input === output) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
const input_type = objectType(input);
|
|
344
|
+
const output_type = objectType(output);
|
|
345
|
+
if (input_type === "array" && output_type === "array") {
|
|
346
|
+
return diffArrays(input, output, ptr, diff);
|
|
347
|
+
}
|
|
348
|
+
if (input_type === "object" && output_type === "object") {
|
|
349
|
+
return diffObjects(input, output, ptr, diff);
|
|
350
|
+
}
|
|
351
|
+
return [{ op: "replace", path: ptr.toString(), value: output }];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/rfc6902/patch.ts
|
|
355
|
+
var MissingError = class extends Error {
|
|
356
|
+
constructor(path) {
|
|
357
|
+
super(`Value required at path: ${path}`);
|
|
358
|
+
this.path = path;
|
|
359
|
+
this.name = "MissingError";
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
var TestError = class extends Error {
|
|
363
|
+
constructor(actual, expected) {
|
|
364
|
+
super(`Test failed: ${actual} != ${expected}`);
|
|
365
|
+
this.actual = actual;
|
|
366
|
+
this.expected = expected;
|
|
367
|
+
this.name = "TestError";
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
function applyPatch(object, patch) {
|
|
371
|
+
return patch.map((operation) => apply(object, operation));
|
|
372
|
+
}
|
|
373
|
+
function _add(object, key, value) {
|
|
374
|
+
if (Array.isArray(object)) {
|
|
375
|
+
if (key === "-") {
|
|
376
|
+
object.push(value);
|
|
377
|
+
} else {
|
|
378
|
+
const index = parseInt(key, 10);
|
|
379
|
+
object.splice(index, 0, value);
|
|
380
|
+
}
|
|
381
|
+
} else {
|
|
382
|
+
object[key] = value;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function _remove(object, key) {
|
|
386
|
+
if (Array.isArray(object)) {
|
|
387
|
+
const index = parseInt(key, 10);
|
|
388
|
+
object.splice(index, 1);
|
|
389
|
+
} else {
|
|
390
|
+
delete object[key];
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function add(object, operation) {
|
|
394
|
+
const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
|
|
395
|
+
if (endpoint.parent === void 0) {
|
|
396
|
+
return new MissingError(operation.path);
|
|
397
|
+
}
|
|
398
|
+
_add(endpoint.parent, endpoint.key, clone(operation.value));
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
function remove(object, operation) {
|
|
402
|
+
const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
|
|
403
|
+
if (endpoint.value === void 0) {
|
|
404
|
+
return new MissingError(operation.path);
|
|
405
|
+
}
|
|
406
|
+
_remove(endpoint.parent, endpoint.key);
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
function replace(object, operation) {
|
|
410
|
+
const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
|
|
411
|
+
if (endpoint.parent === null) {
|
|
412
|
+
return new MissingError(operation.path);
|
|
413
|
+
}
|
|
414
|
+
if (Array.isArray(endpoint.parent)) {
|
|
415
|
+
if (parseInt(endpoint.key, 10) >= endpoint.parent.length) {
|
|
416
|
+
return new MissingError(operation.path);
|
|
417
|
+
}
|
|
418
|
+
} else if (endpoint.value === void 0) {
|
|
419
|
+
return new MissingError(operation.path);
|
|
420
|
+
}
|
|
421
|
+
endpoint.parent[endpoint.key] = operation.value;
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
function move(object, operation) {
|
|
425
|
+
const from_endpoint = Pointer.fromJSON(operation.from).evaluate(object);
|
|
426
|
+
if (from_endpoint.value === void 0) {
|
|
427
|
+
return new MissingError(operation.from);
|
|
428
|
+
}
|
|
429
|
+
const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
|
|
430
|
+
if (endpoint.parent === void 0) {
|
|
431
|
+
return new MissingError(operation.path);
|
|
432
|
+
}
|
|
433
|
+
_remove(from_endpoint.parent, from_endpoint.key);
|
|
434
|
+
_add(endpoint.parent, endpoint.key, from_endpoint.value);
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
function copy(object, operation) {
|
|
438
|
+
const from_endpoint = Pointer.fromJSON(operation.from).evaluate(object);
|
|
439
|
+
if (from_endpoint.value === void 0) {
|
|
440
|
+
return new MissingError(operation.from);
|
|
441
|
+
}
|
|
442
|
+
const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
|
|
443
|
+
if (endpoint.parent === void 0) {
|
|
444
|
+
return new MissingError(operation.path);
|
|
445
|
+
}
|
|
446
|
+
_add(endpoint.parent, endpoint.key, clone(from_endpoint.value));
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
function test(object, operation) {
|
|
450
|
+
const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
|
|
451
|
+
if (diffAny(endpoint.value, operation.value, new Pointer()).length) {
|
|
452
|
+
return new TestError(endpoint.value, operation.value);
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
var InvalidOperationError = class extends Error {
|
|
457
|
+
constructor(operation) {
|
|
458
|
+
super(`Invalid operation: ${operation.op}`);
|
|
459
|
+
this.operation = operation;
|
|
460
|
+
this.name = "InvalidOperationError";
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
function apply(object, operation) {
|
|
464
|
+
switch (operation.op) {
|
|
465
|
+
case "add":
|
|
466
|
+
return add(object, operation);
|
|
467
|
+
case "remove":
|
|
468
|
+
return remove(object, operation);
|
|
469
|
+
case "replace":
|
|
470
|
+
return replace(object, operation);
|
|
471
|
+
case "move":
|
|
472
|
+
return move(object, operation);
|
|
473
|
+
case "copy":
|
|
474
|
+
return copy(object, operation);
|
|
475
|
+
case "test":
|
|
476
|
+
return test(object, operation);
|
|
477
|
+
}
|
|
478
|
+
return new InvalidOperationError(operation);
|
|
479
|
+
}
|
|
3
480
|
|
|
4
481
|
// src/diffing.ts
|
|
5
|
-
import * as rfc6902 from "rfc6902";
|
|
6
482
|
var visibleToAttrName = "visible-to";
|
|
7
483
|
var hiddenFromAttrName = "hidden-from";
|
|
8
484
|
function diffFromApplicationOfStaticVirtualDOMMutationRecordToConnection(mutation, parentNode, connectionId, visibleNodesForConnection) {
|
|
@@ -249,12 +725,12 @@ function findParentNodeOfNodeId(virtualDOMElement, targetNodeId) {
|
|
|
249
725
|
return null;
|
|
250
726
|
}
|
|
251
727
|
function virtualDOMDiffToVirtualDOMMutationRecord(virtualStructure, domDiff) {
|
|
252
|
-
const pointer =
|
|
728
|
+
const pointer = Pointer.fromJSON(domDiff.path);
|
|
253
729
|
const grandParentTokens = pointer.tokens.slice(0, pointer.tokens.length - 2);
|
|
254
730
|
const lastToken = pointer.tokens[pointer.tokens.length - 1];
|
|
255
731
|
const secondLastToken = pointer.tokens[pointer.tokens.length - 2];
|
|
256
732
|
if (lastToken === "textContent") {
|
|
257
|
-
const nodePointer = new
|
|
733
|
+
const nodePointer = new Pointer(pointer.tokens.slice(0, pointer.tokens.length - 1));
|
|
258
734
|
const node = nodePointer.get(virtualStructure);
|
|
259
735
|
return [
|
|
260
736
|
{
|
|
@@ -268,7 +744,7 @@ function virtualDOMDiffToVirtualDOMMutationRecord(virtualStructure, domDiff) {
|
|
|
268
744
|
];
|
|
269
745
|
}
|
|
270
746
|
if (secondLastToken === "attributes") {
|
|
271
|
-
const nodePointer = new
|
|
747
|
+
const nodePointer = new Pointer(grandParentTokens);
|
|
272
748
|
const node = nodePointer.get(virtualStructure);
|
|
273
749
|
return [
|
|
274
750
|
{
|
|
@@ -282,7 +758,7 @@ function virtualDOMDiffToVirtualDOMMutationRecord(virtualStructure, domDiff) {
|
|
|
282
758
|
];
|
|
283
759
|
}
|
|
284
760
|
if (secondLastToken === "childNodes") {
|
|
285
|
-
const nodePointer = new
|
|
761
|
+
const nodePointer = new Pointer(grandParentTokens);
|
|
286
762
|
const node = nodePointer.get(virtualStructure);
|
|
287
763
|
let previousSibling = null;
|
|
288
764
|
if (lastToken === "-") {
|
|
@@ -366,7 +842,7 @@ function virtualDOMDiffToVirtualDOMMutationRecord(virtualStructure, domDiff) {
|
|
|
366
842
|
throw new Error("Unhandled diff type");
|
|
367
843
|
}
|
|
368
844
|
function calculateStaticVirtualDOMDiff(originalState, latestState) {
|
|
369
|
-
const jsonPatchDiffs =
|
|
845
|
+
const jsonPatchDiffs = createPatch(
|
|
370
846
|
originalState,
|
|
371
847
|
latestState,
|
|
372
848
|
(a, b, ptr) => {
|
|
@@ -380,7 +856,7 @@ function calculateStaticVirtualDOMDiff(originalState, latestState) {
|
|
|
380
856
|
const virtualDOMDiffs = [];
|
|
381
857
|
for (const diff of jsonPatchDiffs) {
|
|
382
858
|
if (diff.op === "replace" && diff.path.endsWith("/nodeId")) {
|
|
383
|
-
const pointer =
|
|
859
|
+
const pointer = Pointer.fromJSON(diff.path);
|
|
384
860
|
const originalValue = pointer.get(originalState);
|
|
385
861
|
nodeIdRemappings.push({
|
|
386
862
|
internalNodeId: diff.value,
|
|
@@ -415,7 +891,7 @@ function getRemovedNodeIds(before, diff) {
|
|
|
415
891
|
}
|
|
416
892
|
}
|
|
417
893
|
if (diff.op === "replace" || diff.op === "remove") {
|
|
418
|
-
const removedNode =
|
|
894
|
+
const removedNode = Pointer.fromJSON(diff.path).get(before);
|
|
419
895
|
addNode(removedNode);
|
|
420
896
|
}
|
|
421
897
|
return removedIds;
|
|
@@ -457,7 +933,7 @@ function remapDuplicatedNodeIdsInOperations(virtualDOMDiffStruct, latestState) {
|
|
|
457
933
|
}
|
|
458
934
|
const existingNodeIds = getNodeIdsFromNodeAndChildren(before);
|
|
459
935
|
for (const diff of virtualDOMDiffs) {
|
|
460
|
-
const pointer =
|
|
936
|
+
const pointer = Pointer.fromJSON(diff.path);
|
|
461
937
|
const secondLastToken = pointer.tokens[pointer.tokens.length - 2];
|
|
462
938
|
if (secondLastToken !== "childNodes") {
|
|
463
939
|
continue;
|
|
@@ -473,7 +949,7 @@ function remapDuplicatedNodeIdsInOperations(virtualDOMDiffStruct, latestState) {
|
|
|
473
949
|
addingNodeIds.forEach((addingNodeId) => {
|
|
474
950
|
existingNodeIds.add(addingNodeId);
|
|
475
951
|
});
|
|
476
|
-
const patchErrors =
|
|
952
|
+
const patchErrors = applyPatch(before, [diff]);
|
|
477
953
|
if (patchErrors.length !== 1 || patchErrors[0] !== null) {
|
|
478
954
|
throw new Error("Patch failed");
|
|
479
955
|
}
|
|
@@ -648,7 +1124,7 @@ var _NetworkedDOM = class _NetworkedDOM {
|
|
|
648
1124
|
);
|
|
649
1125
|
if (virtualDOMDiff.path === "" && virtualDOMDiff.op === "replace") {
|
|
650
1126
|
} else {
|
|
651
|
-
const patchResults =
|
|
1127
|
+
const patchResults = applyPatch(domDiff.originalState, [virtualDOMDiff]);
|
|
652
1128
|
for (const patchResult of patchResults) {
|
|
653
1129
|
if (patchResult !== null) {
|
|
654
1130
|
console.error("Patching virtual dom structure resulted in error", patchResult);
|
|
@@ -686,7 +1162,7 @@ var _NetworkedDOM = class _NetworkedDOM {
|
|
|
686
1162
|
const asServerMessages = diffs;
|
|
687
1163
|
const firstDiff = diffs[0];
|
|
688
1164
|
firstDiff.documentTime = this.getDocumentTime();
|
|
689
|
-
const serializedDiffs = JSON.stringify(asServerMessages
|
|
1165
|
+
const serializedDiffs = JSON.stringify(asServerMessages);
|
|
690
1166
|
const webSocketContext = this.connectionIdToWebSocketContext.get(connectionId);
|
|
691
1167
|
if (!webSocketContext) {
|
|
692
1168
|
throw new Error(`webSocketContext not found in addExistingWebsockets`);
|
|
@@ -868,7 +1344,7 @@ var _NetworkedDOM = class _NetworkedDOM {
|
|
|
868
1344
|
diffsByConnectionId.forEach((diffs, connectionId) => {
|
|
869
1345
|
if (diffs.length > 0) {
|
|
870
1346
|
const asServerMessages = diffs;
|
|
871
|
-
const serializedDiffs = JSON.stringify(asServerMessages
|
|
1347
|
+
const serializedDiffs = JSON.stringify(asServerMessages);
|
|
872
1348
|
const webSocketContext = this.connectionIdToWebSocketContext.get(connectionId);
|
|
873
1349
|
if (!webSocketContext) {
|
|
874
1350
|
throw new Error(`webSocketContext not found in processModificationList`);
|