@colyseus/schema 3.0.0-alpha.33 → 3.0.0-alpha.35
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/bin/schema-debug +94 -0
- package/build/cjs/index.js +465 -303
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +465 -303
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +465 -303
- package/lib/Metadata.d.ts +5 -5
- package/lib/Metadata.js +17 -17
- package/lib/Metadata.js.map +1 -1
- package/lib/Schema.js +24 -17
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.js +11 -11
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.js +12 -5
- package/lib/bench_encode.js.map +1 -1
- package/lib/decoder/Decoder.js +1 -1
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +23 -7
- package/lib/encoder/ChangeTree.js +183 -106
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +2 -1
- package/lib/encoder/EncodeOperation.js +2 -2
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +3 -5
- package/lib/encoder/Encoder.js +93 -61
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +12 -7
- package/lib/encoder/Root.js +41 -20
- package/lib/encoder/Root.js.map +1 -1
- package/lib/encoder/StateView.d.ts +5 -5
- package/lib/encoder/StateView.js +29 -23
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/encoding/encode.js +12 -9
- package/lib/encoding/encode.js.map +1 -1
- package/lib/types/TypeContext.js +2 -1
- package/lib/types/TypeContext.js.map +1 -1
- package/lib/types/custom/ArraySchema.js +27 -13
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/lib/types/custom/MapSchema.d.ts +3 -1
- package/lib/types/custom/MapSchema.js +7 -4
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/symbols.d.ts +8 -6
- package/lib/types/symbols.js +9 -7
- package/lib/types/symbols.js.map +1 -1
- package/lib/utils.js +6 -3
- package/lib/utils.js.map +1 -1
- package/package.json +3 -2
- package/src/Metadata.ts +22 -22
- package/src/Schema.ts +33 -25
- package/src/annotations.ts +12 -12
- package/src/bench_encode.ts +15 -6
- package/src/decoder/Decoder.ts +1 -1
- package/src/encoder/ChangeTree.ts +220 -115
- package/src/encoder/EncodeOperation.ts +5 -1
- package/src/encoder/Encoder.ts +110 -68
- package/src/encoder/Root.ts +41 -21
- package/src/encoder/StateView.ts +32 -28
- package/src/encoding/encode.ts +12 -9
- package/src/types/TypeContext.ts +2 -1
- package/src/types/custom/ArraySchema.ts +39 -17
- package/src/types/custom/MapSchema.ts +12 -5
- package/src/types/symbols.ts +10 -9
- package/src/utils.ts +7 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OPERATION } from "../encoding/spec";
|
|
2
2
|
import { Schema } from "../Schema";
|
|
3
|
-
import { $changes, $childType, $decoder, $onEncodeEnd, $encoder, $getByIndex, $
|
|
3
|
+
import { $changes, $childType, $decoder, $onEncodeEnd, $encoder, $getByIndex, $refTypeFieldIndexes, $viewFieldIndexes } from "../types/symbols";
|
|
4
4
|
|
|
5
5
|
import type { MapSchema } from "../types/custom/MapSchema";
|
|
6
6
|
import type { ArraySchema } from "../types/custom/ArraySchema";
|
|
@@ -27,6 +27,50 @@ export type Ref = Schema
|
|
|
27
27
|
| CollectionSchema
|
|
28
28
|
| SetSchema;
|
|
29
29
|
|
|
30
|
+
export type ChangeSetName = "changes"
|
|
31
|
+
| "allChanges"
|
|
32
|
+
| "filteredChanges"
|
|
33
|
+
| "allFilteredChanges";
|
|
34
|
+
|
|
35
|
+
export interface IndexedOperations {
|
|
36
|
+
[index: number]: OPERATION;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ChangeSet {
|
|
40
|
+
// field index -> operation index
|
|
41
|
+
indexes: { [index: number]: number };
|
|
42
|
+
operations: OPERATION[]
|
|
43
|
+
queueRootIndex?: number; // index of ChangeTree structure in `root.changes` or `root.filteredChanges`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function setOperationAtIndex(changeSet: ChangeSet, index: number) {
|
|
47
|
+
const operationsIndex = changeSet.indexes[index];
|
|
48
|
+
if (operationsIndex === undefined) {
|
|
49
|
+
changeSet.indexes[index] = changeSet.operations.push(index) - 1;
|
|
50
|
+
} else {
|
|
51
|
+
changeSet.operations[operationsIndex] = index;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function deleteOperationAtIndex(changeSet: ChangeSet, index: number) {
|
|
56
|
+
const operationsIndex = changeSet.indexes[index];
|
|
57
|
+
if (operationsIndex !== undefined) {
|
|
58
|
+
changeSet.operations[operationsIndex] = undefined;
|
|
59
|
+
}
|
|
60
|
+
delete changeSet.indexes[index];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function enqueueChangeTree(
|
|
64
|
+
root: Root,
|
|
65
|
+
changeTree: ChangeTree,
|
|
66
|
+
changeSet: 'changes' | 'filteredChanges' | 'allFilteredChanges',
|
|
67
|
+
queueRootIndex = changeTree[changeSet].queueRootIndex
|
|
68
|
+
) {
|
|
69
|
+
if (root && root[changeSet][queueRootIndex] !== changeTree) {
|
|
70
|
+
changeTree[changeSet].queueRootIndex = root[changeSet].push(changeTree) - 1;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
30
74
|
export class ChangeTree<T extends Ref=any> {
|
|
31
75
|
ref: T;
|
|
32
76
|
refId: number;
|
|
@@ -38,17 +82,26 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
38
82
|
isFiltered: boolean = false;
|
|
39
83
|
isPartiallyFiltered: boolean = false;
|
|
40
84
|
|
|
41
|
-
|
|
85
|
+
indexedOperations: IndexedOperations = {};
|
|
42
86
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
87
|
+
//
|
|
88
|
+
// TODO:
|
|
89
|
+
// try storing the index + operation per item.
|
|
90
|
+
// example: 1024 & 1025 => ADD, 1026 => DELETE
|
|
91
|
+
//
|
|
92
|
+
// => https://chatgpt.com/share/67107d0c-bc20-8004-8583-83b17dd7c196
|
|
93
|
+
//
|
|
94
|
+
changes: ChangeSet = { indexes: {}, operations: [] };
|
|
95
|
+
allChanges: ChangeSet = { indexes: {}, operations: [] };
|
|
96
|
+
filteredChanges: ChangeSet;
|
|
97
|
+
allFilteredChanges: ChangeSet;
|
|
48
98
|
|
|
49
99
|
indexes: {[index: string]: any}; // TODO: remove this, only used by MapSchema/SetSchema/CollectionSchema (`encodeKeyValueOperation`)
|
|
50
100
|
|
|
51
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Is this a new instance? Used on ArraySchema to determine OPERATION.MOVE_AND_ADD operation.
|
|
103
|
+
*/
|
|
104
|
+
isNew = true;
|
|
52
105
|
|
|
53
106
|
constructor(ref: T) {
|
|
54
107
|
this.ref = ref;
|
|
@@ -56,15 +109,15 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
56
109
|
//
|
|
57
110
|
// Does this structure have "filters" declared?
|
|
58
111
|
//
|
|
59
|
-
if (ref.constructor[Symbol.metadata]?.[
|
|
60
|
-
this.allFilteredChanges =
|
|
61
|
-
this.filteredChanges =
|
|
112
|
+
if (ref.constructor[Symbol.metadata]?.[$viewFieldIndexes]) {
|
|
113
|
+
this.allFilteredChanges = { indexes: {}, operations: [] };
|
|
114
|
+
this.filteredChanges = { indexes: {}, operations: [] };
|
|
62
115
|
}
|
|
63
116
|
}
|
|
64
117
|
|
|
65
118
|
setRoot(root: Root) {
|
|
66
119
|
this.root = root;
|
|
67
|
-
this.root.add(this);
|
|
120
|
+
const isNewChangeTree = this.root.add(this);
|
|
68
121
|
|
|
69
122
|
const metadata: Metadata = this.ref.constructor[Symbol.metadata];
|
|
70
123
|
|
|
@@ -78,25 +131,26 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
78
131
|
this.checkIsFiltered(metadata, this.parent, this.parentIndex);
|
|
79
132
|
|
|
80
133
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
81
|
-
|
|
82
|
-
|
|
134
|
+
enqueueChangeTree(root, this, 'filteredChanges');
|
|
135
|
+
if (isNewChangeTree) {
|
|
136
|
+
this.root.allFilteredChanges.push(this);
|
|
137
|
+
}
|
|
83
138
|
}
|
|
84
139
|
}
|
|
85
140
|
|
|
86
141
|
if (!this.isFiltered) {
|
|
87
|
-
|
|
88
|
-
|
|
142
|
+
enqueueChangeTree(root, this, 'changes');
|
|
143
|
+
if (isNewChangeTree) {
|
|
144
|
+
this.root.allChanges.push(this);
|
|
145
|
+
}
|
|
89
146
|
}
|
|
90
147
|
|
|
91
|
-
|
|
92
|
-
|
|
148
|
+
// Recursively set root on child structures
|
|
93
149
|
if (metadata) {
|
|
94
|
-
metadata[
|
|
150
|
+
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
95
151
|
const field = metadata[index as any as number];
|
|
96
152
|
const value = this.ref[field.name];
|
|
97
|
-
|
|
98
|
-
value[$changes].setRoot(root);
|
|
99
|
-
}
|
|
153
|
+
value?.[$changes].setRoot(root);
|
|
100
154
|
});
|
|
101
155
|
|
|
102
156
|
} else if (this.ref[$childType] && typeof(this.ref[$childType]) !== "string") {
|
|
@@ -119,40 +173,42 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
119
173
|
// avoid setting parents with empty `root`
|
|
120
174
|
if (!root) { return; }
|
|
121
175
|
|
|
122
|
-
root.add(this);
|
|
123
|
-
|
|
124
176
|
const metadata: Metadata = this.ref.constructor[Symbol.metadata];
|
|
125
177
|
|
|
126
178
|
// skip if parent is already set
|
|
127
179
|
if (root !== this.root) {
|
|
128
180
|
this.root = root;
|
|
181
|
+
const isNewChangeTree = root.add(this);
|
|
129
182
|
|
|
130
183
|
if (root.types.hasFilters) {
|
|
131
184
|
this.checkIsFiltered(metadata, parent, parentIndex);
|
|
132
185
|
|
|
133
186
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
134
|
-
|
|
135
|
-
|
|
187
|
+
enqueueChangeTree(root, this, 'filteredChanges');
|
|
188
|
+
if (isNewChangeTree) {
|
|
189
|
+
this.root.allFilteredChanges.push(this);
|
|
190
|
+
}
|
|
136
191
|
}
|
|
137
192
|
}
|
|
138
193
|
|
|
139
194
|
if (!this.isFiltered) {
|
|
140
|
-
|
|
141
|
-
|
|
195
|
+
enqueueChangeTree(root, this, 'changes');
|
|
196
|
+
if (isNewChangeTree) {
|
|
197
|
+
this.root.allChanges.push(this);
|
|
198
|
+
}
|
|
142
199
|
}
|
|
143
200
|
|
|
144
|
-
|
|
201
|
+
} else {
|
|
202
|
+
root.add(this);
|
|
145
203
|
}
|
|
146
204
|
|
|
147
205
|
// assign same parent on child structures
|
|
148
206
|
if (metadata) {
|
|
149
|
-
metadata[
|
|
207
|
+
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
150
208
|
const field = metadata[index as any as number];
|
|
151
209
|
const value = this.ref[field.name];
|
|
152
210
|
value?.[$changes].setParent(this.ref, root, index);
|
|
153
211
|
|
|
154
|
-
// console.log(this.ref.constructor.name, field.name, value);
|
|
155
|
-
|
|
156
212
|
// try { throw new Error(); } catch (e) {
|
|
157
213
|
// console.log(e.stack);
|
|
158
214
|
// }
|
|
@@ -174,7 +230,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
174
230
|
//
|
|
175
231
|
const metadata: Metadata = this.ref.constructor[Symbol.metadata];
|
|
176
232
|
if (metadata) {
|
|
177
|
-
metadata[
|
|
233
|
+
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
178
234
|
const field = metadata[index as any as number];
|
|
179
235
|
const value = this.ref[field.name];
|
|
180
236
|
if (value) {
|
|
@@ -191,8 +247,11 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
191
247
|
}
|
|
192
248
|
|
|
193
249
|
operation(op: OPERATION) {
|
|
194
|
-
|
|
195
|
-
this.
|
|
250
|
+
// operations without index use negative values to represent them
|
|
251
|
+
// this is checked during .encode() time.
|
|
252
|
+
this.changes.operations.push(-op);
|
|
253
|
+
|
|
254
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
196
255
|
}
|
|
197
256
|
|
|
198
257
|
change(index: number, operation: OPERATION = OPERATION.ADD) {
|
|
@@ -203,7 +262,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
203
262
|
? this.filteredChanges
|
|
204
263
|
: this.changes;
|
|
205
264
|
|
|
206
|
-
const previousOperation =
|
|
265
|
+
const previousOperation = this.indexedOperations[index];
|
|
207
266
|
if (!previousOperation || previousOperation === OPERATION.DELETE) {
|
|
208
267
|
const op = (!previousOperation)
|
|
209
268
|
? operation
|
|
@@ -213,17 +272,22 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
213
272
|
//
|
|
214
273
|
// TODO: are DELETE operations being encoded as ADD here ??
|
|
215
274
|
//
|
|
216
|
-
|
|
275
|
+
this.indexedOperations[index] = op;
|
|
217
276
|
}
|
|
218
277
|
|
|
278
|
+
setOperationAtIndex(changeSet, index);
|
|
279
|
+
|
|
219
280
|
if (isFiltered) {
|
|
220
|
-
this.allFilteredChanges
|
|
221
|
-
|
|
222
|
-
this.root
|
|
281
|
+
setOperationAtIndex(this.allFilteredChanges, index);
|
|
282
|
+
|
|
283
|
+
if (this.root) {
|
|
284
|
+
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
285
|
+
enqueueChangeTree(this.root, this, 'allFilteredChanges');
|
|
286
|
+
}
|
|
223
287
|
|
|
224
288
|
} else {
|
|
225
|
-
this.allChanges
|
|
226
|
-
this.root
|
|
289
|
+
setOperationAtIndex(this.allChanges, index);
|
|
290
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
227
291
|
}
|
|
228
292
|
}
|
|
229
293
|
|
|
@@ -237,13 +301,16 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
237
301
|
? this.filteredChanges
|
|
238
302
|
: this.changes;
|
|
239
303
|
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
changeSet.set(index + shiftIndex, op);
|
|
304
|
+
const newIndexedOperations = {};
|
|
305
|
+
const newIndexes = {};
|
|
306
|
+
for (const index in this.indexedOperations) {
|
|
307
|
+
newIndexedOperations[Number(index) + shiftIndex] = this.indexedOperations[index];
|
|
308
|
+
newIndexes[Number(index) + shiftIndex] = changeSet[index];
|
|
246
309
|
}
|
|
310
|
+
this.indexedOperations = newIndexedOperations;
|
|
311
|
+
changeSet.indexes = newIndexes;
|
|
312
|
+
|
|
313
|
+
changeSet.operations = changeSet.operations.map((index) => index + shiftIndex);
|
|
247
314
|
}
|
|
248
315
|
|
|
249
316
|
shiftAllChangeIndexes(shiftIndex: number, startIndex: number = 0) {
|
|
@@ -261,25 +328,39 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
261
328
|
}
|
|
262
329
|
}
|
|
263
330
|
|
|
264
|
-
private _shiftAllChangeIndexes(shiftIndex: number, startIndex: number = 0,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
331
|
+
private _shiftAllChangeIndexes(shiftIndex: number, startIndex: number = 0, changeSet: ChangeSet) {
|
|
332
|
+
const newIndexes = {};
|
|
333
|
+
|
|
334
|
+
for (const key in changeSet.indexes) {
|
|
335
|
+
const index = changeSet.indexes[key];
|
|
336
|
+
if (index > startIndex) {
|
|
337
|
+
newIndexes[Number(key) + shiftIndex] = index;
|
|
338
|
+
} else {
|
|
339
|
+
newIndexes[key] = index;
|
|
269
340
|
}
|
|
270
|
-
}
|
|
341
|
+
}
|
|
342
|
+
changeSet.indexes = newIndexes;
|
|
343
|
+
|
|
344
|
+
for (let i = 0; i < changeSet.operations.length; i++) {
|
|
345
|
+
const index = changeSet.operations[i];
|
|
346
|
+
if (index > startIndex) {
|
|
347
|
+
changeSet.operations[i] = index + shiftIndex;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
271
350
|
}
|
|
272
351
|
|
|
273
|
-
indexedOperation(index: number, operation: OPERATION, allChangesIndex = index) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
this.
|
|
352
|
+
indexedOperation(index: number, operation: OPERATION, allChangesIndex: number = index) {
|
|
353
|
+
this.indexedOperations[index] = operation;
|
|
354
|
+
|
|
355
|
+
if (this.filteredChanges) {
|
|
356
|
+
setOperationAtIndex(this.allFilteredChanges, allChangesIndex);
|
|
357
|
+
setOperationAtIndex(this.filteredChanges, index);
|
|
358
|
+
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
278
359
|
|
|
279
360
|
} else {
|
|
280
|
-
this.allChanges
|
|
281
|
-
this.changes
|
|
282
|
-
this.root
|
|
361
|
+
setOperationAtIndex(this.allChanges, allChangesIndex);
|
|
362
|
+
setOperationAtIndex(this.changes, index);
|
|
363
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
283
364
|
}
|
|
284
365
|
}
|
|
285
366
|
|
|
@@ -300,8 +381,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
300
381
|
}
|
|
301
382
|
|
|
302
383
|
getChange(index: number) {
|
|
303
|
-
|
|
304
|
-
return this.changes.get(index) ?? this.filteredChanges?.get(index);
|
|
384
|
+
return this.indexedOperations[index];
|
|
305
385
|
}
|
|
306
386
|
|
|
307
387
|
//
|
|
@@ -328,9 +408,10 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
328
408
|
? this.filteredChanges
|
|
329
409
|
: this.changes;
|
|
330
410
|
|
|
331
|
-
|
|
411
|
+
this.indexedOperations[index] = operation ?? OPERATION.DELETE;
|
|
412
|
+
setOperationAtIndex(changeSet, index);
|
|
332
413
|
|
|
333
|
-
|
|
414
|
+
const previousValue = this.getValue(index);
|
|
334
415
|
|
|
335
416
|
// remove `root` reference
|
|
336
417
|
if (previousValue && previousValue[$changes]) {
|
|
@@ -348,26 +429,30 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
348
429
|
}
|
|
349
430
|
|
|
350
431
|
//
|
|
351
|
-
// FIXME: this is looking a
|
|
432
|
+
// FIXME: this is looking a ugly and repeated
|
|
352
433
|
//
|
|
353
434
|
if (this.filteredChanges) {
|
|
354
|
-
this.
|
|
355
|
-
this.
|
|
435
|
+
deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
|
|
436
|
+
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
356
437
|
|
|
357
438
|
} else {
|
|
358
|
-
this.
|
|
359
|
-
this.
|
|
439
|
+
deleteOperationAtIndex(this.allChanges, allChangesIndex);
|
|
440
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
360
441
|
}
|
|
361
442
|
}
|
|
362
443
|
|
|
363
444
|
endEncode() {
|
|
364
|
-
this.
|
|
445
|
+
this.indexedOperations = {};
|
|
446
|
+
|
|
447
|
+
// // clear changes
|
|
448
|
+
// this.changes.indexes = {};
|
|
449
|
+
// this.changes.operations.length = 0;
|
|
365
450
|
|
|
366
451
|
// ArraySchema and MapSchema have a custom "encode end" method
|
|
367
452
|
this.ref[$onEncodeEnd]?.();
|
|
368
453
|
|
|
369
454
|
// Not a new instance anymore
|
|
370
|
-
|
|
455
|
+
this.isNew = false;
|
|
371
456
|
}
|
|
372
457
|
|
|
373
458
|
discard(discardAll: boolean = false) {
|
|
@@ -378,15 +463,26 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
378
463
|
//
|
|
379
464
|
this.ref[$onEncodeEnd]?.();
|
|
380
465
|
|
|
381
|
-
this.
|
|
382
|
-
this.filteredChanges?.clear();
|
|
466
|
+
this.indexedOperations = {};
|
|
383
467
|
|
|
384
|
-
|
|
385
|
-
this.
|
|
468
|
+
this.changes.indexes = {};
|
|
469
|
+
this.changes.operations.length = 0;
|
|
470
|
+
this.changes.queueRootIndex = undefined;
|
|
471
|
+
|
|
472
|
+
if (this.filteredChanges !== undefined) {
|
|
473
|
+
this.filteredChanges.indexes = {};
|
|
474
|
+
this.filteredChanges.operations.length = 0;
|
|
475
|
+
this.filteredChanges.queueRootIndex = undefined;
|
|
476
|
+
}
|
|
386
477
|
|
|
387
478
|
if (discardAll) {
|
|
388
|
-
this.allChanges.
|
|
389
|
-
this.
|
|
479
|
+
this.allChanges.indexes = {};
|
|
480
|
+
this.allChanges.operations.length = 0;
|
|
481
|
+
|
|
482
|
+
if (this.allFilteredChanges !== undefined) {
|
|
483
|
+
this.allFilteredChanges.indexes = {};
|
|
484
|
+
this.allFilteredChanges.operations.length = 0;
|
|
485
|
+
}
|
|
390
486
|
|
|
391
487
|
// remove children references
|
|
392
488
|
this.forEachChild((changeTree, _) =>
|
|
@@ -398,13 +494,14 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
398
494
|
* Recursively discard all changes from this, and child structures.
|
|
399
495
|
*/
|
|
400
496
|
discardAll() {
|
|
401
|
-
this.
|
|
402
|
-
|
|
497
|
+
const keys = Object.keys(this.indexedOperations);
|
|
498
|
+
for (let i = 0, len = keys.length; i < len; i++) {
|
|
499
|
+
const value = this.getValue(Number(keys[i]));
|
|
403
500
|
|
|
404
501
|
if (value && value[$changes]) {
|
|
405
502
|
value[$changes].discardAll();
|
|
406
503
|
}
|
|
407
|
-
}
|
|
504
|
+
}
|
|
408
505
|
|
|
409
506
|
this.discard();
|
|
410
507
|
}
|
|
@@ -419,49 +516,57 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
419
516
|
}
|
|
420
517
|
|
|
421
518
|
get changed() {
|
|
422
|
-
return this.
|
|
519
|
+
return (Object.entries(this.indexedOperations).length > 0);
|
|
423
520
|
}
|
|
424
521
|
|
|
425
522
|
protected checkIsFiltered(metadata: Metadata, parent: Ref, parentIndex: number) {
|
|
426
523
|
// Detect if current structure has "filters" declared
|
|
427
|
-
this.isPartiallyFiltered = metadata?.[
|
|
524
|
+
this.isPartiallyFiltered = metadata?.[$viewFieldIndexes] !== undefined;
|
|
428
525
|
|
|
429
526
|
if (this.isPartiallyFiltered) {
|
|
430
|
-
this.filteredChanges = this.filteredChanges ||
|
|
431
|
-
this.allFilteredChanges = this.allFilteredChanges ||
|
|
527
|
+
this.filteredChanges = this.filteredChanges || { indexes: {}, operations: [] };
|
|
528
|
+
this.allFilteredChanges = this.allFilteredChanges || { indexes: {}, operations: [] };
|
|
432
529
|
}
|
|
433
530
|
|
|
434
|
-
if
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
531
|
+
// skip if parent is not set
|
|
532
|
+
if (!parent) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (!Metadata.isValidInstance(parent)) {
|
|
537
|
+
const parentChangeTree = parent[$changes];
|
|
538
|
+
parent = parentChangeTree.parent;
|
|
539
|
+
parentIndex = parentChangeTree.parentIndex;
|
|
540
|
+
}
|
|
440
541
|
|
|
441
|
-
|
|
442
|
-
|
|
542
|
+
const parentMetadata = parent.constructor?.[Symbol.metadata];
|
|
543
|
+
this.isFiltered = parentMetadata?.[$viewFieldIndexes]?.includes(parentIndex);
|
|
443
544
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
545
|
+
//
|
|
546
|
+
// TODO: refactor this!
|
|
547
|
+
//
|
|
548
|
+
// swapping `changes` and `filteredChanges` is required here
|
|
549
|
+
// because "isFiltered" may not be imedialely available on `change()`
|
|
550
|
+
//
|
|
551
|
+
if (this.isFiltered) {
|
|
552
|
+
this.filteredChanges = { indexes: {}, operations: [] };
|
|
553
|
+
this.allFilteredChanges = { indexes: {}, operations: [] };
|
|
554
|
+
|
|
555
|
+
if (this.changes.operations.length > 0) {
|
|
556
|
+
// swap changes reference
|
|
557
|
+
const changes = this.changes;
|
|
558
|
+
this.changes = this.filteredChanges;
|
|
559
|
+
this.filteredChanges = changes;
|
|
560
|
+
|
|
561
|
+
// swap "all changes" reference
|
|
562
|
+
const allFilteredChanges = this.allFilteredChanges;
|
|
563
|
+
this.allFilteredChanges = this.allChanges;
|
|
564
|
+
this.allChanges = allFilteredChanges;
|
|
565
|
+
|
|
566
|
+
// console.log("SWAP =>", {
|
|
567
|
+
// "this.allFilteredChanges": this.allFilteredChanges,
|
|
568
|
+
// "this.allChanges": this.allChanges
|
|
569
|
+
// })
|
|
465
570
|
}
|
|
466
571
|
}
|
|
467
572
|
}
|
|
@@ -23,6 +23,7 @@ export type EncodeOperation<T extends Ref = any> = (
|
|
|
23
23
|
it: Iterator,
|
|
24
24
|
isEncodeAll: boolean,
|
|
25
25
|
hasView: boolean,
|
|
26
|
+
metadata?: Metadata,
|
|
26
27
|
) => void;
|
|
27
28
|
|
|
28
29
|
export function encodeValue(
|
|
@@ -68,6 +69,9 @@ export const encodeSchemaOperation: EncodeOperation = function (
|
|
|
68
69
|
index: number,
|
|
69
70
|
operation: OPERATION,
|
|
70
71
|
it: Iterator,
|
|
72
|
+
_: any,
|
|
73
|
+
__: any,
|
|
74
|
+
metadata: Metadata,
|
|
71
75
|
) {
|
|
72
76
|
// "compress" field index + operation
|
|
73
77
|
bytes[it.offset++] = (index | operation) & 255;
|
|
@@ -78,7 +82,7 @@ export const encodeSchemaOperation: EncodeOperation = function (
|
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
const ref = changeTree.ref;
|
|
81
|
-
const metadata: Metadata = ref.constructor[Symbol.metadata];
|
|
85
|
+
// const metadata: Metadata = ref.constructor[Symbol.metadata];
|
|
82
86
|
const field = metadata[index];
|
|
83
87
|
|
|
84
88
|
// TODO: inline this function call small performance gain
|