@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.
Files changed (63) hide show
  1. package/bin/schema-debug +94 -0
  2. package/build/cjs/index.js +465 -303
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +465 -303
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +465 -303
  7. package/lib/Metadata.d.ts +5 -5
  8. package/lib/Metadata.js +17 -17
  9. package/lib/Metadata.js.map +1 -1
  10. package/lib/Schema.js +24 -17
  11. package/lib/Schema.js.map +1 -1
  12. package/lib/annotations.js +11 -11
  13. package/lib/annotations.js.map +1 -1
  14. package/lib/bench_encode.js +12 -5
  15. package/lib/bench_encode.js.map +1 -1
  16. package/lib/decoder/Decoder.js +1 -1
  17. package/lib/decoder/Decoder.js.map +1 -1
  18. package/lib/encoder/ChangeTree.d.ts +23 -7
  19. package/lib/encoder/ChangeTree.js +183 -106
  20. package/lib/encoder/ChangeTree.js.map +1 -1
  21. package/lib/encoder/EncodeOperation.d.ts +2 -1
  22. package/lib/encoder/EncodeOperation.js +2 -2
  23. package/lib/encoder/EncodeOperation.js.map +1 -1
  24. package/lib/encoder/Encoder.d.ts +3 -5
  25. package/lib/encoder/Encoder.js +93 -61
  26. package/lib/encoder/Encoder.js.map +1 -1
  27. package/lib/encoder/Root.d.ts +12 -7
  28. package/lib/encoder/Root.js +41 -20
  29. package/lib/encoder/Root.js.map +1 -1
  30. package/lib/encoder/StateView.d.ts +5 -5
  31. package/lib/encoder/StateView.js +29 -23
  32. package/lib/encoder/StateView.js.map +1 -1
  33. package/lib/encoding/encode.js +12 -9
  34. package/lib/encoding/encode.js.map +1 -1
  35. package/lib/types/TypeContext.js +2 -1
  36. package/lib/types/TypeContext.js.map +1 -1
  37. package/lib/types/custom/ArraySchema.js +27 -13
  38. package/lib/types/custom/ArraySchema.js.map +1 -1
  39. package/lib/types/custom/MapSchema.d.ts +3 -1
  40. package/lib/types/custom/MapSchema.js +7 -4
  41. package/lib/types/custom/MapSchema.js.map +1 -1
  42. package/lib/types/symbols.d.ts +8 -6
  43. package/lib/types/symbols.js +9 -7
  44. package/lib/types/symbols.js.map +1 -1
  45. package/lib/utils.js +6 -3
  46. package/lib/utils.js.map +1 -1
  47. package/package.json +3 -2
  48. package/src/Metadata.ts +22 -22
  49. package/src/Schema.ts +33 -25
  50. package/src/annotations.ts +12 -12
  51. package/src/bench_encode.ts +15 -6
  52. package/src/decoder/Decoder.ts +1 -1
  53. package/src/encoder/ChangeTree.ts +220 -115
  54. package/src/encoder/EncodeOperation.ts +5 -1
  55. package/src/encoder/Encoder.ts +110 -68
  56. package/src/encoder/Root.ts +41 -21
  57. package/src/encoder/StateView.ts +32 -28
  58. package/src/encoding/encode.ts +12 -9
  59. package/src/types/TypeContext.ts +2 -1
  60. package/src/types/custom/ArraySchema.ts +39 -17
  61. package/src/types/custom/MapSchema.ts +12 -5
  62. package/src/types/symbols.ts +10 -9
  63. 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, $isNew } from "../types/symbols";
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
- currentOperationIndex: number = 0;
85
+ indexedOperations: IndexedOperations = {};
42
86
 
43
- changes = new Map<number, OPERATION>();
44
- allChanges = new Map<number, OPERATION>();
45
-
46
- allFilteredChanges: Map<number, OPERATION>;
47
- filteredChanges: Map<number, OPERATION>;
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
- [$isNew] = true;
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]?.[-2]) {
60
- this.allFilteredChanges = new Map<number, OPERATION>();
61
- this.filteredChanges = new Map<number, OPERATION>();
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
- this.root.allFilteredChanges.set(this, this.allFilteredChanges);
82
- this.root.filteredChanges.set(this, this.filteredChanges);
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
- this.root.changes.set(this, this.changes);
88
- this.root.allChanges.set(this, this.allChanges);
142
+ enqueueChangeTree(root, this, 'changes');
143
+ if (isNewChangeTree) {
144
+ this.root.allChanges.push(this);
145
+ }
89
146
  }
90
147
 
