aberdeen 1.0.5 → 1.0.6
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/dist/aberdeen.d.ts +3 -8
- package/dist/aberdeen.js +81 -77
- package/dist/aberdeen.js.map +4 -4
- package/dist/prediction.d.ts +2 -2
- package/dist/prediction.js +21 -22
- package/dist/prediction.js.map +3 -3
- package/dist/route.d.ts +2 -2
- package/dist/route.js +29 -15
- package/dist/route.js.map +3 -3
- package/dist/transitions.d.ts +14 -14
- package/dist/transitions.js +19 -6
- package/dist/transitions.js.map +3 -3
- package/dist-min/aberdeen.js +5 -5
- package/dist-min/aberdeen.js.map +4 -4
- package/dist-min/prediction.js +2 -2
- package/dist-min/prediction.js.map +3 -3
- package/dist-min/route.js +2 -2
- package/dist-min/route.js.map +3 -3
- package/dist-min/transitions.js +2 -2
- package/dist-min/transitions.js.map +3 -3
- package/package.json +2 -1
- package/src/aberdeen.ts +588 -400
- package/src/helpers/reverseSortedSet.ts +187 -178
- package/src/prediction.ts +73 -55
- package/src/route.ts +115 -97
- package/src/transitions.ts +49 -37
|
@@ -1,188 +1,197 @@
|
|
|
1
|
-
type Item<T> = T & {[idx: symbol]: Item<T>}
|
|
1
|
+
type Item<T> = T & { [idx: symbol]: Item<T> };
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A set-like collection of objects that can do iteration sorted by a specified index property.
|
|
5
5
|
* It also allows retrieving an object by its index property, and quickly getting the object
|
|
6
6
|
* that comes immediately after a given object.
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* It's implemented as a skiplist, maintaining all meta-data as part of the objects that it
|
|
9
9
|
* is tracking, for performance.
|
|
10
10
|
*/
|
|
11
11
|
export class ReverseSortedSet<T extends object> {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
12
|
+
// A fake item, that is not actually T, but *does* contain symbols pointing at the first item for each level.
|
|
13
|
+
private tail: Item<T>;
|
|
14
|
+
// As every SkipList instance has its own symbols, an object can be included in more than one SkipList.
|
|
15
|
+
private symbols: symbol[];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create an empty SortedSet.
|
|
19
|
+
*
|
|
20
|
+
* @param keyProp The name of the property that should be used as the index of this collection. Comparison
|
|
21
|
+
* using `<` will be done on this property, so it should probably be a number or a string (or something that
|
|
22
|
+
* has a useful toString-conversion).
|
|
23
|
+
*/
|
|
24
|
+
constructor(private keyProp: keyof T) {
|
|
25
|
+
this.tail = {} as Item<T>;
|
|
26
|
+
this.symbols = [Symbol(0)];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Add an object to the `SortedSet`.
|
|
31
|
+
*
|
|
32
|
+
* @param item The object to be added to the set. One or more properties with
|
|
33
|
+
* `Symbol` keys will be added to it, for `SortedSet` internals.
|
|
34
|
+
* @returns `true` if the item was added, or `false` if it was *not* added
|
|
35
|
+
* because the item already was part of this set.
|
|
36
|
+
*
|
|
37
|
+
* Note that though an item object may only be part of a particular `SortedSet`
|
|
38
|
+
* once, index properties may be duplicate and an item object may be part of
|
|
39
|
+
* more than one `SortedSet`.
|
|
40
|
+
*
|
|
41
|
+
* **IMPORTANT:** After adding an object, do not modify its index property,
|
|
42
|
+
* as this will lead to undefined (broken) behavior on the entire set.
|
|
43
|
+
*
|
|
44
|
+
* Time complexity: O(log n)
|
|
45
|
+
*/
|
|
46
|
+
add(item: T): boolean {
|
|
47
|
+
if (this.symbols[0] in item) return false; // Already included
|
|
48
|
+
|
|
49
|
+
// Start at level 1. Keep upping the level by 1 with 1/8 chance.
|
|
50
|
+
const level = 1 + (Math.clz32(Math.random() * 0xffffffff) >> 2);
|
|
51
|
+
for (let l = this.symbols.length; l < level; l++)
|
|
52
|
+
this.symbols.push(Symbol(l));
|
|
53
|
+
|
|
54
|
+
const keyProp = this.keyProp;
|
|
55
|
+
const key = item[keyProp];
|
|
56
|
+
|
|
57
|
+
let prev: Item<T> | undefined;
|
|
58
|
+
let current: Item<T> = this.tail;
|
|
59
|
+
for (let l = this.symbols.length - 1; l >= 0; l--) {
|
|
60
|
+
const symbol = this.symbols[l];
|
|
61
|
+
while ((prev = current[symbol] as Item<T>) && prev[keyProp] > key)
|
|
62
|
+
current = prev;
|
|
63
|
+
if (l < level) {
|
|
64
|
+
(item as any)[symbol] = current[symbol];
|
|
65
|
+
(current as any)[symbol] = item;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return true; // Added
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param item An object to test for inclusion in the set.
|
|
74
|
+
* @returns true if this object item is already part of the set.
|
|
75
|
+
*/
|
|
76
|
+
has(item: T): boolean {
|
|
77
|
+
return this.symbols[0] in item;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Remove and return the last item.
|
|
82
|
+
* @returns what was previously the last item in the sorted set, or `undefined` if the set was empty.
|
|
83
|
+
*/
|
|
84
|
+
fetchLast(): T | undefined {
|
|
85
|
+
const item = this.tail[this.symbols[0]];
|
|
86
|
+
if (item) {
|
|
87
|
+
this.remove(item);
|
|
88
|
+
return item;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @returns whether the set is empty (`true`) or has at least one item (`false`).
|
|
94
|
+
*/
|
|
95
|
+
isEmpty(): boolean {
|
|
96
|
+
return this.tail[this.symbols[0]] === undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Retrieve an item object based on its index property value.
|
|
101
|
+
*
|
|
102
|
+
* @param indexValue The index property value to search for.
|
|
103
|
+
* @returns `undefined` if the index property value does not exist in the `SortedSet` or
|
|
104
|
+
* otherwise the *first* item object that has this index value (meaning any further
|
|
105
|
+
* instances can be iterated using `next()`).
|
|
106
|
+
*
|
|
107
|
+
* Time complexity: O(log n)
|
|
108
|
+
*/
|
|
109
|
+
get(indexValue: string | number): T | undefined {
|
|
110
|
+
const keyProp = this.keyProp;
|
|
111
|
+
let current = this.tail;
|
|
112
|
+
let prev: Item<T> | undefined;
|
|
113
|
+
for (let l = this.symbols.length - 1; l >= 0; l--) {
|
|
114
|
+
const symbol = this.symbols[l];
|
|
115
|
+
while ((prev = current[symbol] as Item<T>) && prev[keyProp] > indexValue)
|
|
116
|
+
current = prev;
|
|
117
|
+
}
|
|
118
|
+
return current[this.symbols[0]]?.[keyProp] === indexValue
|
|
119
|
+
? current[this.symbols[0]]
|
|
120
|
+
: undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The iterator will go through the items in reverse index-order.
|
|
125
|
+
*/
|
|
126
|
+
*[Symbol.iterator](): IterableIterator<T> {
|
|
127
|
+
const symbol = this.symbols[0];
|
|
128
|
+
let node: Item<T> | undefined = this.tail[symbol] as Item<T>;
|
|
129
|
+
while (node) {
|
|
130
|
+
yield node;
|
|
131
|
+
node = node[symbol] as Item<T> | undefined;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Given an item object, returns the one that comes right before in the set.
|
|
137
|
+
* @param item The object to start from.
|
|
138
|
+
* @returns The next object, or `undefined` if there is none.
|
|
139
|
+
*
|
|
140
|
+
* Time complexity: O(1)
|
|
141
|
+
*/
|
|
142
|
+
prev(item: T): T | undefined {
|
|
143
|
+
return (item as Item<T>)[this.symbols[0]];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Remove an item object from the set, deleting all meta-data keys that
|
|
148
|
+
* were created on `add()`.
|
|
149
|
+
* @param item The object to be removed.
|
|
150
|
+
* @returns `true` on success or `false` when the item was not part of the set.
|
|
151
|
+
*
|
|
152
|
+
* Time complexity: O(log n)
|
|
153
|
+
*/
|
|
154
|
+
remove(item: T): boolean {
|
|
155
|
+
if (!(this.symbols[0] in item)) return false;
|
|
156
|
+
const keyProp = this.keyProp;
|
|
157
|
+
const prop = item[keyProp];
|
|
158
|
+
|
|
159
|
+
let prev: Item<T> | undefined;
|
|
160
|
+
let current: Item<T> = this.tail;
|
|
161
|
+
|
|
162
|
+
for (let l = this.symbols.length - 1; l >= 0; l--) {
|
|
163
|
+
const symbol = this.symbols[l];
|
|
164
|
+
while (
|
|
165
|
+
(prev = current[symbol] as Item<T>) &&
|
|
166
|
+
prev[keyProp] >= prop &&
|
|
167
|
+
prev !== item
|
|
168
|
+
)
|
|
169
|
+
current = prev;
|
|
170
|
+
if (prev === item) {
|
|
171
|
+
(current as any)[symbol] = prev[symbol];
|
|
172
|
+
delete prev[symbol];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return prev === item;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Remove all items for the set.
|
|
181
|
+
*
|
|
182
|
+
* Time complexity: O(n)
|
|
183
|
+
*/
|
|
184
|
+
clear(): void {
|
|
185
|
+
const symbol = this.symbols[0];
|
|
186
|
+
let current: Item<T> | undefined = this.tail;
|
|
187
|
+
while (current) {
|
|
188
|
+
const prev = current[symbol] as Item<T> | undefined;
|
|
189
|
+
for (const symbol of this.symbols) {
|
|
190
|
+
if (!(symbol in current)) break;
|
|
191
|
+
delete current[symbol];
|
|
192
|
+
}
|
|
193
|
+
current = prev;
|
|
194
|
+
}
|
|
195
|
+
this.tail = {} as Item<T>;
|
|
196
|
+
}
|
|
187
197
|
}
|
|
188
|
-
|
package/src/prediction.ts
CHANGED
|
@@ -1,70 +1,85 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { DatumType, TargetType } from
|
|
1
|
+
import { defaultEmitHandler, withEmitHandler } from "./aberdeen.js";
|
|
2
|
+
import type { DatumType, TargetType } from "./aberdeen.js";
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
5
|
* Represents a set of changes that can be applied to proxied objects.
|
|
6
6
|
* This is an opaque type - its internal structure is not part of the public API.
|
|
7
7
|
* @private
|
|
8
8
|
*/
|
|
9
9
|
export type Patch = Map<TargetType, Map<any, [DatumType, DatumType]>>;
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
function recordPatch(func: () => void): Patch {
|
|
13
|
-
const recordingPatch = new Map()
|
|
14
|
-
withEmitHandler(
|
|
15
|
-
addToPatch(recordingPatch, target, index, newData, oldData)
|
|
16
|
-
}, func)
|
|
17
|
-
return recordingPatch
|
|
12
|
+
const recordingPatch = new Map();
|
|
13
|
+
withEmitHandler((target, index, newData, oldData) => {
|
|
14
|
+
addToPatch(recordingPatch, target, index, newData, oldData);
|
|
15
|
+
}, func);
|
|
16
|
+
return recordingPatch;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
function addToPatch(
|
|
21
|
-
|
|
19
|
+
function addToPatch(
|
|
20
|
+
patch: Patch,
|
|
21
|
+
collection: TargetType,
|
|
22
|
+
index: any,
|
|
23
|
+
newData: DatumType,
|
|
24
|
+
oldData: DatumType,
|
|
25
|
+
) {
|
|
26
|
+
let collectionMap = patch.get(collection);
|
|
22
27
|
if (!collectionMap) {
|
|
23
|
-
collectionMap = new Map()
|
|
24
|
-
patch.set(collection, collectionMap)
|
|
28
|
+
collectionMap = new Map();
|
|
29
|
+
patch.set(collection, collectionMap);
|
|
25
30
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (newData ===
|
|
29
|
-
else collectionMap.set(index, [newData,
|
|
31
|
+
const prev = collectionMap.get(index);
|
|
32
|
+
const oldData0 = prev ? prev[1] : oldData;
|
|
33
|
+
if (newData === oldData0) collectionMap.delete(index);
|
|
34
|
+
else collectionMap.set(index, [newData, oldData0]);
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
function emitPatch(patch: Patch) {
|
|
33
|
-
for(
|
|
34
|
-
for(
|
|
38
|
+
for (const [collection, collectionMap] of patch) {
|
|
39
|
+
for (const [index, [newData, oldData]] of collectionMap) {
|
|
35
40
|
defaultEmitHandler(collection, index, newData, oldData);
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
function mergePatch(target: Patch, source: Patch, reverse
|
|
41
|
-
for(
|
|
42
|
-
for(
|
|
43
|
-
addToPatch(
|
|
45
|
+
function mergePatch(target: Patch, source: Patch, reverse = false) {
|
|
46
|
+
for (const [collection, collectionMap] of source) {
|
|
47
|
+
for (const [index, [newData, oldData]] of collectionMap) {
|
|
48
|
+
addToPatch(
|
|
49
|
+
target,
|
|
50
|
+
collection,
|
|
51
|
+
index,
|
|
52
|
+
reverse ? oldData : newData,
|
|
53
|
+
reverse ? newData : oldData,
|
|
54
|
+
);
|
|
44
55
|
}
|
|
45
56
|
}
|
|
46
57
|
}
|
|
47
58
|
|
|
48
|
-
function silentlyApplyPatch(patch: Patch, force
|
|
49
|
-
for(
|
|
50
|
-
for(
|
|
51
|
-
|
|
59
|
+
function silentlyApplyPatch(patch: Patch, force = false): boolean {
|
|
60
|
+
for (const [collection, collectionMap] of patch) {
|
|
61
|
+
for (const [index, [newData, oldData]] of collectionMap) {
|
|
62
|
+
const actualData = (collection as any)[index];
|
|
52
63
|
if (actualData !== oldData) {
|
|
53
|
-
if (force)
|
|
54
|
-
|
|
64
|
+
if (force)
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Applying invalid patch: data ${actualData} is unequal to expected old data ${oldData} for index ${index}`,
|
|
68
|
+
);
|
|
69
|
+
}, 0);
|
|
70
|
+
else return false;
|
|
55
71
|
}
|
|
56
72
|
}
|
|
57
73
|
}
|
|
58
|
-
for(
|
|
59
|
-
for(
|
|
60
|
-
(collection as any)[index] = newData
|
|
74
|
+
for (const [collection, collectionMap] of patch) {
|
|
75
|
+
for (const [index, [newData, oldData]] of collectionMap) {
|
|
76
|
+
(collection as any)[index] = newData;
|
|
61
77
|
}
|
|
62
78
|
}
|
|
63
|
-
return true
|
|
79
|
+
return true;
|
|
64
80
|
}
|
|
65
81
|
|
|
66
|
-
|
|
67
|
-
const appliedPredictions: Array<Patch> = []
|
|
82
|
+
const appliedPredictions: Array<Patch> = [];
|
|
68
83
|
|
|
69
84
|
/**
|
|
70
85
|
* Run the provided function, while treating all changes to Observables as predictions,
|
|
@@ -76,19 +91,19 @@ const appliedPredictions: Array<Patch> = []
|
|
|
76
91
|
* @returns A `Patch` object. Don't modify it. This is only meant to be passed to `applyCanon`.
|
|
77
92
|
*/
|
|
78
93
|
export function applyPrediction(predictFunc: () => void): Patch {
|
|
79
|
-
|
|
80
|
-
appliedPredictions.push(patch)
|
|
81
|
-
emitPatch(patch)
|
|
82
|
-
return patch
|
|
94
|
+
const patch = recordPatch(predictFunc);
|
|
95
|
+
appliedPredictions.push(patch);
|
|
96
|
+
emitPatch(patch);
|
|
97
|
+
return patch;
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
/**
|
|
86
101
|
* Temporarily revert all outstanding predictions, optionally run the provided function
|
|
87
102
|
* (which will generally make authoritative changes to the data based on a server response),
|
|
88
|
-
* and then attempt to reapply the predictions on top of the new canonical state, dropping
|
|
103
|
+
* and then attempt to reapply the predictions on top of the new canonical state, dropping
|
|
89
104
|
* any predictions that can no longer be applied cleanly (the data has been modified) or
|
|
90
105
|
* that were specified in `dropPredictions`.
|
|
91
|
-
*
|
|
106
|
+
*
|
|
92
107
|
* All of this is done such that redraws are only triggered if the overall effect is an
|
|
93
108
|
* actual change to an `Observable`.
|
|
94
109
|
* @param canonFunc The function to run without any predictions applied. This will typically
|
|
@@ -97,26 +112,29 @@ export function applyPrediction(predictFunc: () => void): Patch {
|
|
|
97
112
|
* to undo. Typically, when a server response for a certain request is being handled,
|
|
98
113
|
* you'd want to drop the prediction that was done for that request.
|
|
99
114
|
*/
|
|
100
|
-
export function applyCanon(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
export function applyCanon(
|
|
116
|
+
canonFunc?: () => void,
|
|
117
|
+
dropPredictions: Array<Patch> = [],
|
|
118
|
+
) {
|
|
119
|
+
const resultPatch = new Map();
|
|
120
|
+
for (const prediction of appliedPredictions)
|
|
121
|
+
mergePatch(resultPatch, prediction, true);
|
|
122
|
+
silentlyApplyPatch(resultPatch, true);
|
|
105
123
|
|
|
106
|
-
for(
|
|
107
|
-
|
|
108
|
-
if (pos >= 0) appliedPredictions.splice(pos, 1)
|
|
124
|
+
for (const prediction of dropPredictions) {
|
|
125
|
+
const pos = appliedPredictions.indexOf(prediction);
|
|
126
|
+
if (pos >= 0) appliedPredictions.splice(pos, 1);
|
|
109
127
|
}
|
|
110
|
-
if (canonFunc) mergePatch(resultPatch, recordPatch(canonFunc))
|
|
128
|
+
if (canonFunc) mergePatch(resultPatch, recordPatch(canonFunc));
|
|
111
129
|
|
|
112
|
-
for(let idx=0; idx<appliedPredictions.length; idx++) {
|
|
130
|
+
for (let idx = 0; idx < appliedPredictions.length; idx++) {
|
|
113
131
|
if (silentlyApplyPatch(appliedPredictions[idx])) {
|
|
114
|
-
mergePatch(resultPatch, appliedPredictions[idx])
|
|
132
|
+
mergePatch(resultPatch, appliedPredictions[idx]);
|
|
115
133
|
} else {
|
|
116
|
-
appliedPredictions.splice(idx, 1)
|
|
117
|
-
idx
|
|
134
|
+
appliedPredictions.splice(idx, 1);
|
|
135
|
+
idx--;
|
|
118
136
|
}
|
|
119
137
|
}
|
|
120
138
|
|
|
121
|
-
emitPatch(resultPatch)
|
|
139
|
+
emitPatch(resultPatch);
|
|
122
140
|
}
|