91
- this.ensureRefId();
92
-
148
+ // Recursively set root on child structures
93
149
  if (metadata) {
94
- metadata[-4]?.forEach((index) => {
150
+ metadata[$refTypeFieldIndexes]?.forEach((index) => {
95
151
  const field = metadata[index as any as number];
96
152
  const value = this.ref[field.name];
97
- if (value) {
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
- this.root.filteredChanges.set(this, this.filteredChanges);
135
- this.root.allFilteredChanges.set(this, this.filteredChanges);
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
- this.root.changes.set(this, this.changes);
141
- this.root.allChanges.set(this, this.allChanges);
195
+ enqueueChangeTree(root, this, 'changes');
196
+ if (isNewChangeTree) {
197
+ this.root.allChanges.push(this);
198
+ }
142
199
  }
143
200
 
144
- this.ensureRefId();
201
+ } else {
202
+ root.add(this);
145
203
  }
146
204
 
147
205
  // assign same parent on child structures
148
206
  if (metadata) {
149
- metadata[-4]?.forEach((index) => {
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[-4]?.forEach((index) => {
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
- this.changes.set(--this.currentOperationIndex, op);
195
- this.root?.changes.set(this, this.changes);
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 = changeSet.get(index);
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
- changeSet.set(index, op);
275
+ this.indexedOperations[index] = op;
217
276
  }
218
277
 
278
+ setOperationAtIndex(changeSet, index);
279
+
219
280
  if (isFiltered) {
220
- this.allFilteredChanges.set(index, OPERATION.ADD);
221
- this.root?.filteredChanges.set(this, this.filteredChanges);
222
- this.root?.allFilteredChanges.set(this, this.allFilteredChanges);
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.set(index, OPERATION.ADD);
226
- this.root?.changes.set(this, this.changes);
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 changeSetEntries = Array.from(changeSet.entries());
241
- changeSet.clear();
242
-
243
- // Re-insert each entry with the shifted index
244
- for (const [index, op] of changeSetEntries) {
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, allChangeSet: Map<number, OPERATION>) {
265
- Array.from(allChangeSet.entries()).forEach(([index, op]) => {
266
- if (index >= startIndex) {
267
- allChangeSet.delete(index);
268
- allChangeSet.set(index + shiftIndex, op);
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
- if (this.filteredChanges !== undefined) {
275
- this.allFilteredChanges.set(allChangesIndex, OPERATION.ADD);
276
- this.filteredChanges.set(index, operation);
277
- this.root?.filteredChanges.set(this, this.filteredChanges);
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.set(allChangesIndex, OPERATION.ADD);
281
- this.changes.set(index, operation);
282
- this.root?.changes.set(this, this.changes);
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
- // TODO: optimize this. avoid checking against multiple instances
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
- const previousValue = this.getValue(index);
411
+ this.indexedOperations[index] = operation ?? OPERATION.DELETE;
412
+ setOperationAtIndex(changeSet, index);
332
413
 
333
- changeSet.set(index, operation ?? OPERATION.DELETE);
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 bit ugly (and repeated from `.change()`)
432
+ // FIXME: this is looking a ugly and repeated
352
433
  //
353
434
  if (this.filteredChanges) {
354
- this.root?.filteredChanges.set(this, this.filteredChanges);
355
- this.allFilteredChanges.delete(allChangesIndex);
435
+ deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
436
+ enqueueChangeTree(this.root, this, 'filteredChanges');
356
437
 
357
438
  } else {
358
- this.root?.changes.set(this, this.changes);
359
- this.allChanges.delete(allChangesIndex);
439
+ deleteOperationAtIndex(this.allChanges, allChangesIndex);
440
+ enqueueChangeTree(this.root, this, 'changes');
360
441
  }
361
442
  }
362
443
 
363
444
  endEncode() {
364
- this.changes.clear();
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
- delete this[$isNew];
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.changes.clear();
382
- this.filteredChanges?.clear();
466
+ this.indexedOperations = {};
383
467
 
384
- // reset operation index
385
- this.currentOperationIndex = 0;
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.clear();
389
- this.allFilteredChanges?.clear();
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.changes.forEach((_, fieldIndex) => {
402
- const value = this.getValue(fieldIndex);
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.changes.size > 0;
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?.[-2] !== undefined;
524
+ this.isPartiallyFiltered = metadata?.[$viewFieldIndexes] !== undefined;
428
525
 
429
526
  if (this.isPartiallyFiltered) {
430
- this.filteredChanges = this.filteredChanges || new Map<number, OPERATION>();
431
- this.allFilteredChanges = this.allFilteredChanges || new Map<number, OPERATION>();
527
+ this.filteredChanges = this.filteredChanges || { indexes: {}, operations: [] };
528
+ this.allFilteredChanges = this.allFilteredChanges || { indexes: {}, operations: [] };
432
529
  }
433
530
 
434
- if (parent) {
435
- if (!Metadata.isValidInstance(parent)) {
436
- const parentChangeTree = parent[$changes];
437
- parent = parentChangeTree.parent;
438
- parentIndex = parentChangeTree.parentIndex;
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
- const parentMetadata = parent?.constructor?.[Symbol.metadata];
442
- this.isFiltered = (parent && parentMetadata?.[-2]?.includes(parentIndex));
542
+ const parentMetadata = parent.constructor?.[Symbol.metadata];
543
+ this.isFiltered = parentMetadata?.[$viewFieldIndexes]?.includes(parentIndex);
443
544
 
444
- //
445
- // TODO: refactor this!
446
- //
447
- // swapping `changes` and `filteredChanges` is required here
448
- // because "isFiltered" may not be imedialely available on `change()`
449
- //
450
- if (this.isFiltered) {
451
- this.filteredChanges = new Map<number, OPERATION>();
452
- this.allFilteredChanges = new Map<number, OPERATION>();
453
-
454
- if (this.changes.size > 0) {
455
- // swap changes reference
456
- const changes = this.changes;
457
- this.changes = this.filteredChanges;
458
- this.filteredChanges = changes;
459
-
460
- // swap "all changes" reference
461
- const allFilteredChanges = this.allFilteredChanges;
462
- this.allFilteredChanges = this.allChanges;
463
- this.allChanges = allFilteredChanges;
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